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

46 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-10-01 18:33 +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: none; 

58 border-radius: 5px; 

59 margin-top: 5px; 

60 padding-top: 5px; 

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: none; 

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: none; 

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: none; 

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: none; 

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: none; 

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: none; 

276 border-radius: 5px; 

277 margin-top: 5px; 

278 padding-top: 5px; 

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: none; 

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

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

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

304 border: none; 

305 border-radius: 3px; 

306 padding: 5px; 

307 }} 

308 QPushButton:hover {{ 

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

310 }} 

311 QPushButton:pressed {{ 

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

313 }} 

314 QPushButton:disabled {{ 

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

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

317 }} 

318 QCheckBox {{ 

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

320 }} 

321 QFrame {{ 

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

323 border: none; 

324 border-radius: 3px; 

325 padding: 5px; 

326 }} 

327 """ 

328 

329 def generate_config_button_styles(self) -> dict: 

330 """ 

331 Generate individual button styles for config window buttons. 

332 

333 Returns: 

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

335 """ 

336 cs = self.color_scheme 

337 

338 return { 

339 "reset": f""" 

340 QPushButton {{ 

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

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

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

344 border-radius: 3px; 

345 padding: 8px; 

346 }} 

347 QPushButton:hover {{ 

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

349 }} 

350 """, 

351 "cancel": f""" 

352 QPushButton {{ 

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

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

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

356 border-radius: 3px; 

357 padding: 8px; 

358 }} 

359 QPushButton:hover {{ 

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

361 }} 

362 """, 

363 "save": f""" 

364 QPushButton {{ 

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

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

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

368 border-radius: 3px; 

369 padding: 8px; 

370 }} 

371 QPushButton:hover {{ 

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

373 }} 

374 """ 

375 } 

376 

377 def generate_plate_manager_style(self) -> str: 

378 """ 

379 Generate QStyleSheet for plate manager widget with all components. 

380 

381 Returns: 

382 str: Complete QStyleSheet for plate manager styling 

383 """ 

384 cs = self.color_scheme 

385 return f""" 

386 QListWidget {{ 

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

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

389 border: none; 

390 border-radius: 3px; 

391 padding: 5px; 

392 }} 

393 QListWidget::item {{ 

394 padding: 5px; 

395 border: none; 

396 }} 

397 QListWidget::item:selected {{ 

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

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

400 }} 

401 QListWidget::item:hover {{ 

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

403 }} 

404 QFrame {{ 

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

406 border: none; 

407 border-radius: 3px; 

408 padding: 5px; 

409 }} 

410 QPushButton {{ 

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

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

413 border: none; 

414 border-radius: 3px; 

415 padding: 5px; 

416 }} 

417 QPushButton:hover {{ 

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

419 }} 

420 QPushButton:pressed {{ 

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

422 }} 

423 QPushButton:disabled {{ 

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

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

426 }} 

427 QProgressBar {{ 

428 border: none; 

429 border-radius: 3px; 

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

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

432 text-align: center; 

433 }} 

434 QProgressBar::chunk {{ 

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

436 border-radius: 2px; 

437 }} 

438 """ 

439 

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

441 """ 

442 Get hex color string for status type. 

443 

444 Args: 

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

446 

447 Returns: 

448 str: Hex color string for the status type 

449 """ 

450 cs = self.color_scheme 

451 status_colors = { 

452 "success": cs.status_success, 

453 "warning": cs.status_warning, 

454 "error": cs.status_error, 

455 "info": cs.status_info, 

456 } 

457 

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

459 return cs.to_hex(color)