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,95 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
from enum import Enum
|
3
|
+
|
4
|
+
from PySide6.QtGui import QColor
|
5
|
+
|
6
|
+
from .style_sheet import themeColor, Theme, isDarkTheme
|
7
|
+
from .config import isDarkThemeMode
|
8
|
+
|
9
|
+
|
10
|
+
class FluentThemeColor(Enum):
|
11
|
+
""" Fluent theme color
|
12
|
+
|
13
|
+
Refer to: https://www.figma.com/file/iM7EPX8Jn37zjeSezb43cF
|
14
|
+
"""
|
15
|
+
YELLOW_GOLD = "#FFB900"
|
16
|
+
GOLD = "#FF8C00"
|
17
|
+
ORANGE_BRIGHT = "#F7630C"
|
18
|
+
ORANGE_DARK = "#CA5010"
|
19
|
+
RUST = "#DA3B01"
|
20
|
+
PALE_RUST = "#EF6950"
|
21
|
+
BRICK_RED = "#D13438"
|
22
|
+
MOD_RED = "#FF4343"
|
23
|
+
PALE_RED = "#E74856"
|
24
|
+
RED = "#E81123"
|
25
|
+
ROSE_BRIGHT = "#EA005E"
|
26
|
+
ROSE = "#C30052"
|
27
|
+
PLUM_LIGHT = "#E3008C"
|
28
|
+
PLUM = "#BF0077"
|
29
|
+
ORCHID_LIGHT = "#BF0077"
|
30
|
+
ORCHID = "#9A0089"
|
31
|
+
DEFAULT_BLUE = "#0078D7"
|
32
|
+
NAVY_BLUE = "#0063B1"
|
33
|
+
PURPLE_SHADOW = "#8E8CD8"
|
34
|
+
PURPLE_SHADOW_DARK = "#6B69D6"
|
35
|
+
IRIS_PASTEL = "#8764B8"
|
36
|
+
IRIS_SPRING = "#744DA9"
|
37
|
+
VIOLET_RED_LIGHT = "#B146C2"
|
38
|
+
VIOLET_RED = "#881798"
|
39
|
+
COOL_BLUE_BRIGHT = "#0099BC"
|
40
|
+
COOL_BLUR = "#2D7D9A"
|
41
|
+
SEAFOAM = "#00B7C3"
|
42
|
+
SEAFOAM_TEAL = "#038387"
|
43
|
+
MINT_LIGHT = "#00B294"
|
44
|
+
MINT_DARK = "#018574"
|
45
|
+
TURF_GREEN = "#00CC6A"
|
46
|
+
SPORT_GREEN = "#10893E"
|
47
|
+
GRAY = "#7A7574"
|
48
|
+
GRAY_BROWN = "#5D5A58"
|
49
|
+
STEAL_BLUE = "#68768A"
|
50
|
+
METAL_BLUE = "#515C6B"
|
51
|
+
PALE_MOSS = "#567C73"
|
52
|
+
MOSS = "#486860"
|
53
|
+
MEADOW_GREEN = "#498205"
|
54
|
+
GREEN = "#107C10"
|
55
|
+
OVERCAST = "#767676"
|
56
|
+
STORM = "#4C4A48"
|
57
|
+
BLUE_GRAY = "#69797E"
|
58
|
+
GRAY_DARK = "#4A5459"
|
59
|
+
LIDDY_GREEN = "#647C64"
|
60
|
+
SAGE = "#525E54"
|
61
|
+
CAMOUFLAGE_DESERT = "#847545"
|
62
|
+
CAMOUFLAGE = "#7E735F"
|
63
|
+
|
64
|
+
def color(self):
|
65
|
+
return QColor(self.value)
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
class FluentSystemColor(Enum):
|
70
|
+
|
71
|
+
SUCCESS_FOREGROUND = ("#0f7b0f", "#6ccb5f")
|
72
|
+
CAUTION_FOREGROUND = ("#9d5d00", "#fce100")
|
73
|
+
CRITICAL_FOREGROUND = ("#c42b1c", "#ff99a4")
|
74
|
+
|
75
|
+
SUCCESS_BACKGROUND = ("#dff6dd", "#393d1b")
|
76
|
+
CAUTION_BACKGROUND = ("#fff4ce", "#433519")
|
77
|
+
CRITICAL_BACKGROUND = ("#fde7e9", "#442726")
|
78
|
+
|
79
|
+
def color(self, theme=Theme.AUTO) -> QColor:
|
80
|
+
color = self.value[1] if isDarkThemeMode(theme) else self.value[0]
|
81
|
+
return QColor(color)
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
def validColor(color: QColor, default: QColor) -> QColor:
|
86
|
+
return color if color.isValid() else default
|
87
|
+
|
88
|
+
|
89
|
+
def fallbackThemeColor(color: QColor):
|
90
|
+
return color if color.isValid() else themeColor()
|
91
|
+
|
92
|
+
|
93
|
+
def autoFallbackThemeColor(light: QColor, dark: QColor):
|
94
|
+
color = dark if isDarkTheme() else light
|
95
|
+
return fallbackThemeColor(color)
|
@@ -0,0 +1,423 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
import json
|
3
|
+
from copy import deepcopy
|
4
|
+
from enum import Enum
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import List
|
7
|
+
|
8
|
+
import darkdetect
|
9
|
+
from PySide6.QtCore import QObject, Signal
|
10
|
+
from PySide6.QtGui import QColor
|
11
|
+
|
12
|
+
from .exception_handler import exceptionHandler
|
13
|
+
|
14
|
+
|
15
|
+
ALERT = "\n\033[1;33m📢 Tips:\033[0m QFluentWidgets Pro is now released. Click \033[1;96mhttps://fastuiwidgets.com/pages/pro\033[0m to learn more about it.\n"
|
16
|
+
|
17
|
+
|
18
|
+
class Theme(Enum):
|
19
|
+
""" Theme enumeration """
|
20
|
+
|
21
|
+
LIGHT = "Light"
|
22
|
+
DARK = "Dark"
|
23
|
+
AUTO = "Auto"
|
24
|
+
|
25
|
+
|
26
|
+
class ConfigValidator:
|
27
|
+
""" Config validator """
|
28
|
+
|
29
|
+
def validate(self, value):
|
30
|
+
""" Verify whether the value is legal """
|
31
|
+
return True
|
32
|
+
|
33
|
+
def correct(self, value):
|
34
|
+
""" correct illegal value """
|
35
|
+
return value
|
36
|
+
|
37
|
+
|
38
|
+
class RangeValidator(ConfigValidator):
|
39
|
+
""" Range validator """
|
40
|
+
|
41
|
+
def __init__(self, min, max):
|
42
|
+
self.min = min
|
43
|
+
self.max = max
|
44
|
+
self.range = (min, max)
|
45
|
+
|
46
|
+
def validate(self, value):
|
47
|
+
return self.min <= value <= self.max
|
48
|
+
|
49
|
+
def correct(self, value):
|
50
|
+
return min(max(self.min, value), self.max)
|
51
|
+
|
52
|
+
|
53
|
+
class OptionsValidator(ConfigValidator):
|
54
|
+
""" Options validator """
|
55
|
+
|
56
|
+
def __init__(self, options):
|
57
|
+
if not options:
|
58
|
+
raise ValueError("The `options` can't be empty.")
|
59
|
+
|
60
|
+
if isinstance(options, Enum):
|
61
|
+
options = options._member_map_.values()
|
62
|
+
|
63
|
+
self.options = list(options)
|
64
|
+
|
65
|
+
def validate(self, value):
|
66
|
+
return value in self.options
|
67
|
+
|
68
|
+
def correct(self, value):
|
69
|
+
return value if self.validate(value) else self.options[0]
|
70
|
+
|
71
|
+
|
72
|
+
class BoolValidator(OptionsValidator):
|
73
|
+
""" Boolean validator """
|
74
|
+
|
75
|
+
def __init__(self):
|
76
|
+
super().__init__([True, False])
|
77
|
+
|
78
|
+
|
79
|
+
class FolderValidator(ConfigValidator):
|
80
|
+
""" Folder validator """
|
81
|
+
|
82
|
+
def validate(self, value):
|
83
|
+
return Path(value).exists()
|
84
|
+
|
85
|
+
def correct(self, value):
|
86
|
+
path = Path(value)
|
87
|
+
path.mkdir(exist_ok=True, parents=True)
|
88
|
+
return str(path.absolute()).replace("\\", "/")
|
89
|
+
|
90
|
+
|
91
|
+
class FolderListValidator(ConfigValidator):
|
92
|
+
""" Folder list validator """
|
93
|
+
|
94
|
+
def validate(self, value):
|
95
|
+
return all(Path(i).exists() for i in value)
|
96
|
+
|
97
|
+
def correct(self, value: List[str]):
|
98
|
+
folders = []
|
99
|
+
for folder in value:
|
100
|
+
path = Path(folder)
|
101
|
+
if path.exists():
|
102
|
+
folders.append(str(path.absolute()).replace("\\", "/"))
|
103
|
+
|
104
|
+
return folders
|
105
|
+
|
106
|
+
|
107
|
+
class ColorValidator(ConfigValidator):
|
108
|
+
""" RGB color validator """
|
109
|
+
|
110
|
+
def __init__(self, default):
|
111
|
+
self.default = QColor(default)
|
112
|
+
|
113
|
+
def validate(self, color):
|
114
|
+
try:
|
115
|
+
return QColor(color).isValid()
|
116
|
+
except:
|
117
|
+
return False
|
118
|
+
|
119
|
+
def correct(self, value):
|
120
|
+
return QColor(value) if self.validate(value) else self.default
|
121
|
+
|
122
|
+
|
123
|
+
class ConfigSerializer:
|
124
|
+
""" Config serializer """
|
125
|
+
|
126
|
+
def serialize(self, value):
|
127
|
+
""" serialize config value """
|
128
|
+
return value
|
129
|
+
|
130
|
+
def deserialize(self, value):
|
131
|
+
""" deserialize config from config file's value """
|
132
|
+
return value
|
133
|
+
|
134
|
+
|
135
|
+
class EnumSerializer(ConfigSerializer):
|
136
|
+
""" enumeration class serializer """
|
137
|
+
|
138
|
+
def __init__(self, enumClass):
|
139
|
+
self.enumClass = enumClass
|
140
|
+
|
141
|
+
def serialize(self, value):
|
142
|
+
return value.value
|
143
|
+
|
144
|
+
def deserialize(self, value):
|
145
|
+
return self.enumClass(value)
|
146
|
+
|
147
|
+
|
148
|
+
class ColorSerializer(ConfigSerializer):
|
149
|
+
""" QColor serializer """
|
150
|
+
|
151
|
+
def serialize(self, value: QColor):
|
152
|
+
return value.name(QColor.HexArgb)
|
153
|
+
|
154
|
+
def deserialize(self, value):
|
155
|
+
if isinstance(value, list):
|
156
|
+
return QColor(*value)
|
157
|
+
|
158
|
+
return QColor(value)
|
159
|
+
|
160
|
+
|
161
|
+
class ConfigItem(QObject):
|
162
|
+
""" Config item """
|
163
|
+
|
164
|
+
valueChanged = Signal(object)
|
165
|
+
|
166
|
+
def __init__(self, group, name, default, validator=None, serializer=None, restart=False):
|
167
|
+
"""
|
168
|
+
Parameters
|
169
|
+
----------
|
170
|
+
group: str
|
171
|
+
config group name
|
172
|
+
|
173
|
+
name: str
|
174
|
+
config item name, can be empty
|
175
|
+
|
176
|
+
default:
|
177
|
+
default value
|
178
|
+
|
179
|
+
options: list
|
180
|
+
options value
|
181
|
+
|
182
|
+
serializer: ConfigSerializer
|
183
|
+
config serializer
|
184
|
+
|
185
|
+
restart: bool
|
186
|
+
whether to restart the application after updating value
|
187
|
+
"""
|
188
|
+
super().__init__()
|
189
|
+
self.group = group
|
190
|
+
self.name = name
|
191
|
+
self.validator = validator or ConfigValidator()
|
192
|
+
self.serializer = serializer or ConfigSerializer()
|
193
|
+
self.__value = default
|
194
|
+
self.value = default
|
195
|
+
self.restart = restart
|
196
|
+
self.defaultValue = self.validator.correct(default)
|
197
|
+
|
198
|
+
@property
|
199
|
+
def value(self):
|
200
|
+
""" get the value of config item """
|
201
|
+
return self.__value
|
202
|
+
|
203
|
+
@value.setter
|
204
|
+
def value(self, v):
|
205
|
+
v = self.validator.correct(v)
|
206
|
+
ov = self.__value
|
207
|
+
self.__value = v
|
208
|
+
if ov != v:
|
209
|
+
self.valueChanged.emit(v)
|
210
|
+
|
211
|
+
@property
|
212
|
+
def key(self):
|
213
|
+
""" get the config key separated by `.` """
|
214
|
+
return self.group+"."+self.name if self.name else self.group
|
215
|
+
|
216
|
+
def __str__(self):
|
217
|
+
return f'{self.__class__.__name__}[value={self.value}]'
|
218
|
+
|
219
|
+
def serialize(self):
|
220
|
+
return self.serializer.serialize(self.value)
|
221
|
+
|
222
|
+
def deserializeFrom(self, value):
|
223
|
+
self.value = self.serializer.deserialize(value)
|
224
|
+
|
225
|
+
|
226
|
+
class RangeConfigItem(ConfigItem):
|
227
|
+
""" Config item of range """
|
228
|
+
|
229
|
+
@property
|
230
|
+
def range(self):
|
231
|
+
""" get the available range of config """
|
232
|
+
return self.validator.range
|
233
|
+
|
234
|
+
def __str__(self):
|
235
|
+
return f'{self.__class__.__name__}[range={self.range}, value={self.value}]'
|
236
|
+
|
237
|
+
|
238
|
+
class OptionsConfigItem(ConfigItem):
|
239
|
+
""" Config item with options """
|
240
|
+
|
241
|
+
@property
|
242
|
+
def options(self):
|
243
|
+
return self.validator.options
|
244
|
+
|
245
|
+
def __str__(self):
|
246
|
+
return f'{self.__class__.__name__}[options={self.options}, value={self.value}]'
|
247
|
+
|
248
|
+
|
249
|
+
class ColorConfigItem(ConfigItem):
|
250
|
+
""" Color config item """
|
251
|
+
|
252
|
+
def __init__(self, group, name, default, restart=False):
|
253
|
+
super().__init__(group, name, QColor(default), ColorValidator(default),
|
254
|
+
ColorSerializer(), restart)
|
255
|
+
|
256
|
+
def __str__(self):
|
257
|
+
return f'{self.__class__.__name__}[value={self.value.name()}]'
|
258
|
+
|
259
|
+
|
260
|
+
class QConfig(QObject):
|
261
|
+
""" Config of app """
|
262
|
+
|
263
|
+
appRestartSig = Signal()
|
264
|
+
themeChanged = Signal(Theme)
|
265
|
+
themeChangedFinished = Signal()
|
266
|
+
themeColorChanged = Signal(QColor)
|
267
|
+
|
268
|
+
themeMode = OptionsConfigItem(
|
269
|
+
"QFluentWidgets", "ThemeMode", Theme.LIGHT, OptionsValidator(Theme), EnumSerializer(Theme))
|
270
|
+
themeColor = ColorConfigItem("QFluentWidgets", "ThemeColor", '#009faa')
|
271
|
+
|
272
|
+
def __init__(self):
|
273
|
+
super().__init__()
|
274
|
+
self.file = Path("config/config.json")
|
275
|
+
self._theme = Theme.LIGHT
|
276
|
+
self._cfg = self
|
277
|
+
|
278
|
+
def get(self, item):
|
279
|
+
""" get the value of config item """
|
280
|
+
return item.value
|
281
|
+
|
282
|
+
def set(self, item, value, save=True, copy=True):
|
283
|
+
""" set the value of config item
|
284
|
+
|
285
|
+
Parameters
|
286
|
+
----------
|
287
|
+
item: ConfigItem
|
288
|
+
config item
|
289
|
+
|
290
|
+
value:
|
291
|
+
the new value of config item
|
292
|
+
|
293
|
+
save: bool
|
294
|
+
whether to save the change to config file
|
295
|
+
|
296
|
+
copy: bool
|
297
|
+
whether to deep copy the new value
|
298
|
+
"""
|
299
|
+
if item.value == value:
|
300
|
+
return
|
301
|
+
|
302
|
+
# deepcopy new value
|
303
|
+
try:
|
304
|
+
item.value = deepcopy(value) if copy else value
|
305
|
+
except:
|
306
|
+
item.value = value
|
307
|
+
|
308
|
+
if save:
|
309
|
+
self.save()
|
310
|
+
|
311
|
+
if item.restart:
|
312
|
+
self._cfg.appRestartSig.emit()
|
313
|
+
|
314
|
+
if item is self._cfg.themeMode:
|
315
|
+
self.theme = value
|
316
|
+
self._cfg.themeChanged.emit(value)
|
317
|
+
|
318
|
+
if item is self._cfg.themeColor:
|
319
|
+
self._cfg.themeColorChanged.emit(value)
|
320
|
+
|
321
|
+
def toDict(self, serialize=True):
|
322
|
+
""" convert config items to `dict` """
|
323
|
+
items = {}
|
324
|
+
for name in dir(self._cfg.__class__):
|
325
|
+
item = getattr(self._cfg.__class__, name)
|
326
|
+
if not isinstance(item, ConfigItem):
|
327
|
+
continue
|
328
|
+
|
329
|
+
value = item.serialize() if serialize else item.value
|
330
|
+
if not items.get(item.group):
|
331
|
+
if not item.name:
|
332
|
+
items[item.group] = value
|
333
|
+
else:
|
334
|
+
items[item.group] = {}
|
335
|
+
|
336
|
+
if item.name:
|
337
|
+
items[item.group][item.name] = value
|
338
|
+
|
339
|
+
return items
|
340
|
+
|
341
|
+
def save(self):
|
342
|
+
""" save config """
|
343
|
+
self._cfg.file.parent.mkdir(parents=True, exist_ok=True)
|
344
|
+
with open(self._cfg.file, "w", encoding="utf-8") as f:
|
345
|
+
json.dump(self._cfg.toDict(), f, ensure_ascii=False, indent=4)
|
346
|
+
|
347
|
+
@exceptionHandler()
|
348
|
+
def load(self, file=None, config=None):
|
349
|
+
""" load config
|
350
|
+
|
351
|
+
Parameters
|
352
|
+
----------
|
353
|
+
file: str or Path
|
354
|
+
the path of json config file
|
355
|
+
|
356
|
+
config: Config
|
357
|
+
config object to be initialized
|
358
|
+
"""
|
359
|
+
if isinstance(config, QConfig):
|
360
|
+
self._cfg = config
|
361
|
+
self._cfg.themeChanged.connect(self.themeChanged)
|
362
|
+
|
363
|
+
if isinstance(file, (str, Path)):
|
364
|
+
self._cfg.file = Path(file)
|
365
|
+
|
366
|
+
try:
|
367
|
+
with open(self._cfg.file, encoding="utf-8") as f:
|
368
|
+
cfg = json.load(f)
|
369
|
+
except:
|
370
|
+
cfg = {}
|
371
|
+
|
372
|
+
# map config items'key to item
|
373
|
+
items = {}
|
374
|
+
for name in dir(self._cfg.__class__):
|
375
|
+
item = getattr(self._cfg.__class__, name)
|
376
|
+
if isinstance(item, ConfigItem):
|
377
|
+
items[item.key] = item
|
378
|
+
|
379
|
+
# update the value of config item
|
380
|
+
for k, v in cfg.items():
|
381
|
+
if not isinstance(v, dict) and items.get(k) is not None:
|
382
|
+
items[k].deserializeFrom(v)
|
383
|
+
elif isinstance(v, dict):
|
384
|
+
for key, value in v.items():
|
385
|
+
key = k + "." + key
|
386
|
+
if items.get(key) is not None:
|
387
|
+
items[key].deserializeFrom(value)
|
388
|
+
|
389
|
+
self.theme = self.get(self._cfg.themeMode)
|
390
|
+
|
391
|
+
@property
|
392
|
+
def theme(self):
|
393
|
+
""" get theme mode, can be `Theme.Light` or `Theme.Dark` """
|
394
|
+
return self._cfg._theme
|
395
|
+
|
396
|
+
@theme.setter
|
397
|
+
def theme(self, t):
|
398
|
+
""" chaneg the theme without modifying the config file """
|
399
|
+
if t == Theme.AUTO:
|
400
|
+
t = darkdetect.theme()
|
401
|
+
t = Theme(t) if t else Theme.LIGHT
|
402
|
+
|
403
|
+
self._cfg._theme = t
|
404
|
+
|
405
|
+
|
406
|
+
qconfig = QConfig()
|
407
|
+
try:
|
408
|
+
print(ALERT)
|
409
|
+
except UnicodeEncodeError:
|
410
|
+
print(ALERT.replace("📢", ""))
|
411
|
+
|
412
|
+
|
413
|
+
def isDarkTheme():
|
414
|
+
""" whether the theme is dark mode """
|
415
|
+
return qconfig.theme == Theme.DARK
|
416
|
+
|
417
|
+
def theme():
|
418
|
+
""" get theme mode """
|
419
|
+
return qconfig.theme
|
420
|
+
|
421
|
+
def isDarkThemeMode(theme=Theme.AUTO):
|
422
|
+
""" whether the theme is dark mode """
|
423
|
+
return theme == Theme.DARK if theme != Theme.AUTO else isDarkTheme()
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from copy import deepcopy
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
def exceptionHandler(*default):
|
7
|
+
""" decorator for exception handling
|
8
|
+
|
9
|
+
Parameters
|
10
|
+
----------
|
11
|
+
*default:
|
12
|
+
the default value returned when an exception occurs
|
13
|
+
"""
|
14
|
+
|
15
|
+
def outer(func):
|
16
|
+
|
17
|
+
def inner(*args, **kwargs):
|
18
|
+
try:
|
19
|
+
return func(*args, **kwargs)
|
20
|
+
except BaseException as e:
|
21
|
+
value = deepcopy(default)
|
22
|
+
if len(value) == 0:
|
23
|
+
return None
|
24
|
+
elif len(value) == 1:
|
25
|
+
return value[0]
|
26
|
+
|
27
|
+
return value
|
28
|
+
|
29
|
+
return inner
|
30
|
+
|
31
|
+
return outer
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
from PySide6.QtGui import QFont
|
3
|
+
from PySide6.QtWidgets import QWidget
|
4
|
+
|
5
|
+
|
6
|
+
def setFont(widget: QWidget, fontSize=14, weight=QFont.Normal):
|
7
|
+
""" set the font of widget
|
8
|
+
|
9
|
+
Parameters
|
10
|
+
----------
|
11
|
+
widget: QWidget
|
12
|
+
the widget to set font
|
13
|
+
|
14
|
+
fontSize: int
|
15
|
+
font pixel size
|
16
|
+
|
17
|
+
weight: `QFont.Weight`
|
18
|
+
font weight
|
19
|
+
"""
|
20
|
+
widget.setFont(getFont(fontSize, weight))
|
21
|
+
|
22
|
+
|
23
|
+
def getFont(fontSize=14, weight=QFont.Normal):
|
24
|
+
""" create font
|
25
|
+
|
26
|
+
Parameters
|
27
|
+
----------
|
28
|
+
fontSize: int
|
29
|
+
font pixel size
|
30
|
+
|
31
|
+
weight: `QFont.Weight`
|
32
|
+
font weight
|
33
|
+
"""
|
34
|
+
font = QFont()
|
35
|
+
font.setFamilies(['Segoe UI', 'Microsoft YaHei', 'PingFang SC'])
|
36
|
+
font.setPixelSize(fontSize)
|
37
|
+
font.setWeight(weight)
|
38
|
+
return font
|