Coverage for openhcs/pyqt_gui/windows/help_window.py: 0.0%

97 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2025-08-14 05:57 +0000

1""" 

2Help Window for PyQt6 

3 

4Help display dialog with OpenHCS documentation and usage information. 

5Uses hybrid approach: extracted business logic + clean PyQt6 UI. 

6""" 

7 

8import logging 

9from typing import Optional 

10 

11from PyQt6.QtWidgets import ( 

12 QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, 

13 QScrollArea, QTextEdit, QFrame, QWidget 

14) 

15from PyQt6.QtCore import Qt 

16from PyQt6.QtGui import QFont 

17 

18from openhcs.pyqt_gui.shared.style_generator import StyleSheetGenerator 

19from openhcs.pyqt_gui.shared.color_scheme import PyQt6ColorScheme 

20 

21logger = logging.getLogger(__name__) 

22 

23 

24class HelpWindow(QDialog): 

25 """ 

26 PyQt6 Help Window. 

27  

28 Help display dialog with OpenHCS documentation and usage information. 

29 Preserves all business logic from Textual version with clean PyQt6 UI. 

30 """ 

31 

32 # Help content (extracted from Textual version) 

33 HELP_TEXT = """OpenHCS - Open High-Content Screening 

34 

35🔬 Visual Programming for Cell Biology Research 

36 

37Key Features: 

38• GPU-accelerated image processing 

39• Visual pipeline building 

40• Multi-backend storage support 

41• Real-time parameter editing 

42 

43Workflow: 

441. Add Plate → Select microscopy data 

452. Edit Step → Visual function selection 

463. Compile → Create execution plan 

474. Run → Process images 

48 

49Navigation: 

50• Use the Plate Manager to add and manage your microscopy plates 

51• Build processing pipelines in the Pipeline Editor 

52• Configure functions in the Function Library 

53• Monitor system performance in real-time 

54 

55Keyboard Shortcuts: 

56• Ctrl+N: New pipeline 

57• Ctrl+O: Open pipeline 

58• Ctrl+S: Save pipeline 

59• F1: Show this help 

60• Esc: Close current dialog 

61 

62Tips: 

63• Right-click on widgets for context menus 

64• Use drag-and-drop to reorder pipeline steps 

65• Parameters are validated in real-time 

66• GPU acceleration is automatic when available 

67 

68For detailed documentation, see the Nature Methods publication 

69and the online documentation at https://openhcs.org 

70 

71Version: PyQt6 GUI 1.0.0 

72""" 

73 

74 def __init__(self, content: Optional[str] = None, 

75 color_scheme: Optional[PyQt6ColorScheme] = None, parent=None): 

76 """ 

77 Initialize the help window. 

78 

79 Args: 

80 content: Custom help content (uses default if None) 

81 color_scheme: Color scheme for styling (optional, uses default if None) 

82 parent: Parent widget 

83 """ 

84 super().__init__(parent) 

85 

86 # Initialize color scheme and style generator 

87 self.color_scheme = color_scheme or PyQt6ColorScheme() 

88 self.style_generator = StyleSheetGenerator(self.color_scheme) 

89 

90 # Business logic state 

91 self.content = content or self.HELP_TEXT 

92 

93 # Setup UI 

94 self.setup_ui() 

95 self.setup_connections() 

96 

97 logger.debug("Help window initialized") 

98 

99 def setup_ui(self): 

100 """Setup the user interface.""" 

101 self.setWindowTitle("OpenHCS Help") 

102 self.setModal(True) 

103 self.setMinimumSize(600, 500) 

104 self.resize(800, 700) 

105 

106 layout = QVBoxLayout(self) 

107 layout.setSpacing(10) 

108 

109 # Header with OpenHCS logo/title 

110 header_frame = self.create_header() 

111 layout.addWidget(header_frame) 

112 

113 # Scrollable content area 

114 content_area = self.create_content_area() 

115 layout.addWidget(content_area) 

116 

117 # Button panel 

118 button_panel = self.create_button_panel() 

119 layout.addWidget(button_panel) 

120 

121 # Apply centralized styling 

