Python-FastUI-Widgets 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. fastuiwidgets/__init__.py +12 -0
  2. fastuiwidgets/_rc/__init__.py +0 -0
  3. fastuiwidgets/_rc/resource.py +98835 -0
  4. fastuiwidgets/common/__init__.py +12 -0
  5. fastuiwidgets/common/animation.py +530 -0
  6. fastuiwidgets/common/auto_wrap.py +164 -0
  7. fastuiwidgets/common/color.py +95 -0
  8. fastuiwidgets/common/config.py +423 -0
  9. fastuiwidgets/common/exception_handler.py +31 -0
  10. fastuiwidgets/common/font.py +38 -0
  11. fastuiwidgets/common/icon.py +703 -0
  12. fastuiwidgets/common/image_utils.py +198 -0
  13. fastuiwidgets/common/overload.py +47 -0
  14. fastuiwidgets/common/router.py +133 -0
  15. fastuiwidgets/common/screen.py +25 -0
  16. fastuiwidgets/common/smooth_scroll.py +141 -0
  17. fastuiwidgets/common/style_sheet.py +512 -0
  18. fastuiwidgets/common/theme_listener.py +27 -0
  19. fastuiwidgets/common/translator.py +14 -0
  20. fastuiwidgets/components/__init__.py +6 -0
  21. fastuiwidgets/components/date_time/__init__.py +4 -0
  22. fastuiwidgets/components/date_time/calendar_picker.py +121 -0
  23. fastuiwidgets/components/date_time/calendar_view.py +671 -0
  24. fastuiwidgets/components/date_time/date_picker.py +245 -0
  25. fastuiwidgets/components/date_time/fast_calendar_view.py +487 -0
  26. fastuiwidgets/components/date_time/picker_base.py +632 -0
  27. fastuiwidgets/components/date_time/time_picker.py +223 -0
  28. fastuiwidgets/components/dialog_box/__init__.py +6 -0
  29. fastuiwidgets/components/dialog_box/color_dialog.py +414 -0
  30. fastuiwidgets/components/dialog_box/dialog.py +167 -0
  31. fastuiwidgets/components/dialog_box/folder_list_dialog.py +307 -0
  32. fastuiwidgets/components/dialog_box/mask_dialog_base.py +120 -0
  33. fastuiwidgets/components/dialog_box/message_box_base.py +92 -0
  34. fastuiwidgets/components/dialog_box/message_dialog.py +65 -0
  35. fastuiwidgets/components/layout/__init__.py +3 -0
  36. fastuiwidgets/components/layout/expand_layout.py +96 -0
  37. fastuiwidgets/components/layout/flow_layout.py +236 -0
  38. fastuiwidgets/components/layout/v_box_layout.py +41 -0
  39. fastuiwidgets/components/material/__init__.py +6 -0
  40. fastuiwidgets/components/material/acrylic_combo_box.py +96 -0
  41. fastuiwidgets/components/material/acrylic_flyout.py +105 -0
  42. fastuiwidgets/components/material/acrylic_line_edit.py +27 -0
  43. fastuiwidgets/components/material/acrylic_menu.py +204 -0
  44. fastuiwidgets/components/material/acrylic_tool_tip.py +39 -0
  45. fastuiwidgets/components/material/acrylic_widget.py +42 -0
  46. fastuiwidgets/components/navigation/__init__.py +9 -0
  47. fastuiwidgets/components/navigation/breadcrumb.py +350 -0
  48. fastuiwidgets/components/navigation/navigation_bar.py +416 -0
  49. fastuiwidgets/components/navigation/navigation_interface.py +268 -0
  50. fastuiwidgets/components/navigation/navigation_panel.py +657 -0
  51. fastuiwidgets/components/navigation/navigation_widget.py +686 -0
  52. fastuiwidgets/components/navigation/pivot.py +272 -0
  53. fastuiwidgets/components/navigation/segmented_widget.py +174 -0
  54. fastuiwidgets/components/settings/__init__.py +8 -0
  55. fastuiwidgets/components/settings/custom_color_setting_card.py +139 -0
  56. fastuiwidgets/components/settings/expand_setting_card.py +390 -0
  57. fastuiwidgets/components/settings/folder_list_setting_card.py +134 -0
  58. fastuiwidgets/components/settings/options_setting_card.py +86 -0
  59. fastuiwidgets/components/settings/setting_card.py +449 -0
  60. fastuiwidgets/components/settings/setting_card_group.py +48 -0
  61. fastuiwidgets/components/widgets/__init__.py +41 -0
  62. fastuiwidgets/components/widgets/acrylic_label.py +261 -0
  63. fastuiwidgets/components/widgets/button.py +1059 -0
  64. fastuiwidgets/components/widgets/card_widget.py +369 -0
  65. fastuiwidgets/components/widgets/check_box.py +203 -0
  66. fastuiwidgets/components/widgets/combo_box.py +556 -0
  67. fastuiwidgets/components/widgets/command_bar.py +636 -0
  68. fastuiwidgets/components/widgets/cycle_list_widget.py +251 -0
  69. fastuiwidgets/components/widgets/flip_view.py +430 -0
  70. fastuiwidgets/components/widgets/flyout.py +521 -0
  71. fastuiwidgets/components/widgets/frameless_window.py +49 -0
  72. fastuiwidgets/components/widgets/icon_widget.py +53 -0
  73. fastuiwidgets/components/widgets/info_badge.py +483 -0
  74. fastuiwidgets/components/widgets/info_bar.py +596 -0
  75. fastuiwidgets/components/widgets/label.py +553 -0
  76. fastuiwidgets/components/widgets/line_edit.py +551 -0
  77. fastuiwidgets/components/widgets/list_view.py +158 -0
  78. fastuiwidgets/components/widgets/menu.py +1318 -0
  79. fastuiwidgets/components/widgets/pips_pager.py +331 -0
  80. fastuiwidgets/components/widgets/progress_bar.py +311 -0
  81. fastuiwidgets/components/widgets/progress_ring.py +212 -0
  82. fastuiwidgets/components/widgets/scroll_area.py +125 -0
  83. fastuiwidgets/components/widgets/scroll_bar.py +673 -0
  84. fastuiwidgets/components/widgets/separator.py +43 -0
  85. fastuiwidgets/components/widgets/slider.py +307 -0
  86. fastuiwidgets/components/widgets/spin_box.py +306 -0
  87. fastuiwidgets/components/widgets/stacked_widget.py +211 -0
  88. fastuiwidgets/components/widgets/state_tool_tip.py +188 -0
  89. fastuiwidgets/components/widgets/switch_button.py +312 -0
  90. fastuiwidgets/components/widgets/tab_view.py +804 -0
  91. fastuiwidgets/components/widgets/table_view.py +360 -0
  92. fastuiwidgets/components/widgets/teaching_tip.py +657 -0
  93. fastuiwidgets/components/widgets/tool_tip.py +460 -0
  94. fastuiwidgets/components/widgets/tree_view.py +216 -0
  95. fastuiwidgets/multimedia/__init__.py +3 -0
  96. fastuiwidgets/multimedia/media_play_bar.py +319 -0
  97. fastuiwidgets/multimedia/media_player.py +124 -0
  98. fastuiwidgets/multimedia/video_widget.py +93 -0
  99. fastuiwidgets/window/__init__.py +2 -0
  100. fastuiwidgets/window/fluent_window.py +413 -0
  101. fastuiwidgets/window/splash_screen.py +92 -0
  102. fastuiwidgets/window/stacked_widget.py +66 -0
  103. python_fastui_widgets-1.0.0.dist-info/METADATA +30 -0
  104. python_fastui_widgets-1.0.0.dist-info/RECORD +107 -0
  105. python_fastui_widgets-1.0.0.dist-info/WHEEL +5 -0
  106. python_fastui_widgets-1.0.0.dist-info/licenses/LICENSE +674 -0
  107. python_fastui_widgets-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,512 @@
