Coverage for openhcs/pyqt_gui/shared/style_generator.py: 0.0%
51 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 02:09 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 02:09 +0000
1"""
2QStyleSheet Generator for OpenHCS PyQt6 GUI
4Generates dynamic QStyleSheet strings from PyQt6ColorScheme objects, enabling
5centralized styling with theme support. Replaces hardcoded color strings with
6semantic color scheme references.
7"""
9import logging
10from openhcs.pyqt_gui.shared.color_scheme import PyQt6ColorScheme
12logger = logging.getLogger(__name__)
15class StyleSheetGenerator:
16 """
17 Generates QStyleSheet strings from PyQt6ColorScheme objects.
19 Provides methods to generate complete stylesheets for different widget types,
20 replacing hardcoded colors with centralized color scheme references.
21 """
23 def __init__(self, color_scheme: PyQt6ColorScheme):
24 """
25 Initialize the style generator with a color scheme.
27 Args:
28 color_scheme: PyQt6ColorScheme instance to use for styling
29 """
30 self.color_scheme = color_scheme
32 def update_color_scheme(self, color_scheme: PyQt6ColorScheme):
33 """
34 Update the color scheme used for style generation.
36 Args:
37 color_scheme: New PyQt6ColorScheme instance
38 """
39 self.color_scheme = color_scheme
41 def generate_dialog_style(self) -> str:
42 """
43 Generate QStyleSheet for dialog windows.
45 Returns:
46 str: Complete QStyleSheet for dialog styling
47 """
48 cs = self.color_scheme
49 return f"""
50 QDialog {{
51 background-color: {cs.to_hex(cs.window_bg)};
52 color: {cs.to_hex(cs.text_primary)};
53 }}
54 QGroupBox {{
55 font-weight: bold;
56 border: none;
57 border-radius: 5px;
58 margin-top: 5px;
59 padding-top: 5px;
60 background-color: {cs.to_hex(cs.panel_bg)};
61 color: {cs.to_hex(cs.text_primary)};
62 }}
63 QGroupBox::title {{
64 subcontrol-origin: margin;
65 left: 10px;
66 padding: 0 5px 0 5px;
67 color: {cs.to_hex(cs.text_accent)};
68 }}
69 QLabel {{
70 color: {cs.to_hex(cs.text_secondary)};
71 }}
72 QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox {{
73 background-color: {cs.to_hex(cs.input_bg)};
74 color: {cs.to_hex(cs.input_text)};
75 border: 1px solid {cs.to_hex(cs.input_border)};
76 border-radius: 3px;
77 padding: 5px;
78 }}
79 QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QComboBox:focus {{
80 border: 1px solid {cs.to_hex(cs.input_focus_border)};
81 }}
82 QCheckBox {{
83 color: {cs.to_hex(cs.text_primary)};
84 }}
85 """
87 def generate_tree_widget_style(self) -> str:
88 """
89 Generate QStyleSheet for tree widgets and list widgets.
91 Returns:
92 str: Complete QStyleSheet for tree/list widget styling
93 """
94 cs = self.color_scheme
95 return f"""
96 QTreeWidget, QListWidget {{
97 background-color: {cs.to_hex(cs.panel_bg)};
98 color: {cs.to_hex(cs.text_primary)};
99 border: none;
100 border-radius: 3px;
101 selection-background-color: {cs.to_hex(cs.selection_bg)};
102 }}
103 QTreeWidget::item, QListWidget::item {{
104 padding: 4px;
105 border-bottom: 1px solid {cs.to_hex(cs.border_color)};
106 }}
107 QTreeWidget::item:hover, QListWidget::item:hover {{
108 background-color: {cs.to_hex(cs.hover_bg)};
109 }}
110 QTreeWidget::item:selected, QListWidget::item:selected {{
111 background-color: {cs.to_hex(cs.selection_bg)};
112 color: {cs.to_hex(cs.selection_text)};
113 }}
114 QHeaderView::section {{
115 background-color: {cs.to_hex(cs.panel_bg)};
116 color: {cs.to_hex(cs.text_primary)};
117 padding: 4px;
118 border: none;
119 border-bottom: 1px solid {cs.to_hex(cs.border_color)};
120 font-weight: normal;
121 }}
122 """
124 def generate_table_widget_style(self) -> str:
125 """
126 Generate QStyleSheet for table widgets.
128 Returns:
129 str: Complete QStyleSheet for table widget styling
130 """
131 cs = self.color_scheme
132 return f"""
133 QTableWidget {{
134 background-color: {cs.to_hex(cs.panel_bg)};
135 color: {cs.to_hex(cs.text_primary)};
136 border: none;
137 border-radius: 3px;
138 gridline-color: {cs.to_hex(cs.border_color)};
139 selection-background-color: {cs.to_hex(cs.selection_bg)};
140 }}
141 QTableWidget::item {{
142 padding: 4px;
143 border: none;
144 }}
145 QTableWidget::item:hover {{
146 background-color: {cs.to_hex(cs.hover_bg)};
147 }}
148 QTableWidget::item:selected {{
149 background-color: {cs.to_hex(cs.selection_bg)};
150 color: {cs.to_hex(cs.selection_text)};
151 }}
152 QHeaderView::section {{
153 background-color: {cs.to_hex(cs.input_bg)};
154 color: {cs.to_hex(cs.text_primary)};
155 padding: 5px;
156 border: none;
157 border-bottom: 1px solid {cs.to_hex(cs.border_color)};
158 font-weight: bold;
159 }}
160 """
162 def generate_button_style(self) -> str:
163 """
164 Generate QStyleSheet for buttons with all states.
166 Returns:
167 str: Complete QStyleSheet for button styling
168 """
169 cs = self.color_scheme
170 return f"""
171 QPushButton {{
172 background-color: {cs.to_hex(cs.button_normal_bg)};
173 color: {cs.to_hex(cs.button_text)};
174 border: none;
175 border-radius: 3px;
176 padding: 5px;
177 }}
178 QPushButton:hover {{
179 background-color: {cs.to_hex(cs.button_hover_bg)};
180 }}
181 QPushButton:pressed {{
182 background-color: {cs.to_hex(cs.button_pressed_bg)};
183 }}
184 QPushButton:disabled {{
185 background-color: {cs.to_hex(cs.button_disabled_bg)};
186 color: {cs.to_hex(cs.button_disabled_text)};
187 }}
188 """
190 def generate_combobox_style(self) -> str:
191 """
192 Generate QStyleSheet for combo boxes with dropdown styling.
194 Returns:
195 str: Complete QStyleSheet for combo box styling
196 """
197 cs = self.color_scheme
198 return f"""
199 QComboBox {{
200 background-color: {cs.to_hex(cs.input_bg)};
201 color: {cs.to_hex(cs.input_text)};
202 border: 1px solid {cs.to_hex(cs.input_border)};
203 border-radius: 3px;
204 padding: 5px;
205 }}
206 QComboBox::drop-down {{
207 border: none;
208 background-color: {cs.to_hex(cs.button_normal_bg)};
209 }}
210 QComboBox::down-arrow {{
211 border: none;
212 }}
213 QComboBox QAbstractItemView {{
214 background-color: {cs.to_hex(cs.input_bg)};
215 color: {cs.to_hex(cs.input_text)};
216 selection-background-color: {cs.to_hex(cs.selection_bg)};
217 }}
218 """
220 def generate_progress_bar_style(self) -> str:
221 """
222 Generate QStyleSheet for progress bars.
224 Returns:
225 str: Complete QStyleSheet for progress bar styling
226 """
227 cs = self.color_scheme
228 return f"""
229 QProgressBar {{
230 border: none;
231 border-radius: 3px;
232 background-color: {cs.to_hex(cs.progress_bg)};
233 color: {cs.to_hex(cs.text_primary)};
234 text-align: center;
235 }}
236 QProgressBar::chunk {{
237 background-color: {cs.to_hex(cs.progress_fill)};
238 border-radius: 2px;
239 }}
240 """
242 def generate_frame_style(self) -> str:
243 """
244 Generate QStyleSheet for frames and panels.
246 Returns:
247 str: Complete QStyleSheet for frame styling
248 """
249 cs = self.color_scheme
250 return f"""
251 QFrame {{
252 background-color: {cs.to_hex(cs.frame_bg)};
253 border: none;
254 border-radius: 3px;
255 padding: 5px;
256 }}
257 """
259 def generate_tab_widget_style(self) -> str:
260 """
261 Generate QStyleSheet for tab widgets.
263 Returns:
264 str: Complete QStyleSheet for tab widget styling
265 """
266 cs = self.color_scheme
267 return f"""
268 QTabWidget::pane {{
269 border: 1px solid {cs.to_hex(cs.border_color)};
270 border-radius: 3px;
271 background-color: {cs.to_hex(cs.panel_bg)};
272 }}
273 QTabBar::tab {{
274 background-color: {cs.to_hex(cs.button_normal_bg)};
275 color: {cs.to_hex(cs.text_secondary)};
276 border: none;
277 border-top-left-radius: 3px;
278 border-top-right-radius: 3px;
279 padding: 8px 16px;
280 margin-right: 2px;
281 }}
282 QTabBar::tab:selected {{
283 background-color: {cs.to_hex(cs.selection_bg)};
284 color: {cs.to_hex(cs.selection_text)};
285 font-weight: bold;
286 }}
287 QTabBar::tab:hover {{
288 background-color: {cs.to_hex(cs.button_hover_bg)};
289 }}
290 """
292 def generate_system_monitor_style(self) -> str:
293 """
294 Generate QStyleSheet for system monitor widget.
296 Returns:
297 str: Complete QStyleSheet for system monitor styling
298 """
299 cs = self.color_scheme
300 return f"""
301 SystemMonitorWidget {{
302 background-color: {cs.to_hex(cs.window_bg)};
303 color: {cs.to_hex(cs.text_primary)};
304 border: 1px solid {cs.to_hex(cs.border_color)};
305 border-radius: 5px;
306 }}
307 QLabel {{
308 color: {cs.to_hex(cs.text_primary)};
309 }}
310 #header_label {{
311 color: {cs.to_hex(cs.text_accent)};
312 font-weight: bold;
313 font-size: 14px;
314 }}
315 #info_panel {{
316 background-color: {cs.to_hex(cs.panel_bg)};
317 border: 2px solid {cs.to_hex(cs.border_color)};
318 border-radius: 8px;
319 padding: 5px;
320 }}
321 #info_title {{
322 color: {cs.to_hex(cs.text_accent)};
323 font-weight: bold;
324 padding-bottom: 5px;
325 }}
326 #info_label_key {{
327 color: {cs.to_hex(cs.text_secondary)};
328 }}
329 #info_label_value {{
330 color: {cs.to_hex(cs.text_primary)};
331 font-weight: bold;
332 }}
333 """
335 def generate_complete_application_style(self) -> str:
336 """
337 Generate complete application-wide QStyleSheet.
339 Returns:
340 str: Complete QStyleSheet for entire application
341 """
342 return (
343 self.generate_dialog_style() + "\n" +
344 self.generate_tree_widget_style() + "\n" +
345 self.generate_button_style() + "\n" +
346 self.generate_combobox_style() + "\n" +
347 self.generate_progress_bar_style() + "\n" +
348 self.generate_frame_style() + "\n" +
349 self.generate_system_monitor_style()
350 )
352 def generate_config_window_style(self) -> str:
353 """
354 Generate QStyleSheet for configuration windows with button panel.
356 Returns:
357 str: Complete QStyleSheet for config window styling
358 """
359 cs = self.color_scheme
360 return f"""
361 QDialog {{
362 background-color: {cs.to_hex(cs.window_bg)};
363 color: {cs.to_hex(cs.text_primary)};
364 }}
365 QGroupBox {{
366 font-weight: bold;
367 border: none;
368 border-radius: 5px;
369 margin-top: 5px;
370 padding-top: 5px;
371 background-color: {cs.to_hex(cs.panel_bg)};
372 color: {cs.to_hex(cs.text_primary)};
373 }}
374 QGroupBox::title {{
375 subcontrol-origin: margin;
376 left: 10px;
377 padding: 0 5px 0 5px;
378 color: {cs.to_hex(cs.text_accent)};
379 }}
380 QLabel {{
381 color: {cs.to_hex(cs.text_secondary)};
382 }}
383 QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox {{
384 background-color: {cs.to_hex(cs.input_bg)};
385 color: {cs.to_hex(cs.input_text)};
386 border: none;
387 border-radius: 3px;
388 padding: 5px;
389 }}
390 QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QComboBox:focus {{
391 border: 1px solid {cs.to_hex(cs.input_focus_border)};
392 }}
393 QPushButton {{
394 background-color: {cs.to_hex(cs.button_normal_bg)};
395 color: {cs.to_hex(cs.button_text)};
396 border: none;
397 border-radius: 3px;
398 padding: 5px;
399 }}
400 QPushButton:hover {{
401 background-color: {cs.to_hex(cs.button_hover_bg)};
402 }}
403 QPushButton:pressed {{
404 background-color: {cs.to_hex(cs.button_pressed_bg)};
405 }}
406 QPushButton:disabled {{
407 background-color: {cs.to_hex(cs.button_disabled_bg)};
408 color: {cs.to_hex(cs.button_disabled_text)};
409 }}
410 QCheckBox {{
411 color: {cs.to_hex(cs.text_primary)};
412 }}
413 QFrame {{
414 background-color: {cs.to_hex(cs.panel_bg)};
415 border: none;
416 border-radius: 3px;
417 padding: 5px;
418 }}
419 """
421 def generate_config_button_styles(self) -> dict:
422 """
423 Generate individual button styles for config window buttons.
425 Returns:
426 dict: Dictionary with button styles for reset, cancel, save
427 """
428 cs = self.color_scheme
430 return {
431 "reset": f"""
432 QPushButton {{
433 background-color: {cs.to_hex(cs.button_normal_bg)};
434 color: {cs.to_hex(cs.button_text)};
435 border: 1px solid {cs.to_hex(cs.border_light)};
436 border-radius: 3px;
437 padding: 8px;
438 }}
439 QPushButton:hover {{
440 background-color: {cs.to_hex(cs.button_hover_bg)};
441 }}
442 """,
443 "cancel": f"""
444 QPushButton {{
445 background-color: {cs.to_hex(cs.button_normal_bg)};
446 color: {cs.to_hex(cs.button_text)};
447 border: 1px solid {cs.to_hex(cs.border_light)};
448 border-radius: 3px;
449 padding: 8px;
450 }}
451 QPushButton:hover {{
452 background-color: {cs.to_hex(cs.button_hover_bg)};
453 }}
454 """,
455 "save": f"""
456 QPushButton {{
457 background-color: {cs.to_hex(cs.selection_bg)};
458 color: {cs.to_hex(cs.selection_text)};
459 border: 1px solid {cs.to_hex(cs.selection_bg)};
460 border-radius: 3px;
461 padding: 8px;
462 }}
463 QPushButton:hover {{
464 background-color: rgba({cs.selection_bg[0]}, {cs.selection_bg[1]}, {cs.selection_bg[2]}, 0.8);
465 }}
466 """
467 }
469 def generate_plate_manager_style(self) -> str:
470 """
471 Generate QStyleSheet for plate manager widget with all components.
473 Returns:
474 str: Complete QStyleSheet for plate manager styling
475 """
476 cs = self.color_scheme
477 return f"""
478 QListWidget {{
479 background-color: {cs.to_hex(cs.panel_bg)};
480 color: {cs.to_hex(cs.text_primary)};
481 border: none;
482 border-radius: 3px;
483 padding: 5px;
484 }}
485 QListWidget::item {{
486 padding: 5px;
487 border: none;
488 }}
489 QListWidget::item:selected {{
490 background-color: {cs.to_hex(cs.selection_bg)};
491 color: {cs.to_hex(cs.selection_text)};
492 }}
493 QListWidget::item:hover {{
494 background-color: {cs.to_hex(cs.hover_bg)};
495 }}
496 QFrame {{
497 background-color: {cs.to_hex(cs.window_bg)};
498 border: none;
499 border-radius: 3px;
500 padding: 5px;
501 }}
502 QPushButton {{
503 background-color: {cs.to_hex(cs.button_normal_bg)};
504 color: {cs.to_hex(cs.button_text)};
505 border: none;
506 border-radius: 3px;
507 padding: 5px;
508 }}
509 QPushButton:hover {{
510 background-color: {cs.to_hex(cs.button_hover_bg)};
511 }}
512 QPushButton:pressed {{
513 background-color: {cs.to_hex(cs.button_pressed_bg)};
514 }}
515 QPushButton:disabled {{
516 background-color: {cs.to_hex(cs.button_disabled_bg)};
517 color: {cs.to_hex(cs.button_disabled_text)};
518 }}
519 QProgressBar {{
520 border: none;
521 border-radius: 3px;
522 background-color: {cs.to_hex(cs.progress_bg)};
523 color: {cs.to_hex(cs.text_primary)};
524 text-align: center;
525 }}
526 QProgressBar::chunk {{
527 background-color: {cs.to_hex(cs.progress_fill)};
528 border-radius: 2px;
529 }}
530 """
532 def get_status_color_hex(self, status_type: str) -> str:
533 """
534 Get hex color string for status type.
536 Args:
537 status_type: Status type (success, warning, error, info)
539 Returns:
540 str: Hex color string for the status type
541 """
542 cs = self.color_scheme
543 status_colors = {
544 "success": cs.status_success,
545 "warning": cs.status_warning,
546 "error": cs.status_error,
547 "info": cs.status_info,
548 }
550 color = status_colors.get(status_type, cs.status_info)
551 return cs.to_hex(color)