Coverage for openhcs/pyqt_gui/shared/style_generator.py: 0.0%

46 statements  

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

1""" 

2QStyleSheet Generator for OpenHCS PyQt6 GUI 

3 

4Generates dynamic QStyleSheet strings from PyQt6ColorScheme objects, enabling 

5centralized styling with theme support. Replaces hardcoded color strings with 

6semantic color scheme references. 

7""" 

8 

9import logging 

10from typing import Dict, Optional 

11from openhcs.pyqt_gui.shared.color_scheme import PyQt6ColorScheme 

12 

13logger = logging.getLogger(__name__) 

14 

15 

16class StyleSheetGenerator: 

17 """ 

18 Generates QStyleSheet strings from PyQt6ColorScheme objects. 

19  

20 Provides methods to generate complete stylesheets for different widget types, 

21 replacing hardcoded colors with centralized color scheme references. 

22 """ 

23 

24 def __init__(self, color_scheme: PyQt6ColorScheme): 

25 """ 

26 Initialize the style generator with a color scheme. 

27  

28 Args: 

29 color_scheme: PyQt6ColorScheme instance to use for styling 

30 """ 

31 self.color_scheme = color_scheme 

32 

33 def update_color_scheme(self, color_scheme: PyQt6ColorScheme): 

34 """ 

35 Update the color scheme used for style generation. 

36  

37 Args: 

38 color_scheme: New PyQt6ColorScheme instance 

39 """ 

40 self.color_scheme = color_scheme 

41 

42 def generate_dialog_style(self) -> str: 

43 """ 

44 Generate QStyleSheet for dialog windows. 

45  

46 Returns: 

47 str: Complete QStyleSheet for dialog styling 

48 """ 

49 cs = self.color_scheme 

50 return f""" 

51 QDialog {{ 

52 background-color: {cs.to_hex(cs.window_bg)}; 

53 color: {cs.to_hex(cs.text_primary)}; 

54 }} 

55 QGroupBox {{ 

56 font-weight: bold; 

57 border: 1px solid {cs.to_hex(cs.border_color)}; 

58 border-radius: 5px; 

59 margin-top: 10px; 

60 padding-top: 10px; 

61 background-color: {cs.to_hex(cs.panel_bg)}; 

62 color: {cs.to_hex(cs.text_primary)}; 

63 }} 

64 QGroupBox::title {{ 

65 subcontrol-origin: margin; 

66 left: 10px; 

67 padding: 0 5px 0 5px; 

68 color: {cs.to_hex(cs.text_accent)}; 

69 }} 

70 QLabel {{ 

71 color: {cs.to_hex(cs.text_secondary)}; 

72 }} 

73 QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox {{ 

74 background-color: {cs.to_hex(cs.input_bg)}; 

75 color: {cs.to_hex(cs.input_text)}; 

76 border: 1px solid {cs.to_hex(cs.input_border)}; 

77 border-radius: 3px; 

78 padding: 5px; 

79 }} 

80 QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QComboBox:focus {{ 

81 border: 1px solid {cs.to_hex(cs.input_focus_border)}; 

82 }} 

83 QCheckBox {{ 

84 color: {cs.to_hex(cs.text_primary)}; 

85 }} 

86 """ 

87 

88 def generate_tree_widget_style(self) -> str: 

89 """ 

90 Generate QStyleSheet for tree widgets and list widgets. 

91  

92 Returns: 

93 str: Complete QStyleSheet for tree/list widget styling 

94 """ 

95 cs = self.color_scheme 

96 return f""" 

97 QTreeWidget, QListWidget {{ 

98 background-color: {cs.to_hex(cs.panel_bg)}; 

99 color: {cs.to_hex(cs.text_primary)}; 

100 border: 1px solid {cs.to_hex(cs.border_color)}; 

101 border-radius: 3px; 

102 selection-background-color: {cs.to_hex(cs.selection_bg)}; 

103 }} 

104 QTreeWidget::item, QListWidget::item {{ 

105 padding: 4px; 

106 border-bottom: 1px solid {cs.to_hex(cs.separator_color)}; 

107 }} 

108 QTreeWidget::item:hover, QListWidget::item:hover {{ 

109 background-color: {cs.to_hex(cs.hover_bg)}; 

110 }} 

111 QTreeWidget::item:selected, QListWidget::item:selected {{ 

112 background-color: {cs.to_hex(cs.selection_bg)}; 

113 color: {cs.to_hex(cs.selection_text)}; 

114 }} 

115 """ 

