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,703 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from enum import Enum
|
3
|
+
from typing import Union
|
4
|
+
import json
|
5
|
+
|
6
|
+
from PySide6.QtXml import QDomDocument
|
7
|
+
from PySide6.QtCore import QRectF, Qt, QFile, QObject, QRect
|
8
|
+
from PySide6.QtGui import QIcon, QIconEngine, QColor, QPixmap, QImage, QPainter, QFontDatabase, QFont, QAction, QPainterPath
|
9
|
+
from PySide6.QtSvg import QSvgRenderer
|
10
|
+
from PySide6.QtWidgets import QApplication
|
11
|
+
|
12
|
+
from .config import isDarkTheme, Theme
|
13
|
+
from .overload import singledispatchmethod
|
14
|
+
|
15
|
+
|
16
|
+
class FluentIconEngine(QIconEngine):
|
17
|
+
""" Fluent icon engine """
|
18
|
+
|
19
|
+
def __init__(self, icon, reverse=False):
|
20
|
+
"""
|
21
|
+
Parameters
|
22
|
+
----------
|
23
|
+
icon: QICon | Icon | FluentIconBase
|
24
|
+
the icon to be drawn
|
25
|
+
|
26
|
+
reverse: bool
|
27
|
+
whether to reverse the theme of icon
|
28
|
+
"""
|
29
|
+
super().__init__()
|
30
|
+
self.icon = icon
|
31
|
+
self.isThemeReversed = reverse
|
32
|
+
|
33
|
+
def paint(self, painter, rect, mode, state):
|
34
|
+
painter.save()
|
35
|
+
|
36
|
+
if mode == QIcon.Disabled:
|
37
|
+
painter.setOpacity(0.5)
|
38
|
+
elif mode == QIcon.Selected:
|
39
|
+
painter.setOpacity(0.7)
|
40
|
+
|
41
|
+
# change icon color according to the theme
|
42
|
+
icon = self.icon
|
43
|
+
|
44
|
+
if not self.isThemeReversed:
|
45
|
+
theme = Theme.AUTO
|
46
|
+
else:
|
47
|
+
theme = Theme.LIGHT if isDarkTheme() else Theme.DARK
|
48
|
+
|
49
|
+
if isinstance(self.icon, Icon):
|
50
|
+
icon = self.icon.fluentIcon.icon(theme)
|
51
|
+
elif isinstance(self.icon, FluentIconBase):
|
52
|
+
icon = self.icon.icon(theme)
|
53
|
+
|
54
|
+
if rect.x() == 19:
|
55
|
+
rect = rect.adjusted(-1, 0, 0, 0)
|
56
|
+
|
57
|
+
icon.paint(painter, rect, Qt.AlignCenter, QIcon.Normal, state)
|
58
|
+
painter.restore()
|
59
|
+
|
60
|
+
|
61
|
+
class SvgIconEngine(QIconEngine):
|
62
|
+
""" Svg icon engine """
|
63
|
+
|
64
|
+
def __init__(self, svg: str):
|
65
|
+
super().__init__()
|
66
|
+
self.svg = svg
|
67
|
+
|
68
|
+
def paint(self, painter, rect, mode, state):
|
69
|
+
drawSvgIcon(self.svg.encode(), painter, rect)
|
70
|
+
|
71
|
+
def clone(self) -> QIconEngine:
|
72
|
+
return SvgIconEngine(self.svg)
|
73
|
+
|
74
|
+
def pixmap(self, size, mode, state):
|
75
|
+
image = QImage(size, QImage.Format_ARGB32)
|
76
|
+
image.fill(Qt.transparent)
|
77
|
+
pixmap = QPixmap.fromImage(image, Qt.NoFormatConversion)
|
78
|
+
|
79
|
+
painter = QPainter(pixmap)
|
80
|
+
rect = QRect(0, 0, size.width(), size.height())
|
81
|
+
self.paint(painter, rect, mode, state)
|
82
|
+
return pixmap
|
83
|
+
|
84
|
+
|
85
|
+
class FontIconEngine(QIconEngine):
|
86
|
+
""" Font icon engine """
|
87
|
+
|
88
|
+
def __init__(self, fontFamily: str, char: str, color, isBold):
|
89
|
+
super().__init__()
|
90
|
+
self.color = color
|
91
|
+
self.char = char
|
92
|
+
self.fontFamily = fontFamily
|
93
|
+
self.isBold = isBold
|
94
|
+
|
95
|
+
def paint(self, painter, rect, mode, state):
|
96
|
+
font = QFont(self.fontFamily)
|
97
|
+
font.setBold(self.isBold)
|
98
|
+
font.setPixelSize(round(rect.height()))
|
99
|
+
painter.setFont(font)
|
100
|
+
painter.setPen(Qt.PenStyle.NoPen)
|
101
|
+
painter.setBrush(self.color)
|
102
|
+
painter.setRenderHints(
|
103
|
+
QPainter.RenderHint.Antialiasing | QPainter.RenderHint.TextAntialiasing)
|
104
|
+
|
105
|
+
path = QPainterPath()
|
106
|
+
path.addText(rect.x(), rect.y() + rect.height(), font, self.char)
|
107
|
+
painter.drawPath(path)
|
108
|
+
|
109
|
+
|
110
|
+
def clone(self) -> QIconEngine:
|
111
|
+
return FontIconEngine(self.fontFamily, self.char, self.color, self.isBold)
|
112
|
+
|
113
|
+
def pixmap(self, size, mode, state):
|
114
|
+
image = QImage(size, QImage.Format_ARGB32)
|
115
|
+
image.fill(Qt.transparent)
|
116
|
+
pixmap = QPixmap.fromImage(image, Qt.NoFormatConversion)
|
117
|
+
|
118
|
+
painter = QPainter(pixmap)
|
119
|
+
rect = QRect(0, 0, size.width(), size.height())
|
120
|
+
self.paint(painter, rect, mode, state)
|
121
|
+
return pixmap
|
122
|
+
|
123
|
+
|
124
|
+
def getIconColor(theme=Theme.AUTO, reverse=False):
|
125
|
+
""" get the color of icon based on theme """
|
126
|
+
if not reverse:
|
127
|
+
lc, dc = "black", "white"
|
128
|
+
else:
|
129
|
+
lc, dc = "white", "black"
|
130
|
+
|
131
|
+
if theme == Theme.AUTO:
|
132
|
+
color = dc if isDarkTheme() else lc
|
133
|
+
else:
|
134
|
+
color = dc if theme == Theme.DARK else lc
|
135
|
+
|
136
|
+
return color
|
137
|
+
|
138
|
+
|
139
|
+
def drawSvgIcon(icon, painter, rect):
|
140
|
+
""" draw svg icon
|
141
|
+
|
142
|
+
Parameters
|
143
|
+
----------
|
144
|
+
icon: str | bytes | QByteArray
|
145
|
+
the path or code of svg icon
|
146
|
+
|
147
|
+
painter: QPainter
|
148
|
+
painter
|
149
|
+
|
150
|
+
rect: QRect | QRectF
|
151
|
+
the rect to render icon
|
152
|
+
"""
|
153
|
+
renderer = QSvgRenderer(icon)
|
154
|
+
renderer.render(painter, QRectF(rect))
|
155
|
+
|
156
|
+
|
157
|
+
def writeSvg(iconPath: str, indexes=None, **attributes):
|
158
|
+
""" write svg with specified attributes
|
159
|
+
|
160
|
+
Parameters
|
161
|
+
----------
|
162
|
+
iconPath: str
|
163
|
+
svg icon path
|
164
|
+
|
165
|
+
indexes: List[int]
|
166
|
+
the path to be filled
|
167
|
+
|
168
|
+
**attributes:
|
169
|
+
the attributes of path
|
170
|
+
|
171
|
+
Returns
|
172
|
+
-------
|
173
|
+
svg: str
|
174
|
+
svg code
|
175
|
+
"""
|
176
|
+
if not iconPath.lower().endswith('.svg'):
|
177
|
+
return ""
|
178
|
+
|
179
|
+
f = QFile(iconPath)
|
180
|
+
f.open(QFile.ReadOnly)
|
181
|
+
|
182
|
+
dom = QDomDocument()
|
183
|
+
dom.setContent(f.readAll())
|
184
|
+
|
185
|
+
f.close()
|
186
|
+
|
187
|
+
# change the color of each path
|
188
|
+
pathNodes = dom.elementsByTagName('path')
|
189
|
+
indexes = range(pathNodes.length()) if not indexes else indexes
|
190
|
+
for i in indexes:
|
191
|
+
element = pathNodes.at(i).toElement()
|
192
|
+
|
193
|
+
for k, v in attributes.items():
|
194
|
+
element.setAttribute(k, v)
|
195
|
+
|
196
|
+
return dom.toString()
|
197
|
+
|
198
|
+
|
199
|
+
def drawIcon(icon, painter, rect, state=QIcon.Off, **attributes):
|
200
|
+
""" draw icon
|
201
|
+
|
202
|
+
Parameters
|
203
|
+
----------
|
204
|
+
icon: str | QIcon | FluentIconBaseBase
|
205
|
+
the icon to be drawn
|
206
|
+
|
207
|
+
painter: QPainter
|
208
|
+
painter
|
209
|
+
|
210
|
+
rect: QRect | QRectF
|
211
|
+
the rect to render icon
|
212
|
+
|
213
|
+
**attribute:
|
214
|
+
the attribute of svg icon
|
215
|
+
"""
|
216
|
+
if isinstance(icon, FluentIconBase):
|
217
|
+
icon.render(painter, rect, **attributes)
|
218
|
+
elif isinstance(icon, Icon):
|
219
|
+
icon.fluentIcon.render(painter, rect, **attributes)
|
220
|
+
else:
|
221
|
+
icon = QIcon(icon)
|
222
|
+
icon.paint(painter, QRectF(rect).toRect(), Qt.AlignCenter, state=state)
|
223
|
+
|
224
|
+
|
225
|
+
class FluentIconBase:
|
226
|
+
""" Fluent icon base class """
|
227
|
+
|
228
|
+
def path(self, theme=Theme.AUTO) -> str:
|
229
|
+
""" get the path of icon
|
230
|
+
|
231
|
+
Parameters
|
232
|
+
----------
|
233
|
+
theme: Theme
|
234
|
+
the theme of icon
|
235
|
+
* `Theme.Light`: black icon
|
236
|
+
* `Theme.DARK`: white icon
|
237
|
+
* `Theme.AUTO`: icon color depends on `config.theme`
|
238
|
+
"""
|
239
|
+
raise NotImplementedError
|
240
|
+
|
241
|
+
def icon(self, theme=Theme.AUTO, color: QColor = None) -> QIcon:
|
242
|
+
""" create a fluent icon
|
243
|
+
|
244
|
+
Parameters
|
245
|
+
----------
|
246
|
+
theme: Theme
|
247
|
+
the theme of icon
|
248
|
+
* `Theme.Light`: black icon
|
249
|
+
* `Theme.DARK`: white icon
|
250
|
+
* `Theme.AUTO`: icon color depends on `qconfig.theme`
|
251
|
+
|
252
|
+
color: QColor | Qt.GlobalColor | str
|
253
|
+
icon color, only applicable to svg icon
|
254
|
+
"""
|
255
|
+
path = self.path(theme)
|
256
|
+
|
257
|
+
if not (path.endswith('.svg') and color):
|
258
|
+
return QIcon(self.path(theme))
|
259
|
+
|
260
|
+
color = QColor(color).name()
|
261
|
+
return QIcon(SvgIconEngine(writeSvg(path, fill=color)))
|
262
|
+
|
263
|
+
def colored(self, lightColor: QColor, darkColor: QColor) -> "ColoredFluentIcon":
|
264
|
+
""" create a colored fluent icon
|
265
|
+
|
266
|
+
Parameters
|
267
|
+
----------
|
268
|
+
lightColor: str | QColor | Qt.GlobalColor
|
269
|
+
icon color in light mode
|
270
|
+
|
271
|
+
darkColor: str | QColor | Qt.GlobalColor
|
272
|
+
icon color in dark mode
|
273
|
+
"""
|
274
|
+
return ColoredFluentIcon(self, lightColor, darkColor)
|
275
|
+
|
276
|
+
def qicon(self, reverse=False) -> QIcon:
|
277
|
+
""" convert to QIcon, the theme of icon will be updated synchronously with app
|
278
|
+
|
279
|
+
Parameters
|
280
|
+
----------
|
281
|
+
reverse: bool
|
282
|
+
whether to reverse the theme of icon
|
283
|
+
"""
|
284
|
+
return QIcon(FluentIconEngine(self, reverse))
|
285
|
+
|
286
|
+
def render(self, painter, rect, theme=Theme.AUTO, indexes=None, **attributes):
|
287
|
+
""" draw svg icon
|
288
|
+
|
289
|
+
Parameters
|
290
|
+
----------
|
291
|
+
painter: QPainter
|
292
|
+
painter
|
293
|
+
|
294
|
+
rect: QRect | QRectF
|
295
|
+
the rect to render icon
|
296
|
+
|
297
|
+
theme: Theme
|
298
|
+
the theme of icon
|
299
|
+
* `Theme.Light`: black icon
|
300
|
+
* `Theme.DARK`: white icon
|
301
|
+
* `Theme.AUTO`: icon color depends on `config.theme`
|
302
|
+
|
303
|
+
indexes: List[int]
|
304
|
+
the svg path to be modified
|
305
|
+
|
306
|
+
**attributes:
|
307
|
+
the attributes of modified path
|
308
|
+
"""
|
309
|
+
icon = self.path(theme)
|
310
|
+
|
311
|
+
if icon.endswith('.svg'):
|
312
|
+
if attributes:
|
313
|
+
icon = writeSvg(icon, indexes, **attributes).encode()
|
314
|
+
|
315
|
+
drawSvgIcon(icon, painter, rect)
|
316
|
+
else:
|
317
|
+
icon = QIcon(icon)
|
318
|
+
rect = QRectF(rect).toRect()
|
319
|
+
painter.drawPixmap(rect, icon.pixmap(QRectF(rect).toRect().size()))
|
320
|
+
|
321
|
+
|
322
|
+
class FluentFontIconBase(FluentIconBase):
|
323
|
+
""" Fluent font icon base class """
|
324
|
+
|
325
|
+
_isFontLoaded = False
|
326
|
+
fontId = None
|
327
|
+
fontFamily = None
|
328
|
+
_iconNames = {}
|
329
|
+
|
330
|
+
def __init__(self, char: str):
|
331
|
+
super().__init__()
|
332
|
+
self.char = char
|
333
|
+
self.lightColor = QColor(0, 0, 0)
|
334
|
+
self.darkColor = QColor(255, 255, 255)
|
335
|
+
self.isBold = False
|
336
|
+
self.loadFont()
|
337
|
+
|
338
|
+
@classmethod
|
339
|
+
def fromName(cls, name: str):
|
340
|
+
icon = cls("")
|
341
|
+
icon.char = cls._iconNames.get(name, "")
|
342
|
+
return icon
|
343
|
+
|
344
|
+
def bold(self):
|
345
|
+
self.isBold = True
|
346
|
+
return self
|
347
|
+
|
348
|
+
def icon(self, theme=Theme.AUTO, color: QColor = None) -> QIcon:
|
349
|
+
if not color:
|
350
|
+
color = self._getIconColor(theme)
|
351
|
+
|
352
|
+
return QIcon(FontIconEngine(self.fontFamily, self.char, color, self.isBold))
|
353
|
+
|
354
|
+
def colored(self, lightColor, darkColor):
|
355
|
+
self.lightColor = QColor(lightColor)
|
356
|
+
self.darkColor = QColor(darkColor)
|
357
|
+
return self
|
358
|
+
|
359
|
+
def render(self, painter: QPainter, rect, theme=Theme.AUTO, indexes=None, **attributes):
|
360
|
+
color = self._getIconColor(theme)
|
361
|
+
|
362
|
+
if "fill" in attributes:
|
363
|
+
color = QColor(attributes["fill"])
|
364
|
+
|
365
|
+
font = QFont(self.fontFamily)
|
366
|
+
font.setBold(self.isBold)
|
367
|
+
font.setPixelSize(round(rect.height()))
|
368
|
+
painter.setFont(font)
|
369
|
+
painter.setPen(Qt.PenStyle.NoPen)
|
370
|
+
painter.setBrush(color)
|
371
|
+
painter.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.TextAntialiasing)
|
372
|
+
|
373
|
+
path = QPainterPath()
|
374
|
+
path.addText(rect.x(), rect.y() + rect.height(), font, self.char)
|
375
|
+
painter.drawPath(path)
|
376
|
+
|
377
|
+
def iconNameMapPath(self) -> str:
|
378
|
+
return None
|
379
|
+
|
380
|
+
def loadFont(self):
|
381
|
+
""" Load icon font """
|
382
|
+
cls = self.__class__
|
383
|
+
if cls._isFontLoaded or not QApplication.instance():
|
384
|
+
return
|
385
|
+
|
386
|
+
file = QFile(self.path())
|
387
|
+
if not file.open(QFile.ReadOnly):
|
388
|
+
raise FileNotFoundError(f"Cannot open font file: {self.path()}")
|
389
|
+
|
390
|
+
data = file.readAll()
|
391
|
+
file.close()
|
392
|
+
|
393
|
+
cls.fontId = QFontDatabase.addApplicationFontFromData(data)
|
394
|
+
cls.fontFamily = QFontDatabase.applicationFontFamilies(cls.fontId)[0]
|
395
|
+
|
396
|
+
if self.iconNameMapPath():
|
397
|
+
self.loadIconNames()
|
398
|
+
|
399
|
+
def loadIconNames(self):
|
400
|
+
""" Load icon name map """
|
401
|
+
cls = self.__class__
|
402
|
+
cls._iconNames.clear()
|
403
|
+
|
404
|
+
file = QFile(self.iconNameMapPath())
|
405
|
+
if not file.open(QFile.ReadOnly):
|
406
|
+
raise FileNotFoundError(f"Cannot open font file: {self.iconNameMapPath()}")
|
407
|
+
|
408
|
+
cls._iconNames = json.loads(str(file.readAll(), encoding='utf-8'))
|
409
|
+
file.close()
|
410
|
+
|
411
|
+
def _getIconColor(self, theme):
|
412
|
+
if theme == Theme.AUTO:
|
413
|
+
color = self.darkColor if isDarkTheme() else self.lightColor
|
414
|
+
else:
|
415
|
+
color = self.darkColor if theme == Theme.DARK else self.lightColor
|
416
|
+
|
417
|
+
return color
|
418
|
+
|
419
|
+
|
420
|
+
class ColoredFluentIcon(FluentIconBase):
|
421
|
+
""" Colored fluent icon """
|
422
|
+
|
423
|
+
def __init__(self, icon: FluentIconBase, lightColor, darkColor):
|
424
|
+
"""
|
425
|
+
Parameters
|
426
|
+
----------
|
427
|
+
icon: FluentIconBase
|
428
|
+
the icon to be colored
|
429
|
+
|
430
|
+
lightColor: str | QColor | Qt.GlobalColor
|
431
|
+
icon color in light mode
|
432
|
+
|
433
|
+
darkColor: str | QColor | Qt.GlobalColor
|
434
|
+
icon color in dark mode
|
435
|
+
"""
|
436
|
+
super().__init__()
|
437
|
+
self.fluentIcon = icon
|
438
|
+
self.lightColor = QColor(lightColor)
|
439
|
+
self.darkColor = QColor(darkColor)
|
440
|
+
|
441
|
+
def path(self, theme=Theme.AUTO) -> str:
|
442
|
+
return self.fluentIcon.path(theme)
|
443
|
+
|
444
|
+
def render(self, painter, rect, theme=Theme.AUTO, indexes=None, **attributes):
|
445
|
+
icon = self.path(theme)
|
446
|
+
|
447
|
+
if not icon.endswith('.svg'):
|
448
|
+
return self.fluentIcon.render(painter, rect, theme, indexes, attributes)
|
449
|
+
|
450
|
+
if theme == Theme.AUTO:
|
451
|
+
color = self.darkColor if isDarkTheme() else self.lightColor
|
452
|
+
else:
|
453
|
+
color = self.darkColor if theme == Theme.DARK else self.lightColor
|
454
|
+
|
455
|
+
attributes.update(fill=color.name())
|
456
|
+
icon = writeSvg(icon, indexes, **attributes).encode()
|
457
|
+
drawSvgIcon(icon, painter, rect)
|
458
|
+
|
459
|
+
|
460
|
+
|
461
|
+
class FluentIcon(FluentIconBase, Enum):
|
462
|
+
""" Fluent icon """
|
463
|
+
|
464
|
+
UP = "Up"
|
465
|
+
ADD = "Add"
|
466
|
+
BUS = "Bus"
|
467
|
+
CAR = "Car"
|
468
|
+
CUT = "Cut"
|
469
|
+
IOT = "IOT"
|
470
|
+
PIN = "Pin"
|
471
|
+
TAG = "Tag"
|
472
|
+
VPN = "VPN"
|
473
|
+
CAFE = "Cafe"
|
474
|
+
CHAT = "Chat"
|
475
|
+
COPY = "Copy"
|
476
|
+
CODE = "Code"
|
477
|
+
DOWN = "Down"
|
478
|
+
EDIT = "Edit"
|
479
|
+
FLAG = "Flag"
|
480
|
+
FONT = "Font"
|
481
|
+
GAME = "Game"
|
482
|
+
HELP = "Help"
|
483
|
+
HIDE = "Hide"
|
484
|
+
HOME = "Home"
|
485
|
+
INFO = "Info"
|
486
|
+
LEAF = "Leaf"
|
487
|
+
LINK = "Link"
|
488
|
+
MAIL = "Mail"
|
489
|
+
MENU = "Menu"
|
490
|
+
MUTE = "Mute"
|
491
|
+
MORE = "More"
|
492
|
+
MOVE = "Move"
|
493
|
+
PLAY = "Play"
|
494
|
+
SAVE = "Save"
|
495
|
+
SEND = "Send"
|
496
|
+
SYNC = "Sync"
|
497
|
+
UNIT = "Unit"
|
498
|
+
VIEW = "View"
|
499
|
+
WIFI = "Wifi"
|
500
|
+
ZOOM = "Zoom"
|
501
|
+
ALBUM = "Album"
|
502
|
+
BRUSH = "Brush"
|
503
|
+
BROOM = "Broom"
|
504
|
+
CLOSE = "Close"
|
505
|
+
CLOUD = "Cloud"
|
506
|
+
EMBED = "Embed"
|
507
|
+
GLOBE = "Globe"
|
508
|
+
HEART = "Heart"
|
509
|
+
LABEL = "Label"
|
510
|
+
MEDIA = "Media"
|
511
|
+
MOVIE = "Movie"
|
512
|
+
MUSIC = "Music"
|
513
|
+
ROBOT = "Robot"
|
514
|
+
PAUSE = "Pause"
|
515
|
+
PASTE = "Paste"
|
516
|
+
PHOTO = "Photo"
|
517
|
+
PHONE = "Phone"
|
518
|
+
PRINT = "Print"
|
519
|
+
SHARE = "Share"
|
520
|
+
TILES = "Tiles"
|
521
|
+
UNPIN = "Unpin"
|
522
|
+
VIDEO = "Video"
|
523
|
+
TRAIN = "Train"
|
524
|
+
ADD_TO ="AddTo"
|
525
|
+
ACCEPT = "Accept"
|
526
|
+
CAMERA = "Camera"
|
527
|
+
CANCEL = "Cancel"
|
528
|
+
DELETE = "Delete"
|
529
|
+
FOLDER = "Folder"
|
530
|
+
FILTER = "Filter"
|
531
|
+
MARKET = "Market"
|
532
|
+
SCROLL = "Scroll"
|
533
|
+
LAYOUT = "Layout"
|
534
|
+
GITHUB = "GitHub"
|
535
|
+
UPDATE = "Update"
|
536
|
+
REMOVE = "Remove"
|
537
|
+
RETURN = "Return"
|
538
|
+
PEOPLE = "People"
|
539
|
+
QRCODE = "QRCode"
|
540
|
+
RINGER = "Ringer"
|
541
|
+
ROTATE = "Rotate"
|
542
|
+
SEARCH = "Search"
|
543
|
+
VOLUME = "Volume"
|
544
|
+
FRIGID = "Frigid"
|
545
|
+
SAVE_AS = "SaveAs"
|
546
|
+
ZOOM_IN = "ZoomIn"
|
547
|
+
CONNECT ="Connect"
|
548
|
+
HISTORY = "History"
|
549
|
+
SETTING = "Setting"
|
550
|
+
PALETTE = "Palette"
|
551
|
+
MESSAGE = "Message"
|
552
|
+
FIT_PAGE = "FitPage"
|
553
|
+
ZOOM_OUT = "ZoomOut"
|
554
|
+
AIRPLANE = "Airplane"
|
555
|
+
ASTERISK = "Asterisk"
|
556
|
+
CALORIES = "Calories"
|
557
|
+
CALENDAR = "Calendar"
|
558
|
+
FEEDBACK = "Feedback"
|
559
|
+
LIBRARY = "BookShelf"
|
560
|
+
MINIMIZE = "Minimize"
|
561
|
+
CHECKBOX = "CheckBox"
|
562
|
+
DOCUMENT = "Document"
|
563
|
+
LANGUAGE = "Language"
|
564
|
+
DOWNLOAD = "Download"
|
565
|
+
QUESTION = "Question"
|
566
|
+
SPEAKERS = "Speakers"
|
567
|
+
DATE_TIME = "DateTime"
|
568
|
+
FONT_SIZE = "FontSize"
|
569
|
+
HOME_FILL = "HomeFill"
|
570
|
+
PAGE_LEFT = "PageLeft"
|
571
|
+
SAVE_COPY = "SaveCopy"
|
572
|
+
SEND_FILL = "SendFill"
|
573
|
+
SKIP_BACK = "SkipBack"
|
574
|
+
SPEED_OFF = "SpeedOff"
|
575
|
+
ALIGNMENT = "Alignment"
|
576
|
+
BLUETOOTH = "Bluetooth"
|
577
|
+
COMPLETED = "Completed"
|
578
|
+
CONSTRACT = "Constract"
|
579
|
+
HEADPHONE = "Headphone"
|
580
|
+
MEGAPHONE = "Megaphone"
|
581
|
+
PROJECTOR = "Projector"
|
582
|
+
EDUCATION = "Education"
|
583
|
+
LEFT_ARROW = "LeftArrow"
|
584
|
+
ERASE_TOOL = "EraseTool"
|
585
|
+
PAGE_RIGHT = "PageRight"
|
586
|
+
PLAY_SOLID = "PlaySolid"
|
587
|
+
BOOK_SHELF = "BookShelf"
|
588
|
+
HIGHTLIGHT = "Highlight"
|
589
|
+
FOLDER_ADD = "FolderAdd"
|
590
|
+
PAUSE_BOLD = "PauseBold"
|
591
|
+
PENCIL_INK = "PencilInk"
|
592
|
+
PIE_SINGLE = "PieSingle"
|
593
|
+
QUICK_NOTE = "QuickNote"
|
594
|
+
SPEED_HIGH = "SpeedHigh"
|
595
|
+
STOP_WATCH = "StopWatch"
|
596
|
+
ZIP_FOLDER = "ZipFolder"
|
597
|
+
BASKETBALL = "Basketball"
|
598
|
+
BRIGHTNESS = "Brightness"
|
599
|
+
DICTIONARY = "Dictionary"
|
600
|
+
MICROPHONE = "Microphone"
|
601
|
+
ARROW_DOWN = "ChevronDown"
|
602
|
+
FULL_SCREEN = "FullScreen"
|
603
|
+
MIX_VOLUMES = "MixVolumes"
|
604
|
+
REMOVE_FROM = "RemoveFrom"
|
605
|
+
RIGHT_ARROW = "RightArrow"
|
606
|
+
QUIET_HOURS ="QuietHours"
|
607
|
+
FINGERPRINT = "Fingerprint"
|
608
|
+
APPLICATION = "Application"
|
609
|
+
CERTIFICATE = "Certificate"
|
610
|
+
TRANSPARENT = "Transparent"
|
611
|
+
IMAGE_EXPORT = "ImageExport"
|
612
|
+
SPEED_MEDIUM = "SpeedMedium"
|
613
|
+
LIBRARY_FILL = "LibraryFill"
|
614
|
+
MUSIC_FOLDER = "MusicFolder"
|
615
|
+
POWER_BUTTON = "PowerButton"
|
616
|
+
SKIP_FORWARD = "SkipForward"
|
617
|
+
CARE_UP_SOLID = "CareUpSolid"
|
618
|
+
ACCEPT_MEDIUM = "AcceptMedium"
|
619
|
+
CANCEL_MEDIUM = "CancelMedium"
|
620
|
+
CHEVRON_RIGHT = "ChevronRight"
|
621
|
+
CLIPPING_TOOL = "ClippingTool"
|
622
|
+
SEARCH_MIRROR = "SearchMirror"
|
623
|
+
SHOPPING_CART = "ShoppingCart"
|
624
|
+
FONT_INCREASE = "FontIncrease"
|
625
|
+
BACK_TO_WINDOW = "BackToWindow"
|
626
|
+
COMMAND_PROMPT = "CommandPrompt"
|
627
|
+
CLOUD_DOWNLOAD = "CloudDownload"
|
628
|
+
DICTIONARY_ADD = "DictionaryAdd"
|
629
|
+
CARE_DOWN_SOLID = "CareDownSolid"
|
630
|
+
CARE_LEFT_SOLID = "CareLeftSolid"
|
631
|
+
CLEAR_SELECTION = "ClearSelection"
|
632
|
+
DEVELOPER_TOOLS = "DeveloperTools"
|
633
|
+
BACKGROUND_FILL = "BackgroundColor"
|
634
|
+
CARE_RIGHT_SOLID = "CareRightSolid"
|
635
|
+
CHEVRON_DOWN_MED = "ChevronDownMed"
|
636
|
+
CHEVRON_RIGHT_MED = "ChevronRightMed"
|
637
|
+
EMOJI_TAB_SYMBOLS = "EmojiTabSymbols"
|
638
|
+
EXPRESSIVE_INPUT_ENTRY = "ExpressiveInputEntry"
|
639
|
+
|
640
|
+
def path(self, theme=Theme.AUTO):
|
641
|
+
return f':/fastuiwidgets/images/icons/{self.value}_{getIconColor(theme)}.svg'
|
642
|
+
|
643
|
+
|
644
|
+
class Icon(QIcon):
|
645
|
+
|
646
|
+
def __init__(self, fluentIcon: FluentIcon):
|
647
|
+
super().__init__(fluentIcon.path())
|
648
|
+
self.fluentIcon = fluentIcon
|
649
|
+
|
650
|
+
|
651
|
+
def toQIcon(icon: Union[QIcon, FluentIconBase, str]) -> QIcon:
|
652
|
+
""" convet `icon` to `QIcon` """
|
653
|
+
if isinstance(icon, str):
|
654
|
+
return QIcon(icon)
|
655
|
+
|
656
|
+
if isinstance(icon, FluentIconBase):
|
657
|
+
return icon.icon()
|
658
|
+
|
659
|
+
return icon
|
660
|
+
|
661
|
+
|
662
|
+
class Action(QAction):
|
663
|
+
""" Fluent action
|
664
|
+
|
665
|
+
Constructors
|
666
|
+
------------
|
667
|
+
* Action(`parent`: QWidget = None, `**kwargs`)
|
668
|
+
* Action(`text`: str, `parent`: QWidget = None, `**kwargs`)
|
669
|
+
* Action(`icon`: QIcon | FluentIconBase, `parent`: QWidget = None, `**kwargs`)
|
670
|
+
"""
|
671
|
+
|
672
|
+
@singledispatchmethod
|
673
|
+
def __init__(self, parent: QObject = None, **kwargs):
|
674
|
+
super().__init__(parent, **kwargs)
|
675
|
+
self.fluentIcon = None
|
676
|
+
|
677
|
+
@__init__.register
|
678
|
+
def _(self, text: str, parent: QObject = None, **kwargs):
|
679
|
+
super().__init__(text, parent, **kwargs)
|
680
|
+
self.fluentIcon = None
|
681
|
+
|
682
|
+
@__init__.register
|
683
|
+
def _(self, icon: QIcon, text: str, parent: QObject = None, **kwargs):
|
684
|
+
super().__init__(icon, text, parent, **kwargs)
|
685
|
+
self.fluentIcon = None
|
686
|
+
|
687
|
+
@__init__.register
|
688
|
+
def _(self, icon: FluentIconBase, text: str, parent: QObject = None, **kwargs):
|
689
|
+
super().__init__(icon.icon(), text, parent, **kwargs)
|
690
|
+
self.fluentIcon = icon
|
691
|
+
|
692
|
+
def icon(self) -> QIcon:
|
693
|
+
if self.fluentIcon:
|
694
|
+
return Icon(self.fluentIcon)
|
695
|
+
|
696
|
+
return super().icon()
|
697
|
+
|
698
|
+
def setIcon(self, icon: Union[FluentIconBase, QIcon]):
|
699
|
+
if isinstance(icon, FluentIconBase):
|
700
|
+
self.fluentIcon = icon
|
701
|
+
icon = icon.icon()
|
702
|
+
|
703
|
+
super().setIcon(icon)
|