122 self.setStyleSheet(self.style_generator.generate_dialog_style() + f""" 

123 QTextEdit {{ 

124 background-color: {self.color_scheme.to_hex(self.color_scheme.panel_bg)}; 

125 color: {self.color_scheme.to_hex(self.color_scheme.text_primary)}; 

126 border: 1px solid {self.color_scheme.to_hex(self.color_scheme.border_color)}; 

127 border-radius: 5px; 

128 padding: 10px; 

129 font-family: 'Courier New', monospace; 

130 font-size: 11px; 

131 line-height: 1.4; 

132 }} 

133 """) 

134 

135 def create_header(self) -> QWidget: 

136 """ 

137 Create the header section with title and version. 

138  

139 Returns: 

140 Widget containing header information 

141 """ 

142 frame = QFrame() 

143 frame.setFrameStyle(QFrame.Shape.Box) 

144 frame.setStyleSheet(f""" 

145 QFrame {{ 

146 background-color: {self.color_scheme.to_hex(self.color_scheme.panel_bg)}; 

147 border: 1px solid {self.color_scheme.to_hex(self.color_scheme.border_color)}; 

148 border-radius: 5px; 

149 padding: 15px; 

150 }} 

151 """) 

152 

153 layout = QVBoxLayout(frame) 

154 

155 # ASCII art title 

156 title_label = QLabel(self.get_ascii_title()) 

157 title_label.setFont(QFont("Courier New", 10, QFont.Weight.Bold)) 

158 title_label.setStyleSheet(f"color: {self.color_scheme.to_hex(self.color_scheme.text_accent)};") 

159 title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) 

160 layout.addWidget(title_label) 

161 

162 # Subtitle 

163 subtitle_label = QLabel("Visual Programming for Cell Biology Research") 

164 subtitle_label.setFont(QFont("Arial", 12)) 

165 subtitle_label.setStyleSheet(f"color: {self.color_scheme.to_hex(self.color_scheme.text_secondary)};") 

166 subtitle_label.setAlignment(Qt.AlignmentFlag.AlignCenter) 

167 layout.addWidget(subtitle_label) 

168 

169 return frame 

170 

171 def create_content_area(self) -> QWidget: 

172 """ 

173 Create the scrollable content area. 

174  

175 Returns: 

176 Widget containing help content 

177 """ 

178 # Text edit for help content 

179 self.content_text = QTextEdit() 

180 self.content_text.setPlainText(self.content) 

181 self.content_text.setReadOnly(True) 

182 

183 # Enable rich text formatting 

184 self.content_text.setHtml(self.format_help_content(self.content)) 

185 

186 return self.content_text 

187 

188 def create_button_panel(self) -> QWidget: 

189 """ 

190 Create the button panel. 

191  

192 Returns: 

193 Widget containing action buttons 

194 """ 

195 panel = QFrame() 

196 panel.setFrameStyle(QFrame.Shape.Box) 

197 panel.setStyleSheet(f""" 

198 QFrame {{ 

199 background-color: {self.color_scheme.to_hex(self.color_scheme.panel_bg)}; 

200 border: 1px solid {self.color_scheme.to_hex(self.color_scheme.border_color)}; 

201 border-radius: 3px; 

202 padding: 10px; 

203 }} 

204 """) 

205 

206 layout = QHBoxLayout(panel) 

207 layout.addStretch() 

208 

209 # Close button 

210 close_button = QPushButton("Close") 

211 close_button.setMinimumWidth(100) 

212 close_button.setMinimumHeight(35) 

213 close_button.clicked.connect(self.accept) 

214 close_button.setStyleSheet(""" 

215 QPushButton { 

216 background-color: #0078d4; 

217 color: white; 

218 border: 1px solid #106ebe; 

219 border-radius: 5px; 

220 padding: 8px; 

221 font-weight: bold; 

222 } 

223 QPushButton:hover { 

224 background-color: #106ebe; 

225 } 

226 QPushButton:pressed { 

227 background-color: #005a9e; 

228 } 

229 """) 

230 layout.addWidget(close_button) 

231 

232 return panel 

233 

234 def setup_connections(self): 

235 """Setup signal/slot connections.""" 

236 pass # No additional connections needed 

237 

238 def get_ascii_title(self) -> str: 

239 """ 

240 Get ASCII art title. 

241  

242 Returns: 

243 ASCII art title string 

244 """ 