116 

117 def generate_button_style(self) -> str: 

118 """ 

119 Generate QStyleSheet for buttons with all states. 

120  

121 Returns: 

122 str: Complete QStyleSheet for button styling 

123 """ 

124 cs = self.color_scheme 

125 return f""" 

126 QPushButton {{ 

127 background-color: {cs.to_hex(cs.button_normal_bg)}; 

128 color: {cs.to_hex(cs.button_text)}; 

129 border: 1px solid {cs.to_hex(cs.border_light)}; 

130 border-radius: 3px; 

131 padding: 5px; 

132 }} 

133 QPushButton:hover {{ 

134 background-color: {cs.to_hex(cs.button_hover_bg)}; 

135 }} 

136 QPushButton:pressed {{ 

137 background-color: {cs.to_hex(cs.button_pressed_bg)}; 

138 }} 

139 QPushButton:disabled {{ 

140 background-color: {cs.to_hex(cs.button_disabled_bg)}; 

141 color: {cs.to_hex(cs.button_disabled_text)}; 

142 }} 

143 """ 

144 

145 def generate_combobox_style(self) -> str: 

146 """ 

147 Generate QStyleSheet for combo boxes with dropdown styling. 

148  

149 Returns: 

150 str: Complete QStyleSheet for combo box styling 

151 """ 

152 cs = self.color_scheme 

153 return f""" 

154 QComboBox {{ 

155 background-color: {cs.to_hex(cs.input_bg)}; 

156 color: {cs.to_hex(cs.input_text)}; 

157 border: 1px solid {cs.to_hex(cs.input_border)}; 

158 border-radius: 3px; 

159 padding: 5px; 

160 }} 

161 QComboBox::drop-down {{ 

162 border: none; 

163 background-color: {cs.to_hex(cs.button_normal_bg)}; 

164 }} 

165 QComboBox::down-arrow {{ 

166 border: none; 

167 }} 

168 QComboBox QAbstractItemView {{ 

169 background-color: {cs.to_hex(cs.input_bg)}; 

170 color: {cs.to_hex(cs.input_text)}; 

171 selection-background-color: {cs.to_hex(cs.selection_bg)}; 

172 }} 

173 """ 

174 

175 def generate_progress_bar_style(self) -> str: 

176 """ 

177 Generate QStyleSheet for progress bars. 

178  

179 Returns: 

180 str: Complete QStyleSheet for progress bar styling 

181 """ 

182 cs = self.color_scheme 

183 return f""" 

184 QProgressBar {{ 

185 border: 1px solid {cs.to_hex(cs.border_color)}; 

186 border-radius: 3px; 

187 background-color: {cs.to_hex(cs.progress_bg)}; 

188 color: {cs.to_hex(cs.text_primary)}; 

189 text-align: center; 

190 }} 

191 QProgressBar::chunk {{ 

192 background-color: {cs.to_hex(cs.progress_fill)}; 

193 border-radius: 2px; 

194 }} 

195 """ 

196 

197 def generate_frame_style(self) -> str: 

198 """ 

199 Generate QStyleSheet for frames and panels. 

200  

201 Returns: 

202 str: Complete QStyleSheet for frame styling 

203 """ 

204 cs = self.color_scheme 

205 return f""" 

206 QFrame {{ 

207 background-color: {cs.to_hex(cs.frame_bg)}; 

208 border: 1px solid {cs.to_hex(cs.border_color)}; 

209 border-radius: 3px; 

210 padding: 5px; 

211 }} 

212 """ 

213 

214 def generate_system_monitor_style(self) -> str: 