1
+ # coding:utf-8
2
+ from enum import Enum
3
+ from string import Template
4
+ from typing import List, Union
5
+ import weakref
6
+
7
+ from PySide6.QtCore import QFile, QObject, QEvent, QDynamicPropertyChangeEvent
8
+ from PySide6.QtGui import QColor
9
+ from PySide6.QtWidgets import QWidget
10
+
11
+ from .config import qconfig, Theme, isDarkTheme
12
+
13
+
14
+ class StyleSheetManager(QObject):
15
+ """ Style sheet manager """
16
+
17
+ def __init__(self):
18
+ self.widgets = weakref.WeakKeyDictionary()
19
+
20
+ def register(self, source, widget: QWidget, reset=True):
21
+ """ register widget to manager
22
+
23
+ Parameters
24
+ ----------
25
+ source: str | StyleSheetBase
26
+ qss source, it could be:
27
+ * `str`: qss file path
28
+ * `StyleSheetBase`: style sheet instance
29
+
30
+ widget: QWidget
31
+ the widget to set style sheet
32
+
33
+ reset: bool
34
+ whether to reset the qss source
35
+ """
36
+ if isinstance(source, str):
37
+ source = StyleSheetFile(source)
38
+
39
+ if widget not in self.widgets:
40
+ widget.destroyed.connect(lambda: self.deregister(widget))
41
+ widget.installEventFilter(CustomStyleSheetWatcher(widget))
42
+ widget.installEventFilter(DirtyStyleSheetWatcher(widget))
43
+ self.widgets[widget] = StyleSheetCompose([source, CustomStyleSheet(widget)])
44
+
45
+ if not reset:
46
+ self.source(widget).add(source)
47
+ else:
48
+ self.widgets[widget] = StyleSheetCompose([source, CustomStyleSheet(widget)])
49
+
50
+ def deregister(self, widget: QWidget):
51
+ """ deregister widget from manager """
52
+ if widget not in self.widgets:
53
+ return
54
+
55
+ self.widgets.pop(widget)
56
+
57
+ def items(self):
58
+ return self.widgets.items()
59
+
60
+ def source(self, widget: QWidget):
61
+ """ get the qss source of widget """
62
+ return self.widgets.get(widget, StyleSheetCompose([]))
63
+
64
+
65
+ styleSheetManager = StyleSheetManager()
66
+
67
+
68
+ class QssTemplate(Template):
69
+ """ style sheet template """
70
+
71
+ delimiter = '--'
72
+
73
+
74
+ def applyThemeColor(qss: str):
75
+ """ apply theme color to style sheet
76
+
77
+ Parameters
78
+ ----------
79
+ qss: str
80
+ the style sheet string to apply theme color, the substituted variable
81
+ should be equal to the value of `ThemeColor` and starts width `--`, i.e `--ThemeColorPrimary`
82
+ """
83
+ template = QssTemplate(qss)
84
+ mappings = {c.value: c.name() for c in ThemeColor._member_map_.values()}
85
+ return template.safe_substitute(mappings)
86
+
87
+
88
+ class StyleSheetBase:
89
+ """ Style sheet base class """
90
+
91
+ def path(self, theme=Theme.AUTO):
92
+ """ get the path of style sheet """
93
+ raise NotImplementedError
94
+
95
+ def content(self, theme=Theme.AUTO):
96
+ """ get the content of style sheet """
97
+ return getStyleSheetFromFile(self.path(theme))
98
+
99
+ def apply(self, widget: QWidget, theme=Theme.AUTO):
100
+ """ apply style sheet to widget """
101
+ setStyleSheet(widget, self, theme)
102
+
103
+
104
+ class FluentStyleSheet(StyleSheetBase, Enum):
105
+ """ Fluent style sheet """
106
+
107
+ MENU = "menu"
108
+ LABEL = "label"
109
+ PIVOT = "pivot"
110
+ BUTTON = "button"
111
+ DIALOG = "dialog"
112
+ SLIDER = "slider"
113
+ INFO_BAR = "info_bar"
114
+ SPIN_BOX = "spin_box"
115
+ TAB_VIEW = "tab_view"
116
+ TOOL_TIP = "tool_tip"
117
+ CHECK_BOX = "check_box"
118
+ COMBO_BOX = "combo_box"
119
+ FLIP_VIEW = "flip_view"
120
+ LINE_EDIT = "line_edit"
121
+ LIST_VIEW = "list_view"
122
+ TREE_VIEW = "tree_view"
123
+ INFO_BADGE = "info_badge"
124
+ PIPS_PAGER = "pips_pager"
125
+ TABLE_VIEW = "table_view"
126
+ CARD_WIDGET = "card_widget"
127
+ TIME_PICKER = "time_picker"
128
+ COLOR_DIALOG = "color_dialog"
129
+ MEDIA_PLAYER = "media_player"
130
+ SETTING_CARD = "setting_card"
131
+ TEACHING_TIP = "teaching_tip"
132
+ FLUENT_WINDOW = "fluent_window"
133
+ SWITCH_BUTTON = "switch_button"
134
+ MESSAGE_DIALOG = "message_dialog"
135
+ STATE_TOOL_TIP = "state_tool_tip"
136
+ CALENDAR_PICKER = "calendar_picker"
137
+ FOLDER_LIST_DIALOG = "folder_list_dialog"
138
+ SETTING_CARD_GROUP = "setting_card_group"
139
+ EXPAND_SETTING_CARD = "expand_setting_card"
140
+ NAVIGATION_INTERFACE = "navigation_interface"
141
+
142
+ def path(self, theme=Theme.AUTO):
143
+ theme = qconfig.theme if theme == Theme.AUTO else theme
144
+ return f":/fastuiwidgets/qss/{theme.value.lower()}/{self.value}.qss"
145
+
146
+
147
+ class StyleSheetFile(StyleSheetBase):
148
+ """ Style sheet file """
149
+
150
+ def __init__(self, path: str):
151
+ super().__init__()
152
+ self.filePath = path
153
+
154
+ def path(self, theme=Theme.AUTO):
155
+ return self.filePath
156
+
157
+
158
+ class CustomStyleSheet(StyleSheetBase):
159
+ """ Custom style sheet """
160
+
161
+ DARK_QSS_KEY = 'darkCustomQss'
162
+ LIGHT_QSS_KEY = 'lightCustomQss'
163
+
164
+ def __init__(self, widget: QWidget) -> None:
165
+ super().__init__()
166
+ self._widget = weakref.ref(widget)
167
+
168
+ def path(self, theme=Theme.AUTO):
169
+ return ''
170
+
171
+ @property
172
+ def widget(self):
173
+ return self._widget()
174
+
175
+ def __eq__(self, other: object) -> bool:
176
+ if not isinstance(other, CustomStyleSheet):
177
+ return False
178
+
179
+ return other.widget is self.widget
180
+
181
+ def setCustomStyleSheet(self, lightQss: str, darkQss: str):
182
+ """ set custom style sheet in light and dark theme mode """
183
+ self.setLightStyleSheet(lightQss)
184
+ self.setDarkStyleSheet(darkQss)
185
+ return self
186
+
187
+ def setLightStyleSheet(self, qss: str):
188
+ """ set the style sheet in light mode """
189
+ if self.widget:
190
+ self.widget.setProperty(self.LIGHT_QSS_KEY, qss)
191
+
192
+ return self
193
+
194
+ def setDarkStyleSheet(self, qss: str):
195
+ """ set the style sheet in dark mode """
196
+ if self.widget:
197
+ self.widget.setProperty(self.DARK_QSS_KEY, qss)
198
+
199
+ return self
200
+
201
+ def lightStyleSheet(self) -> str:
202
+ if not self.widget:
203
+ return ''
204
+
205
+ return self.widget.property(self.LIGHT_QSS_KEY) or ''
206
+
207
+ def darkStyleSheet(self) -> str:
208
+ if not self.widget:
209
+ return ''
210
+
211
+ return self.widget.property(self.DARK_QSS_KEY) or ''
212
+
213
+ def content(self, theme=Theme.AUTO) -> str:
214
+ theme = qconfig.theme if theme == Theme.AUTO else theme
215
+
216
+ if theme == Theme.LIGHT:
217
+ return self.lightStyleSheet()
218
+
219
+ return self.darkStyleSheet()
220
+
221
+
222
+ class CustomStyleSheetWatcher(QObject):
223
+ """ Custom style sheet watcher """
224
+
225
+ def eventFilter(self, obj: QWidget, e: QEvent):
226
+ if e.type() != QEvent.DynamicPropertyChange:
227
+ return super().eventFilter(obj, e)
228
+
229
+ name = QDynamicPropertyChangeEvent(e).propertyName().data().decode()
230
+ if name in [CustomStyleSheet.LIGHT_QSS_KEY, CustomStyleSheet.DARK_QSS_KEY]:
231
+ addStyleSheet(obj, CustomStyleSheet(obj))
232
+
233
+ return super().eventFilter(obj, e)
234
+
235
+
236
+ class DirtyStyleSheetWatcher(QObject):
237
+ """ Dirty style sheet watcher """
238
+
239
+ def eventFilter(self, obj: QWidget, e: QEvent):
240
+ if e.type() != QEvent.Type.Paint or not obj.property('dirty-qss'):
241
+ return super().eventFilter(obj, e)
242
+
243
+ obj.setProperty('dirty-qss', False)
244
+ if obj in styleSheetManager.widgets:
245
+ obj.setStyleSheet(getStyleSheet(styleSheetManager.source(obj)))
246
+
247
+ return super().eventFilter(obj, e)
248
+
249
+
250
+ class StyleSheetCompose(StyleSheetBase):
251
+ """ Style sheet compose """
252
+
253
+ def __init__(self, sources: List[StyleSheetBase]):
254
+ super().__init__()
255
+ self.sources = sources
256
+
257
+ def content(self, theme=Theme.AUTO):
258
+ return '\n'.join([i.content(theme) for i in self.sources])
259
+
260
+ def add(self, source: StyleSheetBase):
261
+ """ add style sheet source """
262
+ if source is self or source in self.sources:
263
+ return
264
+
265
+ self.sources.append(source)
266
+
267
+ def remove(self, source: StyleSheetBase):
268
+ """ remove style sheet source """
269
+ if source not in self.sources:
270
+ return
271
+
272
+ self.sources.remove(source)
273
+
274
+
275
+ def getStyleSheetFromFile(file: Union[str, QFile]):
276
+ """ get style sheet from qss file """
277
+ f = QFile(file)
278
+ f.open(QFile.ReadOnly)
279
+ qss = str(f.readAll(), encoding='utf-8')
280
+ f.close()
281
+ return qss
282
+
283
+
284
+ def getStyleSheet(source: Union[str, StyleSheetBase], theme=Theme.AUTO):
285
+ """ get style sheet
286
+
287
+ Parameters
288
+ ----------
289
+ source: str | StyleSheetBase
290
+ qss source, it could be:
291
+ * `str`: qss file path
292
+ * `StyleSheetBase`: style sheet instance
293
+
294
+ theme: Theme
295
+ the theme of style sheet
296
+ """
297
+ if isinstance(source, str):
298
+ source = StyleSheetFile(source)
299
+
300
+ return applyThemeColor(source.content(theme))
301
+
302
+
303
+ def setStyleSheet(widget: QWidget, source: Union[str, StyleSheetBase], theme=Theme.AUTO, register=True):
304
+ """ set the style sheet of widget
305
+
306
+ Parameters
307
+ ----------
308
+ widget: QWidget
309
+ the widget to set style sheet
310
+
311
+ source: str | StyleSheetBase
312
+ qss source, it could be:
313
+ * `str`: qss file path
314
+ * `StyleSheetBase`: style sheet instance
315
+
316
+ theme: Theme
317
+ the theme of style sheet
318
+
319
+ register: bool
320
+ whether to register the widget to the style manager. If `register=True`, the style of
321
+ the widget will be updated automatically when the theme changes
322
+ """
323
+ if register:
324
+ styleSheetManager.register(source, widget)
325
+
326
+ widget.setStyleSheet(getStyleSheet(source, theme))
327
+
328
+
329
+ def setCustomStyleSheet(widget: QWidget, lightQss: str, darkQss: str):
330
+ """ set custom style sheet
331
+
332
+ Parameters
333
+ ----------
334
+ widget: QWidget
335
+ the widget to add style sheet
336
+
337
+ lightQss: str
338
+ style sheet used in light theme mode
339
+
340
+ darkQss: str
341
+ style sheet used in light theme mode
342
+ """
343
+ CustomStyleSheet(widget).setCustomStyleSheet(lightQss, darkQss)
344
+
345
+
346
+ def addStyleSheet(widget: QWidget, source: Union[str, StyleSheetBase], theme=Theme.AUTO, register=True):
347
+ """ add style sheet to widget
348
+
349
+ Parameters
350
+ ----------
351
+ widget: QWidget
352
+ the widget to set style sheet
353
+
354
+ source: str | StyleSheetBase
355
+ qss source, it could be:
356
+ * `str`: qss file path
357
+ * `StyleSheetBase`: style sheet instance
358
+
359
+ theme: Theme
360
+ the theme of style sheet
361
+
362
+ register: bool
363
+ whether to register the widget to the style manager. If `register=True`, the style of
364
+ the widget will be updated automatically when the theme changes
365
+ """
366
+ if register:
367
+ styleSheetManager.register(source, widget, reset=False)
368
+ qss = getStyleSheet(styleSheetManager.source(widget), theme)
369
+ else:
370
+ qss = widget.styleSheet() + '\n' + getStyleSheet(source, theme)
371
+
372
+ if qss.rstrip() != widget.styleSheet().rstrip():
373
+ widget.setStyleSheet(qss)
374
+
375
+
376
+ def updateStyleSheet(lazy=False):
377
+ """ update the style sheet of all fluent widgets
378
+
379
+ Parameters
380
+ ----------
381
+ lazy: bool
382
+ whether to update the style sheet lazily, set to `True` will accelerate theme switching
383
+ """
384
+ removes = []
385
+ for widget, file in styleSheetManager.items():
386
+ try:
387
+ if not (lazy and widget.visibleRegion().isNull()):
388
+ setStyleSheet(widget, file, qconfig.theme)
389
+ else:
390
+ styleSheetManager.register(file, widget)
391
+ widget.setProperty('dirty-qss', True)
392
+ except RuntimeError:
393
+ removes.append(widget)
394
+
395
+ for widget in removes:
396
+ styleSheetManager.deregister(widget)
397
+
398
+
399
+ def setTheme(theme: Theme, save=False, lazy=False):
400
+ """ set the theme of application
401
+
402
+ Parameters
403
+ ----------
404
+ theme: Theme
405
+ theme mode
406
+
407
+ save: bool
408
+ whether to save the change to config file
409
+
410
+ lazy: bool
411
+ whether to update the style sheet lazily, set to `True` will accelerate theme switching
412
+ """
413
+ qconfig.set(qconfig.themeMode, theme, save)
414
+ updateStyleSheet(lazy)
415
+ qconfig.themeChangedFinished.emit()
416
+
417
+
418
+ def toggleTheme(save=False, lazy=False):
419
+ """ toggle the theme of application
420
+
421
+ Parameters
422
+ ----------
423
+ save: bool
424
+ whether to save the change to config file
425
+
426
+ lazy: bool
427
+ whether to update the style sheet lazily, set to `True` will accelerate theme switching
428
+ """
429
+ theme = Theme.LIGHT if isDarkTheme() else Theme.DARK
430
+ setTheme(theme, save, lazy)
431
+
432
+
433
+ class ThemeColor(Enum):
434
+ """ Theme color type """
435
+
436
+ PRIMARY = "ThemeColorPrimary"
437
+ DARK_1 = "ThemeColorDark1"
438
+ DARK_2 = "ThemeColorDark2"
439
+ DARK_3 = "ThemeColorDark3"
440
+ LIGHT_1 = "ThemeColorLight1"
441
+ LIGHT_2 = "ThemeColorLight2"
442
+ LIGHT_3 = "ThemeColorLight3"
443
+
444
+ def name(self):
445
+ return self.color().name()
446
+
447
+ def color(self):
448
+ color = qconfig.get(qconfig._cfg.themeColor) # type:QColor
449
+
450
+ # transform color into hsv space
451
+ h, s, v, _ = color.getHsvF()
452
+
453
+ if isDarkTheme():
454
+ s *= 0.84
455
+ v = 1
456
+ if self == self.DARK_1:
457
+ v *= 0.9
458
+ elif self == self.DARK_2:
459
+ s *= 0.977
460
+ v *= 0.82
461
+ elif self == self.DARK_3:
462
+ s *= 0.95
463
+ v *= 0.7
464
+ elif self == self.LIGHT_1:
465
+ s *= 0.92
466
+ elif self == self.LIGHT_2:
467
+ s *= 0.78
468
+ elif self == self.LIGHT_3:
469
+ s *= 0.65
470
+ else:
471
+ if self == self.DARK_1:
472
+ v *= 0.75
473
+ elif self == self.DARK_2:
474
+ s *= 1.05
475
+ v *= 0.5
476
+ elif self == self.DARK_3:
477
+ s *= 1.1
478
+ v *= 0.4
479
+ elif self == self.LIGHT_1:
480
+ v *= 1.05
481
+ elif self == self.LIGHT_2:
482
+ s *= 0.75
483
+ v *= 1.05
484
+ elif self == self.LIGHT_3:
485
+ s *= 0.65
486
+ v *= 1.05
487
+
488
+ return QColor.fromHsvF(h, min(s, 1), min(v, 1))
489
+
490
+
491
+ def themeColor():
492
+ """ get theme color """
493
+ return ThemeColor.PRIMARY.color()
494
+
495
+
496
+ def setThemeColor(color, save=False, lazy=False):
497
+ """ set theme color
498
+
499
+ Parameters
500
+ ----------
501
+ color: QColor | Qt.GlobalColor | str
502
+ theme color
503
+
504
+ save: bool
505
+ whether to save to change to config file
506
+
507
+ lazy: bool
508
+ whether to update the style sheet lazily
509
+ """
510
+ color = QColor(color)
511
+ qconfig.set(qconfig.themeColor, color, save=save)
512
+ updateStyleSheet(lazy)
@@ -0,0 +1,27 @@
1
+ # coding:utf-8
2
+ from PySide6.QtCore import QThread, Signal
3
+
4
+ from .config import Theme, qconfig
5
+ import darkdetect
6
+
7
+
8
+ class SystemThemeListener(QThread):
9
+ """ System theme listener """
10
+
11
+ systemThemeChanged = Signal()
12
+
13
+ def __init__(self, parent=None):
14
+ super().__init__(parent=parent)
15
+
16
+ def run(self):
17
+ darkdetect.listener(self._onThemeChanged)
18
+
19
+ def _onThemeChanged(self, theme: str):
20
+ theme = Theme.DARK if theme.lower() == "dark" else Theme.LIGHT
21
+
22
+ if qconfig.themeMode.value != Theme.AUTO or theme == qconfig.theme:
23
+ return
24
+
25
+ qconfig.theme = Theme.AUTO
26
+ qconfig._cfg.themeChanged.emit(Theme.AUTO)
27
+ self.systemThemeChanged.emit()
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+ from PySide6.QtCore import QTranslator, QLocale
3
+
4
+
5
+ class FluentTranslator(QTranslator):
6
+ """ Translator of fluent widgets """
7
+
8
+ def __init__(self, locale: QLocale = None, parent=None):
9
+ super().__init__(parent=parent)
10
+ self.load(locale or QLocale())
11
+
12
+ def load(self, locale: QLocale):
13
+ """ load translation file """
14
+ super().load(f":/fastuiwidgets/i18n/fastuiwidgets.{locale.name()}.qm")
@@ -0,0 +1,6 @@
1
+ from .dialog_box import *
2
+ from .layout import *
3
+ from .settings import *
4
+ from .widgets import *
5
+ from .navigation import *
6
+ from .date_time import *
@@ -0,0 +1,4 @@
1
+ from .calendar_picker import CalendarPicker, FastCalendarPicker
2
+ from .date_picker import DatePickerBase, DatePicker, ZhDatePicker
3
+ from .picker_base import PickerBase, PickerPanel, PickerColumnFormatter
4
+ from .time_picker import TimePicker, AMTimePicker
@@ -0,0 +1,121 @@
1
+ # coding:utf-8
2
+ from typing import Union
3
+
4
+ from PySide6.QtCore import Qt, Signal, QRectF, QDate, QPoint, Property
5
+ from PySide6.QtGui import QPainter
6
+ from PySide6.QtWidgets import QWidget, QPushButton, QApplication
7
+
8
+ from ...common.style_sheet import FluentStyleSheet
9
+ from ...common.icon import FluentIcon as FIF
10
+ from ..widgets.flyout import Flyout, FlyoutAnimationType
11
+ from .calendar_view import CalendarView
12
+ from .fast_calendar_view import FastCalendarView
13
+
14
+
15
+ class CalendarPicker(QPushButton):
16
+ """ Calendar picker """
17
+
18
+ dateChanged = Signal(QDate)
19
+
20
+ def __init__(self, parent=None):
21
+ super().__init__(parent=parent)
22
+ self._date = QDate()
23
+ self._dateFormat = Qt.DateFormat.ISODate
24
+ self._isResetEnabled = False
25
+
26
+ self.setText(self.tr('Pick a date'))
27
+ FluentStyleSheet.CALENDAR_PICKER.apply(self)
28
+
29
+ self.clicked.connect(self._showCalendarView)
30
+
31
+ def getDate(self):
32
+ return self._date
33
+
34
+ def setDate(self, date: QDate):
35
+ """ set the selected date """
36
+ self._onDateChanged(date)
37
+
38
+ def reset(self):
39
+ """ reset date """
40
+ self._date = QDate()
41
+ self.setText(self.tr('Pick a date'))
42
+ self.setProperty('hasDate', False)
43
+ self.setStyle(QApplication.style())
44
+ self.update()
45
+
46
+ def getDateFormat(self):
47
+ return self._dateFormat
48
+
49
+ def setDateFormat(self, format: Union[Qt.DateFormat, str]):
50
+ self._dateFormat = format
51
+ if self.date.isValid():
52
+ self.setText(self.date.toString(self.dateFormat))
53
+
54
+ def isRestEnabled(self):
55
+ return self._isResetEnabled
56
+
57
+ def setResetEnabled(self, isEnabled: bool):
58
+ """ set the visibility of reset button """
59
+ self._isResetEnabled = isEnabled
60
+
61
+ def _showCalendarView(self):
62
+ view = CalendarView(self.window())
63
+ view.setResetEnabled(self.isRestEnabled())
64
+
65
+ view.resetted.connect(self.reset)
66
+ view.dateChanged.connect(self._onDateChanged)
67
+
68
+ if self.date.isValid():
69
+ view.setDate(self.date)
70
+
71
+ x = int(self.width()/2 - view.sizeHint().width()/2)
72
+ y = self.height()
73
+ view.exec(self.mapToGlobal(QPoint(x, y)))
74
+
75
+ def _onDateChanged(self, date: QDate):
76
+ self._date = QDate(date)
77
+ self.setText(date.toString(self.dateFormat))
78
+ self.setProperty('hasDate', True)
79
+ self.setStyle(QApplication.style())
80
+ self.update()
81
+
82
+ self.dateChanged.emit(date)
83
+
84
+ def paintEvent(self, e):
85
+ super().paintEvent(e)
86
+ painter = QPainter(self)
87
+ painter.setRenderHints(QPainter.Antialiasing)
88
+
89
+ if not self.property('hasDate'):
90
+ painter.setOpacity(0.6)
91
+
92
+ w = 12
93
+ rect = QRectF(self.width() - 23, self.height()/2 - w/2, w, w)
94
+ FIF.CALENDAR.render(painter, rect)
95
+
96
+ date = Property(QDate, getDate, setDate)
97
+ dateFormat = Property(Qt.DateFormat, getDateFormat, setDateFormat)
98
+
99
+
100
+ class FastCalendarPicker(CalendarPicker):
101
+ """ Pro calendar picker """
102
+
103
+ def __init__(self, parent=None):
104
+ super().__init__(parent=parent)
105
+ self.flyoutAnimationType = FlyoutAnimationType.DROP_DOWN
106
+
107
+ def setFlyoutAnimationType(self, aniType: FlyoutAnimationType):
108
+ self.flyoutAnimationType = aniType
109
+
110
+ def _showCalendarView(self):
111
+ view = FastCalendarView(self.window())
112
+ view.setResetEnabled(self.isRestEnabled())
113
+
114
+ view.resetted.connect(self.reset)
115
+ view.dateChanged.connect(self._onDateChanged)
116
+
117
+ if self.date.isValid():
118
+ view.setDate(self.date)
119
+
120
+ flyout = Flyout.make(view, self, self.window(), self.flyoutAnimationType)
121
+ view.dateChanged.connect(flyout.close)