245 return """ 

246 ██████╗ ██████╗ ███████╗███╗ ██╗██╗ ██╗ ██████╗███████╗ 

247██╔═══██╗██╔══██╗██╔════╝████╗ ██║██║ ██║██╔════╝██╔════╝ 

248██║ ██║██████╔╝█████╗ ██╔██╗ ██║███████║██║ ███████╗ 

249██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██╔══██║██║ ╚════██║ 

250╚██████╔╝██║ ███████╗██║ ╚████║██║ ██║╚██████╗███████║ 

251 ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝╚══════╝ 

252 """ 

253 

254 def format_help_content(self, content: str) -> str: 

255 """ 

256 Format help content with HTML for better display. 

257  

258 Args: 

259 content: Raw help content 

260  

261 Returns: 

262 HTML formatted content 

263 """ 

264 # Convert plain text to HTML with basic formatting 

265 html_content = content.replace('\n', '<br>') 

266 

267 # Format headers (lines starting with uppercase words followed by colon) 

268 import re 

269 accent_color = self.color_scheme.to_hex(self.color_scheme.text_accent) 

270 html_content = re.sub( 

271 r'^([A-Z][A-Za-z\s]+:)$', 

272 rf'<h3 style="color: {accent_color}; margin-top: 20px; margin-bottom: 10px;">\1</h3>', 

273 html_content, 

274 flags=re.MULTILINE 

275 ) 

276 

277 # Format bullet points 

278 secondary_color = self.color_scheme.to_hex(self.color_scheme.text_secondary) 

279 html_content = re.sub( 

280 r'^• (.+)$', 

281 rf'<li style="margin-left: 20px; color: {secondary_color};">\1</li>', 

282 html_content, 

283 flags=re.MULTILINE 

284 ) 

285 

286 # Format numbered lists 

287 html_content = re.sub( 

288 r'^(\d+\.) (.+)$', 

289 rf'<div style="margin-left: 20px; color: {secondary_color};"><strong style="color: {accent_color};">\1</strong> \2</div>', 

290 html_content, 

291 flags=re.MULTILINE 

292 ) 

293 

294 # Format keyboard shortcuts 

295 input_bg_color = self.color_scheme.to_hex(self.color_scheme.input_bg) 

296 text_accent_color = self.color_scheme.to_hex(self.color_scheme.text_accent) 

297 html_content = re.sub( 

298 r'(Ctrl\+[A-Z]|F\d+|Esc)', 

299 rf'<code style="background-color: {input_bg_color}; padding: 2px 4px; border-radius: 3px; color: {text_accent_color};">\1</code>', 

300 html_content 

301 ) 

302 

303 # Wrap in HTML structure 

304 primary_text_color = self.color_scheme.to_hex(self.color_scheme.text_primary) 

305 html_content = f""" 

306 <html> 

307 <body style="font-family: Arial, sans-serif; font-size: 12px; line-height: 1.6; color: {primary_text_color};"> 

308 {html_content} 

309 </body> 

310 </html> 

311 """ 

312 

313 return html_content 

314 

315 def set_content(self, content: str): 

316 """ 

317 Set new help content. 

318  

319 Args: 

320 content: New help content 

321 """ 

322 self.content = content 

323 if hasattr(self, 'content_text'): 

324 self.content_text.setHtml(self.format_help_content(content)) 

325 

326 def show_section(self, section_name: str): 

327 """ 

328 Show specific help section. 

329  

330 Args: 

331 section_name: Name of the section to show 

332 """ 

333 # This could be extended to show specific sections 

334 # For now, just show the full help 

335 self.show() 

336 

337 @staticmethod 

338 def show_help(parent=None, content: Optional[str] = None): 

339 """ 

340 Static method to show help window. 

341  

342 Args: 

343 parent: Parent widget 

344 content: Custom help content 

345  

346 Returns: 

347 Dialog result 

348 """ 

349 help_window = HelpWindow(content, parent) 

350 return help_window.exec() 

351 

352 

353# Convenience function for showing help 

354def show_help_dialog(parent=None, content: Optional[str] = None): 

355 """ 

356 Show help dialog. 

357  

358 Args: 

359 parent: Parent widget 

360 content: Custom help content 

361  

362 Returns: 

363 Dialog result 

364 """ 

365 return HelpWindow.show_help(parent, content)