215 """ 

216 Generate QStyleSheet for system monitor widget. 

217  

218 Returns: 

219 str: Complete QStyleSheet for system monitor styling 

220 """ 

221 cs = self.color_scheme 

222 return f""" 

223 SystemMonitorWidget {{ 

224 background-color: {cs.to_hex(cs.window_bg)}; 

225 color: {cs.to_hex(cs.text_primary)}; 

226 border: 1px solid {cs.to_hex(cs.border_color)}; 

227 border-radius: 5px; 

228 }} 

229 QLabel {{ 

230 color: {cs.to_hex(cs.text_primary)}; 

231 }} 

232 #header_label {{ 

233 color: {cs.to_hex(cs.text_accent)}; 

234 font-weight: bold; 

235 font-size: 14px; 

236 }} 

237 #info_label {{ 

238 color: {cs.to_hex(cs.text_secondary)}; 

239 font-size: 10px; 

240 }} 

241 """ 

242 

243 def generate_complete_application_style(self) -> str: 

244 """ 

245 Generate complete application-wide QStyleSheet. 

246  

247 Returns: 

248 str: Complete QStyleSheet for entire application 

249 """ 

250 return ( 

251 self.generate_dialog_style() + "\n" + 

252 self.generate_tree_widget_style() + "\n" + 

253 self.generate_button_style() + "\n" + 

254 self.generate_combobox_style() + "\n" + 

255 self.generate_progress_bar_style() + "\n" + 

256 self.generate_frame_style() + "\n" + 

257 self.generate_system_monitor_style() 

258 ) 

259 

260 def generate_config_window_style(self) -> str: 

261 """ 

262 Generate QStyleSheet for configuration windows with button panel. 

263 

264 Returns: 

265 str: Complete QStyleSheet for config window styling 

266 """ 

267 cs = self.color_scheme 

268 return f""" 

269 QDialog {{ 

270 background-color: {cs.to_hex(cs.window_bg)}; 

271 color: {cs.to_hex(cs.text_primary)}; 

272 }} 

273 QGroupBox {{ 

274 font-weight: bold; 

275 border: 1px solid {cs.to_hex(cs.border_color)}; 

276 border-radius: 5px; 

277 margin-top: 10px; 

278 padding-top: 10px; 

279 background-color: {cs.to_hex(cs.panel_bg)}; 

280 color: {cs.to_hex(cs.text_primary)}; 

281 }} 

282 QGroupBox::title {{ 

283 subcontrol-origin: margin; 

284 left: 10px; 

285 padding: 0 5px 0 5px; 

286 color: {cs.to_hex(cs.text_accent)}; 

287 }} 

288 QLabel {{ 

289 color: {cs.to_hex(cs.text_secondary)}; 

290 }} 

291 QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox {{ 

292 background-color: {cs.to_hex(cs.input_bg)}; 

293 color: {cs.to_hex(cs.input_text)}; 

294 border: 1px solid {cs.to_hex(cs.input_border)}; 

295 border-radius: 3px; 

296 padding: 5px; 

297 }} 

298 QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QComboBox:focus {{ 

299 border: 1px solid {cs.to_hex(cs.input_focus_border)}; 

300 }} 

301 QCheckBox {{ 

302 color: {cs.to_hex(cs.text_primary)}; 

303 }} 

304 QFrame {{ 

305 background-color: {cs.to_hex(cs.panel_bg)}; 

306 border: 1px solid {cs.to_hex(cs.border_color)}; 

307 border-radius: 3px; 

308 padding: 10px; 

309 }} 

310 """ 

311 

312 def generate_config_button_styles(self) -> dict: 

313 """ 

314 Generate individual button styles for config window buttons. 

315 

316 Returns: 

317 dict: Dictionary with button styles for reset, cancel, save 

318 """ 

319 cs = self.color_scheme 

320 

