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.
- fastuiwidgets/__init__.py +12 -0
- fastuiwidgets/_rc/__init__.py +0 -0
- fastuiwidgets/_rc/resource.py +98835 -0
- fastuiwidgets/common/__init__.py +12 -0
- fastuiwidgets/common/animation.py +530 -0
- fastuiwidgets/common/auto_wrap.py +164 -0
- fastuiwidgets/common/color.py +95 -0
- fastuiwidgets/common/config.py +423 -0
- fastuiwidgets/common/exception_handler.py +31 -0
- fastuiwidgets/common/font.py +38 -0
- fastuiwidgets/common/icon.py +703 -0
- fastuiwidgets/common/image_utils.py +198 -0
- fastuiwidgets/common/overload.py +47 -0
- fastuiwidgets/common/router.py +133 -0
- fastuiwidgets/common/screen.py +25 -0
- fastuiwidgets/common/smooth_scroll.py +141 -0
- fastuiwidgets/common/style_sheet.py +512 -0
- fastuiwidgets/common/theme_listener.py +27 -0
- fastuiwidgets/common/translator.py +14 -0
- fastuiwidgets/components/__init__.py +6 -0
- fastuiwidgets/components/date_time/__init__.py +4 -0
- fastuiwidgets/components/date_time/calendar_picker.py +121 -0
- fastuiwidgets/components/date_time/calendar_view.py +671 -0
- fastuiwidgets/components/date_time/date_picker.py +245 -0
- fastuiwidgets/components/date_time/fast_calendar_view.py +487 -0
- fastuiwidgets/components/date_time/picker_base.py +632 -0
- fastuiwidgets/components/date_time/time_picker.py +223 -0
- fastuiwidgets/components/dialog_box/__init__.py +6 -0
- fastuiwidgets/components/dialog_box/color_dialog.py +414 -0
- fastuiwidgets/components/dialog_box/dialog.py +167 -0
- fastuiwidgets/components/dialog_box/folder_list_dialog.py +307 -0
- fastuiwidgets/components/dialog_box/mask_dialog_base.py +120 -0
- fastuiwidgets/components/dialog_box/message_box_base.py +92 -0
- fastuiwidgets/components/dialog_box/message_dialog.py +65 -0
- fastuiwidgets/components/layout/__init__.py +3 -0
- fastuiwidgets/components/layout/expand_layout.py +96 -0
- fastuiwidgets/components/layout/flow_layout.py +236 -0
- fastuiwidgets/components/layout/v_box_layout.py +41 -0
- fastuiwidgets/components/material/__init__.py +6 -0
- fastuiwidgets/components/material/acrylic_combo_box.py +96 -0
- fastuiwidgets/components/material/acrylic_flyout.py +105 -0
- fastuiwidgets/components/material/acrylic_line_edit.py +27 -0
- fastuiwidgets/components/material/acrylic_menu.py +204 -0
- fastuiwidgets/components/material/acrylic_tool_tip.py +39 -0
- fastuiwidgets/components/material/acrylic_widget.py +42 -0
- fastuiwidgets/components/navigation/__init__.py +9 -0
- fastuiwidgets/components/navigation/breadcrumb.py +350 -0
- fastuiwidgets/components/navigation/navigation_bar.py +416 -0
- fastuiwidgets/components/navigation/navigation_interface.py +268 -0
- fastuiwidgets/components/navigation/navigation_panel.py +657 -0
- fastuiwidgets/components/navigation/navigation_widget.py +686 -0
- fastuiwidgets/components/navigation/pivot.py +272 -0
- fastuiwidgets/components/navigation/segmented_widget.py +174 -0
- fastuiwidgets/components/settings/__init__.py +8 -0
- fastuiwidgets/components/settings/custom_color_setting_card.py +139 -0
- fastuiwidgets/components/settings/expand_setting_card.py +390 -0
- fastuiwidgets/components/settings/folder_list_setting_card.py +134 -0
- fastuiwidgets/components/settings/options_setting_card.py +86 -0
- fastuiwidgets/components/settings/setting_card.py +449 -0
- fastuiwidgets/components/settings/setting_card_group.py +48 -0
- fastuiwidgets/components/widgets/__init__.py +41 -0
- fastuiwidgets/components/widgets/acrylic_label.py +261 -0
- fastuiwidgets/components/widgets/button.py +1059 -0
- fastuiwidgets/components/widgets/card_widget.py +369 -0
- fastuiwidgets/components/widgets/check_box.py +203 -0
- fastuiwidgets/components/widgets/combo_box.py +556 -0
- fastuiwidgets/components/widgets/command_bar.py +636 -0
- fastuiwidgets/components/widgets/cycle_list_widget.py +251 -0
- fastuiwidgets/components/widgets/flip_view.py +430 -0
- fastuiwidgets/components/widgets/flyout.py +521 -0
- fastuiwidgets/components/widgets/frameless_window.py +49 -0
- fastuiwidgets/components/widgets/icon_widget.py +53 -0
- fastuiwidgets/components/widgets/info_badge.py +483 -0
- fastuiwidgets/components/widgets/info_bar.py +596 -0
- fastuiwidgets/components/widgets/label.py +553 -0
- fastuiwidgets/components/widgets/line_edit.py +551 -0
- fastuiwidgets/components/widgets/list_view.py +158 -0
- fastuiwidgets/components/widgets/menu.py +1318 -0
- fastuiwidgets/components/widgets/pips_pager.py +331 -0
- fastuiwidgets/components/widgets/progress_bar.py +311 -0
- fastuiwidgets/components/widgets/progress_ring.py +212 -0
- fastuiwidgets/components/widgets/scroll_area.py +125 -0
- fastuiwidgets/components/widgets/scroll_bar.py +673 -0
- fastuiwidgets/components/widgets/separator.py +43 -0
- fastuiwidgets/components/widgets/slider.py +307 -0
- fastuiwidgets/components/widgets/spin_box.py +306 -0
- fastuiwidgets/components/widgets/stacked_widget.py +211 -0
- fastuiwidgets/components/widgets/state_tool_tip.py +188 -0
- fastuiwidgets/components/widgets/switch_button.py +312 -0
- fastuiwidgets/components/widgets/tab_view.py +804 -0
- fastuiwidgets/components/widgets/table_view.py +360 -0
- fastuiwidgets/components/widgets/teaching_tip.py +657 -0
- fastuiwidgets/components/widgets/tool_tip.py +460 -0
- fastuiwidgets/components/widgets/tree_view.py +216 -0
- fastuiwidgets/multimedia/__init__.py +3 -0
- fastuiwidgets/multimedia/media_play_bar.py +319 -0
- fastuiwidgets/multimedia/media_player.py +124 -0
- fastuiwidgets/multimedia/video_widget.py +93 -0
- fastuiwidgets/window/__init__.py +2 -0
- fastuiwidgets/window/fluent_window.py +413 -0
- fastuiwidgets/window/splash_screen.py +92 -0
- fastuiwidgets/window/stacked_widget.py +66 -0
- python_fastui_widgets-1.0.0.dist-info/METADATA +30 -0
- python_fastui_widgets-1.0.0.dist-info/RECORD +107 -0
- python_fastui_widgets-1.0.0.dist-info/WHEEL +5 -0
- python_fastui_widgets-1.0.0.dist-info/licenses/LICENSE +674 -0
- 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,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)
|