styles.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. """
  2. Styles Module
  3. Centralized stylesheet definitions for the application.
  4. """
  5. from utils.config import UI_COLORS
  6. # ==================== COLOR CONSTANTS ====================
  7. # Export COLORS dictionary for tab modules
  8. COLORS = {
  9. # Primary colors
  10. 'primary': '#3498db',
  11. 'primary_dark': '#2c3e50',
  12. 'primary_light': '#34495e',
  13. # Status colors
  14. 'success': '#27ae60',
  15. 'warning': '#f39c12',
  16. 'error': '#e74c3c',
  17. 'info': '#3498db',
  18. # Background colors
  19. 'bg_light': '#f8f9fa',
  20. 'bg_white': '#ffffff',
  21. 'bg_dark': '#2c3e50',
  22. 'card_bg': '#ffffff',
  23. # Text colors
  24. 'text_primary': '#2c3e50',
  25. 'text_secondary': '#7f8c8d',
  26. 'text_light': '#bdc3c7',
  27. # Border colors
  28. 'border': '#ddd',
  29. 'border_light': '#ecf0f1',
  30. # Phase 8: Classification colors
  31. 'ripeness_unripe': '#95a5a6',
  32. 'ripeness_midripe': '#f39c12',
  33. 'ripeness_ripe': '#27ae60',
  34. 'ripeness_overripe': '#e74c3c',
  35. # Phase 8: Panel header colors
  36. 'panel_rgb': '#3498db',
  37. 'panel_multispectral': '#8e44ad',
  38. 'panel_audio': '#16a085',
  39. 'panel_results': '#27ae60',
  40. 'panel_control': '#34495e',
  41. # Phase 8: Status indicators
  42. 'status_online': '#27ae60',
  43. 'status_offline': '#e74c3c',
  44. 'status_processing': '#f39c12',
  45. }
  46. # Export STYLES dictionary for common button styles
  47. STYLES = {
  48. 'button_primary': f"""
  49. QPushButton {{
  50. background-color: {COLORS['primary']};
  51. border: 2px solid {COLORS['primary']};
  52. border-radius: 8px;
  53. color: white;
  54. font-weight: bold;
  55. font-size: 14px;
  56. padding: 10px 20px;
  57. }}
  58. QPushButton:hover {{
  59. background-color: #2980b9;
  60. }}
  61. QPushButton:pressed {{
  62. background-color: #2471a3;
  63. }}
  64. QPushButton:disabled {{
  65. background-color: {COLORS['text_secondary']};
  66. border-color: {COLORS['text_secondary']};
  67. }}
  68. """,
  69. 'button_success': f"""
  70. QPushButton {{
  71. background-color: {COLORS['success']};
  72. border: 2px solid {COLORS['success']};
  73. border-radius: 8px;
  74. color: white;
  75. font-weight: bold;
  76. font-size: 14px;
  77. padding: 10px 20px;
  78. }}
  79. QPushButton:hover {{
  80. background-color: #229954;
  81. }}
  82. QPushButton:pressed {{
  83. background-color: #1e8449;
  84. }}
  85. """,
  86. }
  87. # ==================== MAIN WINDOW STYLES ====================
  88. MAIN_WINDOW_STYLE = f"""
  89. QMainWindow {{
  90. background-color: {UI_COLORS['bg_light']};
  91. }}
  92. """
  93. # ==================== GROUP BOX STYLES ====================
  94. GROUP_BOX_STYLE = f"""
  95. QGroupBox {{
  96. font-weight: bold;
  97. font-size: 16px;
  98. border: 2px solid #ddd;
  99. border-radius: 5px;
  100. margin-top: 10px;
  101. padding-top: 10px;
  102. background-color: {UI_COLORS['bg_white']};
  103. }}
  104. QGroupBox::title {{
  105. subcontrol-origin: margin;
  106. left: 10px;
  107. padding: 0 10px 0 10px;
  108. color: {UI_COLORS['text_dark']};
  109. background-color: {UI_COLORS['bg_panel']};
  110. }}
  111. """
  112. # ==================== BUTTON STYLES ====================
  113. def get_button_style(
  114. bg_color: str,
  115. hover_color: str,
  116. pressed_color: str = None,
  117. border_color: str = None,
  118. text_color: str = "white"
  119. ) -> str:
  120. """
  121. Generate a button stylesheet with the given colors.
  122. Args:
  123. bg_color: Background color
  124. hover_color: Hover state color
  125. pressed_color: Pressed state color (defaults to hover_color)
  126. border_color: Border color (defaults to hover_color)
  127. text_color: Text color (defaults to white)
  128. Returns:
  129. str: Complete button stylesheet
  130. """
  131. if pressed_color is None:
  132. pressed_color = hover_color
  133. if border_color is None:
  134. border_color = hover_color
  135. return f"""
  136. QPushButton {{
  137. background-color: {bg_color};
  138. border: 2px solid {border_color};
  139. border-radius: 8px;
  140. color: {text_color};
  141. font-weight: bold;
  142. font-size: 16px;
  143. padding: 15px;
  144. }}
  145. QPushButton:hover {{
  146. background-color: {hover_color};
  147. }}
  148. QPushButton:pressed {{
  149. background-color: {pressed_color};
  150. }}
  151. """
  152. RIPENESS_BUTTON_STYLE = get_button_style(
  153. UI_COLORS['btn_green'],
  154. UI_COLORS['btn_green_hover'],
  155. "#1e8449"
  156. )
  157. QUALITY_BUTTON_STYLE = get_button_style(
  158. UI_COLORS['btn_blue'],
  159. UI_COLORS['btn_blue_hover'],
  160. "#2471a3"
  161. )
  162. CALIBRATION_BUTTON_STYLE = get_button_style(
  163. UI_COLORS['btn_orange'],
  164. UI_COLORS['btn_orange_hover'],
  165. "#d68910"
  166. )
  167. BATCH_BUTTON_STYLE = get_button_style(
  168. UI_COLORS['btn_purple'],
  169. UI_COLORS['btn_purple_hover'],
  170. "#7d3c98"
  171. )
  172. LOCULE_BUTTON_STYLE = get_button_style(
  173. "#16a085", # Teal
  174. "#138d75", # Darker teal
  175. "#117864" # Even darker teal
  176. )
  177. EMERGENCY_BUTTON_STYLE = f"""
  178. QPushButton {{
  179. background-color: {UI_COLORS['btn_red']};
  180. border: 2px solid {UI_COLORS['btn_red_hover']};
  181. border-radius: 15px;
  182. color: white;
  183. font-weight: bold;
  184. font-size: 14px;
  185. }}
  186. QPushButton:hover {{
  187. background-color: {UI_COLORS['btn_red_hover']};
  188. }}
  189. """
  190. STANDARD_BUTTON_STYLE = f"""
  191. QPushButton {{
  192. background-color: {UI_COLORS['primary_light']};
  193. border: 1px solid {UI_COLORS['primary_dark']};
  194. border-radius: 4px;
  195. color: white;
  196. padding: 10px;
  197. font-size: 16px;
  198. }}
  199. QPushButton:hover {{
  200. background-color: {UI_COLORS['primary_dark']};
  201. }}
  202. """
  203. # ==================== TABLE STYLES ====================
  204. TABLE_STYLE = f"""
  205. QTableWidget {{
  206. gridline-color: #ddd;
  207. background-color: {UI_COLORS['bg_white']};
  208. alternate-background-color: {UI_COLORS['bg_light']};
  209. font-size: 16px;
  210. }}
  211. QHeaderView::section {{
  212. background-color: {UI_COLORS['bg_light']};
  213. padding: 8px;
  214. border: 1px solid #ddd;
  215. font-weight: bold;
  216. font-size: 16px;
  217. }}
  218. """
  219. # ==================== TAB WIDGET STYLES ====================
  220. TAB_WIDGET_STYLE = f"""
  221. QTabWidget::pane {{
  222. border: 1px solid {UI_COLORS['primary_light']};
  223. background-color: {UI_COLORS['bg_white']};
  224. }}
  225. QTabBar::tab {{
  226. background-color: {UI_COLORS['primary_light']};
  227. color: {UI_COLORS['bg_panel']};
  228. padding: 12px 16px;
  229. margin-right: 2px;
  230. font-size: 14px;
  231. font-weight: bold;
  232. min-width: 120px;
  233. max-width: 180px;
  234. }}
  235. QTabBar::tab:selected {{
  236. background-color: {UI_COLORS['accent_blue']};
  237. color: white;
  238. }}
  239. QTabBar::tab:hover {{
  240. background-color: {UI_COLORS['accent_blue']};
  241. opacity: 0.8;
  242. }}
  243. QTabBar::left-button, QTabBar::right-button {{
  244. background-color: {UI_COLORS['primary_light']};
  245. border: none;
  246. }}
  247. """
  248. # ==================== HEADER STYLES ====================
  249. HEADER_STYLE = f"""
  250. QFrame {{
  251. background-color: {UI_COLORS['primary_dark']};
  252. }}
  253. QLabel {{
  254. color: white;
  255. }}
  256. """
  257. HEADER_ICON_BUTTON_STYLE = f"""
  258. QPushButton {{
  259. background-color: transparent;
  260. border: 2px solid transparent;
  261. border-radius: 8px;
  262. color: white;
  263. font-size: 20px;
  264. padding: 8px 12px;
  265. min-width: 45px;
  266. min-height: 45px;
  267. }}
  268. QPushButton:hover {{
  269. background-color: rgba(255, 255, 255, 0.1);
  270. border: 2px solid rgba(255, 255, 255, 0.3);
  271. }}
  272. QPushButton:pressed {{
  273. background-color: rgba(255, 255, 255, 0.2);
  274. }}
  275. """
  276. # ==================== STATUS BAR STYLES ====================
  277. STATUS_BAR_STYLE = f"""
  278. QFrame {{
  279. background-color: {UI_COLORS['primary_light']};
  280. }}
  281. QLabel {{
  282. color: {UI_COLORS['bg_panel']};
  283. font-size: 16px;
  284. }}
  285. """
  286. # ==================== LABEL STYLES ====================
  287. def get_label_style(
  288. color: str = None,
  289. font_size: int = 16,
  290. font_weight: str = "normal",
  291. background: str = None
  292. ) -> str:
  293. """
  294. Generate a label stylesheet.
  295. Args:
  296. color: Text color
  297. font_size: Font size in pixels
  298. font_weight: Font weight (normal, bold)
  299. background: Background color
  300. Returns:
  301. str: Label stylesheet
  302. """
  303. styles = []
  304. if color:
  305. styles.append(f"color: {color};")
  306. if font_size:
  307. styles.append(f"font-size: {font_size}px;")
  308. if font_weight:
  309. styles.append(f"font-weight: {font_weight};")
  310. if background:
  311. styles.append(f"background-color: {background};")
  312. return " ".join(styles)
  313. HEADER_LABEL_STYLE = get_label_style(
  314. color=UI_COLORS['text_dark'],
  315. font_size=16,
  316. font_weight="bold"
  317. )
  318. INFO_LABEL_STYLE = get_label_style(
  319. color=UI_COLORS['text_medium'],
  320. font_size=16
  321. )
  322. STATUS_LABEL_STYLE = get_label_style(
  323. color=UI_COLORS['text_dark'],
  324. font_size=16
  325. )
  326. # ==================== FEED/DISPLAY STYLES ====================
  327. def get_feed_style(bg_color: str, border_color: str) -> str:
  328. """
  329. Generate a camera feed display stylesheet.
  330. Args:
  331. bg_color: Background color
  332. border_color: Border color
  333. Returns:
  334. str: Feed display stylesheet
  335. """
  336. return f"""
  337. background-color: {bg_color};
  338. border: 1px solid {border_color};
  339. color: white;
  340. font-size: 16px;
  341. """
  342. RGB_FEED_STYLE = get_feed_style(UI_COLORS['primary_dark'], UI_COLORS['primary_light'])
  343. MS_FEED_STYLE = get_feed_style(UI_COLORS['btn_purple'], "#9b59b6")
  344. THERMAL_FEED_STYLE = get_feed_style(UI_COLORS['text_medium'], "#95a5a6")
  345. AUDIO_FEED_STYLE = get_feed_style("#16a085", "#1abc9c")
  346. # ==================== APPLICATION STYLE ====================
  347. APPLICATION_STYLE = """
  348. QApplication {
  349. font-family: Arial, sans-serif;
  350. }
  351. """
  352. # ==================== LOADING OVERLAY STYLE ====================
  353. LOADING_STYLE = """
  354. background-color: rgba(255, 255, 255, 0%);
  355. """
  356. # ==================== UTILITY FUNCTIONS ====================
  357. def get_status_indicator_style(status: str) -> str:
  358. """
  359. Get the stylesheet for a status indicator.
  360. Args:
  361. status: Status type ('online', 'offline', 'updating')
  362. Returns:
  363. str: Stylesheet for the status indicator
  364. """
  365. status_colors = {
  366. "online": UI_COLORS['online'],
  367. "offline": UI_COLORS['offline'],
  368. "updating": UI_COLORS['updating'],
  369. }
  370. color = status_colors.get(status, UI_COLORS['text_medium'])
  371. return f"background-color: {color}; border-radius: 6px;"
  372. def get_grade_color(grade: str) -> str:
  373. """
  374. Get the color for a quality grade.
  375. Args:
  376. grade: Grade letter ('A', 'B', 'C', etc.)
  377. Returns:
  378. str: Color hex code
  379. """
  380. # Handle None or empty grades
  381. if not grade:
  382. return UI_COLORS['text_dark']
  383. grade_colors = {
  384. "A": UI_COLORS['grade_a'],
  385. "B": UI_COLORS['grade_b'],
  386. "C": UI_COLORS['grade_c'],
  387. }
  388. return grade_colors.get(str(grade).upper(), UI_COLORS['text_dark'])
  389. def get_ripeness_color(ripeness: str) -> str:
  390. """
  391. Get the color for a ripeness classification.
  392. Args:
  393. ripeness: Ripeness type ('Ripe', 'Overripe', 'Unripe')
  394. Returns:
  395. str: Color hex code
  396. """
  397. ripeness_colors = {
  398. "Ripe": UI_COLORS['online'],
  399. "Overripe": UI_COLORS['updating'],
  400. "Unripe": UI_COLORS['text_medium'],
  401. }
  402. return ripeness_colors.get(ripeness, UI_COLORS['text_dark'])