321 return { 

322 "reset": f""" 

323 QPushButton {{ 

324 background-color: {cs.to_hex(cs.button_normal_bg)}; 

325 color: {cs.to_hex(cs.button_text)}; 

326 border: 1px solid {cs.to_hex(cs.border_light)}; 

327 border-radius: 3px; 

328 padding: 8px; 

329 }} 

330 QPushButton:hover {{ 

331 background-color: {cs.to_hex(cs.button_hover_bg)}; 

332 }} 

333 """, 

334 "cancel": f""" 

335 QPushButton {{ 

336 background-color: {cs.to_hex(cs.status_error)}; 

337 color: {cs.to_hex(cs.text_primary)}; 

338 border: 1px solid {cs.to_hex(cs.status_error)}; 

339 border-radius: 3px; 

340 padding: 8px; 

341 }} 

342 QPushButton:hover {{ 

343 background-color: rgba({cs.status_error[0]}, {cs.status_error[1]}, {cs.status_error[2]}, 0.8); 

344 }} 

345 """, 

346 "save": f""" 

347 QPushButton {{ 

348 background-color: {cs.to_hex(cs.selection_bg)}; 

349 color: {cs.to_hex(cs.selection_text)}; 

350 border: 1px solid {cs.to_hex(cs.selection_bg)}; 

351 border-radius: 3px; 

352 padding: 8px; 

353 }} 

354 QPushButton:hover {{ 

355 background-color: rgba({cs.selection_bg[0]}, {cs.selection_bg[1]}, {cs.selection_bg[2]}, 0.8); 

356 }} 

357 """ 

358 } 

359 

360 def generate_plate_manager_style(self) -> str: 

361 """ 

362 Generate QStyleSheet for plate manager widget with all components. 

363 

364 Returns: 

365 str: Complete QStyleSheet for plate manager styling 

366 """ 

367 cs = self.color_scheme 

368 return f""" 

369 QListWidget {{ 

370 background-color: {cs.to_hex(cs.panel_bg)}; 

371 color: {cs.to_hex(cs.text_primary)}; 

372 border: 1px solid {cs.to_hex(cs.border_color)}; 

373 border-radius: 3px; 

374 padding: 5px; 

375 }} 

376 QListWidget::item {{ 

377 padding: 5px; 

378 border-bottom: 1px solid {cs.to_hex(cs.separator_color)}; 

379 }} 

380 QListWidget::item:selected {{ 

381 background-color: {cs.to_hex(cs.selection_bg)}; 

382 color: {cs.to_hex(cs.selection_text)}; 

383 }} 

384 QListWidget::item:hover {{ 

385 background-color: {cs.to_hex(cs.hover_bg)}; 

386 }} 

387 QFrame {{ 

388 background-color: {cs.to_hex(cs.window_bg)}; 

389 border: 1px solid {cs.to_hex(cs.border_color)}; 

390 border-radius: 3px; 

391 padding: 5px; 

392 }} 

393 QPushButton {{ 

394 background-color: {cs.to_hex(cs.button_normal_bg)}; 

395 color: {cs.to_hex(cs.button_text)}; 

396 border: 1px solid {cs.to_hex(cs.border_light)}; 

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 QProgressBar {{ 

411 border: 1px solid {cs.to_hex(cs.border_color)}; 

412 border-radius: 3px; 

413 background-color: {cs.to_hex(cs.progress_bg)}; 

414 color: {cs.to_hex(cs.text_primary)}; 

415 text-align: center; 

416 }} 

417 QProgressBar::chunk {{ 

418 background-color: {cs.to_hex(cs.progress_fill)}; 

419 border-radius: 2px; 

420 }} 

421 """ 

422 

423 def get_status_color_hex(self, status_type: str) -> str: 

424 """ 

425 Get hex color string for status type. 

426 

427 Args: 

428 status_type: Status type (success, warning, error, info) 

429 

430 Returns: 

431 str: Hex color string for the status type 

432 """ 

433 cs = self.color_scheme 

434 status_colors = { 

435 "success": cs.status_success, 

436 "warning": cs.status_warning, 

437 "error": cs.status_error, 

438 "info": cs.status_info, 

439 } 

440 

441 color = status_colors.get(status_type, cs.status_info) 

442 return cs.to_hex(color)