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,307 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from PySide6.QtCore import QSize, Qt, Signal, QPoint, QRectF, QPropertyAnimation, Property, QEasingCurve
|
3
|
+
from PySide6.QtGui import QColor, QMouseEvent, QPainter, QPainterPath
|
4
|
+
from PySide6.QtWidgets import QProxyStyle, QSlider, QStyle, QStyleOptionSlider, QWidget
|
5
|
+
|
6
|
+
from ...common.style_sheet import FluentStyleSheet, themeColor, isDarkTheme
|
7
|
+
from ...common.color import autoFallbackThemeColor
|
8
|
+
from ...common.overload import singledispatchmethod
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
class SliderHandle(QWidget):
|
13
|
+
""" Slider handle """
|
14
|
+
|
15
|
+
pressed = Signal()
|
16
|
+
released = Signal()
|
17
|
+
|
18
|
+
def __init__(self, parent: QSlider):
|
19
|
+
super().__init__(parent=parent)
|
20
|
+
self.setFixedSize(22, 22)
|
21
|
+
self._radius = 5
|
22
|
+
self.lightHandleColor = QColor()
|
23
|
+
self.darkHandleColor = QColor()
|
24
|
+
self.radiusAni = QPropertyAnimation(self, b'radius', self)
|
25
|
+
self.radiusAni.setDuration(100)
|
26
|
+
|
27
|
+
@Property(int)
|
28
|
+
def radius(self):
|
29
|
+
return self._radius
|
30
|
+
|
31
|
+
@radius.setter
|
32
|
+
def radius(self, r):
|
33
|
+
self._radius = r
|
34
|
+
self.update()
|
35
|
+
|
36
|
+
def setHandleColor(self, light, dark):
|
37
|
+
self.lightHandleColor = QColor(light)
|
38
|
+
self.darkHandleColor = QColor(dark)
|
39
|
+
self.update()
|
40
|
+
|
41
|
+
def enterEvent(self, e):
|
42
|
+
self._startAni(6)
|
43
|
+
|
44
|
+
def leaveEvent(self, e):
|
45
|
+
self._startAni(5)
|
46
|
+
|
47
|
+
def mousePressEvent(self, e):
|
48
|
+
self._startAni(4)
|
49
|
+
self.pressed.emit()
|
50
|
+
|
51
|
+
def mouseReleaseEvent(self, e):
|
52
|
+
self._startAni(6)
|
53
|
+
self.released.emit()
|
54
|
+
|
55
|
+
def _startAni(self, radius):
|
56
|
+
self.radiusAni.stop()
|
57
|
+
self.radiusAni.setStartValue(self.radius)
|
58
|
+
self.radiusAni.setEndValue(radius)
|
59
|
+
self.radiusAni.start()
|
60
|
+
|
61
|
+
def paintEvent(self, e):
|
62
|
+
painter = QPainter(self)
|
63
|
+
painter.setRenderHints(QPainter.RenderHint.Antialiasing)
|
64
|
+
painter.setPen(Qt.PenStyle.NoPen)
|
65
|
+
|
66
|
+
# draw outer circle
|
67
|
+
isDark = isDarkTheme()
|
68
|
+
painter.setPen(QColor(0, 0, 0, 90 if isDark else 25))
|
69
|
+
painter.setBrush(QColor(69, 69, 69) if isDark else Qt.GlobalColor.white)
|
70
|
+
painter.drawEllipse(self.rect().adjusted(1, 1, -1, -1))
|
71
|
+
|
72
|
+
# draw innert circle
|
73
|
+
painter.setBrush(autoFallbackThemeColor(self.lightHandleColor, self.darkHandleColor))
|
74
|
+
painter.drawEllipse(QPoint(11, 11), self.radius, self.radius)
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
class Slider(QSlider):
|
79
|
+
""" A slider can be clicked
|
80
|
+
|
81
|
+
Constructors
|
82
|
+
------------
|
83
|
+
* Slider(`parent`: QWidget = None)
|
84
|
+
* Slider(`orient`: Qt.Orientation, `parent`: QWidget = None)
|
85
|
+
"""
|
86
|
+
|
87
|
+
clicked = Signal(int)
|
88
|
+
|
89
|
+
@singledispatchmethod
|
90
|
+
def __init__(self, parent: QWidget = None):
|
91
|
+
super().__init__(parent)
|
92
|
+
self._postInit()
|
93
|
+
|
94
|
+
@__init__.register
|
95
|
+
def _(self, orientation: Qt.Orientation, parent: QWidget = None):
|
96
|
+
super().__init__(orientation, parent=parent)
|
97
|
+
self._postInit()
|
98
|
+
|
99
|
+
def _postInit(self):
|
100
|
+
self.handle = SliderHandle(self)
|
101
|
+
self._pressedPos = QPoint()
|
102
|
+
self.lightGrooveColor = QColor()
|
103
|
+
self.darkGrooveColor = QColor()
|
104
|
+
self.setOrientation(self.orientation())
|
105
|
+
|
106
|
+
self.handle.pressed.connect(self.sliderPressed)
|
107
|
+
self.handle.released.connect(self.sliderReleased)
|
108
|
+
self.valueChanged.connect(self._adjustHandlePos)
|
109
|
+
|
110
|
+
def setThemeColor(self, light, dark):
|
111
|
+
self.lightGrooveColor = QColor(light)
|
112
|
+
self.darkGrooveColor = QColor(dark)
|
113
|
+
self.handle.setHandleColor(light, dark)
|
114
|
+
self.update()
|
115
|
+
|
116
|
+
def setOrientation(self, orientation: Qt.Orientation) -> None:
|
117
|
+
super().setOrientation(orientation)
|
118
|
+
if orientation == Qt.Orientation.Horizontal:
|
119
|
+
self.setMinimumHeight(22)
|
120
|
+
else:
|
121
|
+
self.setMinimumWidth(22)
|
122
|
+
|
123
|
+
def mousePressEvent(self, e: QMouseEvent):
|
124
|
+
self._pressedPos = e.pos()
|
125
|
+
self.setValue(self._posToValue(e.pos()))
|
126
|
+
self.clicked.emit(self.value())
|
127
|
+
|
128
|
+
def mouseMoveEvent(self, e: QMouseEvent):
|
129
|
+
self.setValue(self._posToValue(e.pos()))
|
130
|
+
self._pressedPos = e.pos()
|
131
|
+
self.sliderMoved.emit(self.value())
|
132
|
+
|
133
|
+
@property
|
134
|
+
def grooveLength(self):
|
135
|
+
l = self.width() if self.orientation() == Qt.Orientation.Horizontal else self.height()
|
136
|
+
return l - self.handle.width()
|
137
|
+
|
138
|
+
def _adjustHandlePos(self):
|
139
|
+
total = max(self.maximum() - self.minimum(), 1)
|
140
|
+
delta = int((self.value() - self.minimum()) / total * self.grooveLength)
|
141
|
+
|
142
|
+
if self.orientation() == Qt.Orientation.Vertical:
|
143
|
+
self.handle.move(0, delta)
|
144
|
+
else:
|
145
|
+
self.handle.move(delta, 0)
|
146
|
+
|
147
|
+
def _posToValue(self, pos: QPoint):
|
148
|
+
pd = self.handle.width() / 2
|
149
|
+
gs = max(self.grooveLength, 1)
|
150
|
+
v = pos.x() if self.orientation() == Qt.Orientation.Horizontal else pos.y()
|
151
|
+
return int((v - pd) / gs * (self.maximum() - self.minimum()) + self.minimum())
|
152
|
+
|
153
|
+
def paintEvent(self, e):
|
154
|
+
painter = QPainter(self)
|
155
|
+
painter.setRenderHints(QPainter.RenderHint.Antialiasing)
|
156
|
+
painter.setPen(Qt.PenStyle.NoPen)
|
157
|
+
painter.setBrush(QColor(255, 255, 255, 115) if isDarkTheme() else QColor(0, 0, 0, 100))
|
158
|
+
|
159
|
+
if self.orientation() == Qt.Orientation.Horizontal:
|
160
|
+
self._drawHorizonGroove(painter)
|
161
|
+
else:
|
162
|
+
self._drawVerticalGroove(painter)
|
163
|
+
|
164
|
+
def _drawHorizonGroove(self, painter: QPainter):
|
165
|
+
w, r = self.width(), self.handle.width() / 2
|
166
|
+
painter.drawRoundedRect(QRectF(r, r-2, w-r*2, 4), 2, 2)
|
167
|
+
|
168
|
+
if self.maximum() - self.minimum() == 0:
|
169
|
+
return
|
170
|
+
|
171
|
+
painter.setBrush(autoFallbackThemeColor(self.lightGrooveColor, self.darkGrooveColor))
|
172
|
+
aw = (self.value() - self.minimum()) / (self.maximum() - self.minimum()) * (w - r*2)
|
173
|
+
painter.drawRoundedRect(QRectF(r, r-2, aw, 4), 2, 2)
|
174
|
+
|
175
|
+
def _drawVerticalGroove(self, painter: QPainter):
|
176
|
+
h, r = self.height(), self.handle.width() / 2
|
177
|
+
painter.drawRoundedRect(QRectF(r-2, r, 4, h-2*r), 2, 2)
|
178
|
+
|
179
|
+
if self.maximum() - self.minimum() == 0:
|
180
|
+
return
|
181
|
+
|
182
|
+
painter.setBrush(autoFallbackThemeColor(self.lightGrooveColor, self.darkGrooveColor))
|
183
|
+
ah = (self.value() - self.minimum()) / (self.maximum() - self.minimum()) * (h - r*2)
|
184
|
+
painter.drawRoundedRect(QRectF(r-2, r, 4, ah), 2, 2)
|
185
|
+
|
186
|
+
def resizeEvent(self, e):
|
187
|
+
self._adjustHandlePos()
|
188
|
+
|
189
|
+
|
190
|
+
class ClickableSlider(QSlider):
|
191
|
+
""" A slider can be clicked """
|
192
|
+
|
193
|
+
clicked = Signal(int)
|
194
|
+
|
195
|
+
def mousePressEvent(self, e: QMouseEvent):
|
196
|
+
super().mousePressEvent(e)
|
197
|
+
|
198
|
+
if self.orientation() == Qt.Horizontal:
|
199
|
+
value = int(e.pos().x() / self.width() * self.maximum())
|
200
|
+
else:
|
201
|
+
value = int((self.height()-e.pos().y()) /
|
202
|
+
self.height() * self.maximum())
|
203
|
+
|
204
|
+
self.setValue(value)
|
205
|
+
self.clicked.emit(self.value())
|
206
|
+
|
207
|
+
|
208
|
+
|
209
|
+
class HollowHandleStyle(QProxyStyle):
|
210
|
+
""" Hollow handle style """
|
211
|
+
|
212
|
+
def __init__(self, config: dict = None):
|
213
|
+
"""
|
214
|
+
Parameters
|
215
|
+
----------
|
216
|
+
config: dict
|
217
|
+
style config
|
218
|
+
"""
|
219
|
+
super().__init__()
|
220
|
+
self.config = {
|
221
|
+
"groove.height": 3,
|
222
|
+
"sub-page.color": QColor(255, 255, 255),
|
223
|
+
"add-page.color": QColor(255, 255, 255, 64),
|
224
|
+
"handle.color": QColor(255, 255, 255),
|
225
|
+
"handle.ring-width": 4,
|
226
|
+
"handle.hollow-radius": 6,
|
227
|
+
"handle.margin": 4
|
228
|
+
}
|
229
|
+
config = config if config else {}
|
230
|
+
self.config.update(config)
|
231
|
+
|
232
|
+
# get handle size
|
233
|
+
w = self.config["handle.margin"]+self.config["handle.ring-width"] + \
|
234
|
+
self.config["handle.hollow-radius"]
|
235
|
+
self.config["handle.size"] = QSize(2*w, 2*w)
|
236
|
+
|
237
|
+
def subControlRect(self, cc: QStyle.ComplexControl, opt: QStyleOptionSlider, sc: QStyle.SubControl, widget: QSlider):
|
238
|
+
""" get the rectangular area occupied by the sub control """
|
239
|
+
if cc != self.ComplexControl.CC_Slider or widget.orientation() != Qt.Horizontal \
|
240
|
+
or sc == self.SubControl.SC_SliderTickmarks:
|
241
|
+
return super().subControlRect(cc, opt, sc, widget)
|
242
|
+
|
243
|
+
rect = widget.rect()
|
244
|
+
|
245
|
+
if sc == self.SubControl.SC_SliderGroove:
|
246
|
+
h = self.config["groove.height"]
|
247
|
+
grooveRect = QRectF(0, (rect.height()-h)//2, rect.width(), h)
|
248
|
+
return grooveRect.toRect()
|
249
|
+
|
250
|
+
elif sc == self.SubControl.SC_SliderHandle:
|
251
|
+
size = self.config["handle.size"]
|
252
|
+
x = self.sliderPositionFromValue(
|
253
|
+
widget.minimum(), widget.maximum(), widget.value(), rect.width())
|
254
|
+
|
255
|
+
# solve the situation that the handle runs out of slider
|
256
|
+
x *= (rect.width()-size.width())/rect.width()
|
257
|
+
sliderRect = QRectF(x, 0, size.width(), size.height())
|
258
|
+
return sliderRect.toRect()
|
259
|
+
|
260
|
+
def drawComplexControl(self, cc: QStyle.ComplexControl, opt: QStyleOptionSlider, painter: QPainter, widget: QSlider):
|
261
|
+
""" draw sub control """
|
262
|
+
if cc != self.ComplexControl.CC_Slider or widget.orientation() != Qt.Horizontal:
|
263
|
+
return super().drawComplexControl(cc, opt, painter, widget)
|
264
|
+
|
265
|
+
grooveRect = self.subControlRect(cc, opt, self.SubControl.SC_SliderGroove, widget)
|
266
|
+
handleRect = self.subControlRect(cc, opt, self.SubControl.SC_SliderHandle, widget)
|
267
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
268
|
+
painter.setPen(Qt.NoPen)
|
269
|
+
|
270
|
+
# paint groove
|
271
|
+
painter.save()
|
272
|
+
painter.translate(grooveRect.topLeft())
|
273
|
+
|
274
|
+
# paint the crossed part
|
275
|
+
w = handleRect.x()-grooveRect.x()
|
276
|
+
h = self.config['groove.height']
|
277
|
+
painter.setBrush(self.config["sub-page.color"])
|
278
|
+
painter.drawRect(0, 0, w, h)
|
279
|
+
|
280
|
+
# paint the uncrossed part
|
281
|
+
x = w+self.config['handle.size'].width()
|
282
|
+
painter.setBrush(self.config["add-page.color"])
|
283
|
+
painter.drawRect(x, 0, grooveRect.width()-w, h)
|
284
|
+
painter.restore()
|
285
|
+
|
286
|
+
# paint handle
|
287
|
+
ringWidth = self.config["handle.ring-width"]
|
288
|
+
hollowRadius = self.config["handle.hollow-radius"]
|
289
|
+
radius = ringWidth + hollowRadius
|
290
|
+
|
291
|
+
path = QPainterPath()
|
292
|
+
path.moveTo(0, 0)
|
293
|
+
center = handleRect.center() + QPoint(1, 1)
|
294
|
+
path.addEllipse(center, radius, radius)
|
295
|
+
path.addEllipse(center, hollowRadius, hollowRadius)
|
296
|
+
|
297
|
+
handleColor = self.config["handle.color"] # type:QColor
|
298
|
+
handleColor.setAlpha(255 if opt.activeSubControls !=
|
299
|
+
self.SubControl.SC_SliderHandle else 153)
|
300
|
+
painter.setBrush(handleColor)
|
301
|
+
painter.drawPath(path)
|
302
|
+
|
303
|
+
# press handle
|
304
|
+
if widget.isSliderDown():
|
305
|
+
handleColor.setAlpha(255)
|
306
|
+
painter.setBrush(handleColor)
|
307
|
+
painter.drawEllipse(handleRect)
|
@@ -0,0 +1,306 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from enum import Enum
|
3
|
+
|
4
|
+
from PySide6.QtCore import Qt, QSize, QRectF, QPoint
|
5
|
+
from PySide6.QtGui import QPainter, QPainterPath, QColor
|
6
|
+
from PySide6.QtWidgets import (QSpinBox, QDoubleSpinBox, QToolButton, QHBoxLayout,
|
7
|
+
QDateEdit, QDateTimeEdit, QTimeEdit, QVBoxLayout, QApplication)
|
8
|
+
|
9
|
+
from ...common.style_sheet import FluentStyleSheet, themeColor, isDarkTheme
|
10
|
+
from ...common.icon import FluentIconBase, Theme, getIconColor
|
11
|
+
from ...common.font import setFont
|
12
|
+
from ...common.color import FluentSystemColor, autoFallbackThemeColor
|
13
|
+
from .button import TransparentToolButton
|
14
|
+
from .line_edit import LineEditMenu
|
15
|
+
from .flyout import Flyout, FlyoutViewBase, FlyoutAnimationType
|
16
|
+
|
17
|
+
|
18
|
+
class SpinIcon(FluentIconBase, Enum):
|
19
|
+
""" Spin icon """
|
20
|
+
|
21
|
+
UP = "Up"
|
22
|
+
DOWN = "Down"
|
23
|
+
|
24
|
+
def path(self, theme=Theme.AUTO):
|
25
|
+
return f':/fastuiwidgets/images/spin_box/{self.value}_{getIconColor(theme)}.svg'
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
class SpinButton(QToolButton):
|
30
|
+
|
31
|
+
def __init__(self, icon: SpinIcon, parent=None):
|
32
|
+
super().__init__(parent=parent)
|
33
|
+
self.isPressed = False
|
34
|
+
self._icon = icon
|
35
|
+
self.setFixedSize(31, 23)
|
36
|
+
self.setIconSize(QSize(10, 10))
|
37
|
+
FluentStyleSheet.SPIN_BOX.apply(self)
|
38
|
+
|
39
|
+
def mousePressEvent(self, e):
|
40
|
+
self.isPressed = True
|
41
|
+
super().mousePressEvent(e)
|
42
|
+
|
43
|
+
def mouseReleaseEvent(self, e):
|
44
|
+
self.isPressed = False
|
45
|
+
super().mouseReleaseEvent(e)
|
46
|
+
|
47
|
+
def paintEvent(self, e):
|
48
|
+
super().paintEvent(e)
|
49
|
+
painter = QPainter(self)
|
50
|
+
painter.setRenderHints(QPainter.Antialiasing |
|
51
|
+
QPainter.SmoothPixmapTransform)
|
52
|
+
|
53
|
+
if not self.isEnabled():
|
54
|
+
painter.setOpacity(0.36)
|
55
|
+
elif self.isPressed:
|
56
|
+
painter.setOpacity(0.7)
|
57
|
+
|
58
|
+
self._icon.render(painter, QRectF(10, 6.5, 11, 11))
|
59
|
+
|
60
|
+
|
61
|
+
class CompactSpinButton(QToolButton):
|
62
|
+
""" Compact spin button """
|
63
|
+
|
64
|
+
def __init__(self, parent=None):
|
65
|
+
super().__init__(parent=parent)
|
66
|
+
self.setFixedSize(26, 33)
|
67
|
+
self.setCursor(Qt.IBeamCursor)
|
68
|
+
|
69
|
+
def paintEvent(self, e):
|
70
|
+
painter = QPainter(self)
|
71
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
72
|
+
|
73
|
+
x = (self.width() - 10) / 2
|
74
|
+
s = 9
|
75
|
+
|
76
|
+
SpinIcon.UP.render(painter, QRectF(x, self.height() / 2 - s + 1, s, s))
|
77
|
+
SpinIcon.DOWN.render(painter, QRectF(x, self.height() / 2 , s, s))
|
78
|
+
|
79
|
+
|
80
|
+
class SpinFlyoutView(FlyoutViewBase):
|
81
|
+
""" Spin flyout view """
|
82
|
+
|
83
|
+
def __init__(self, parent=None):
|
84
|
+
super().__init__(parent)
|
85
|
+
self.upButton = TransparentToolButton(SpinIcon.UP, self)
|
86
|
+
self.downButton = TransparentToolButton(SpinIcon.DOWN, self)
|
87
|
+
self.vBoxLayout = QVBoxLayout(self)
|
88
|
+
|
89
|
+
self.upButton.setFixedSize(36, 36)
|
90
|
+
self.downButton.setFixedSize(36, 36)
|
91
|
+
self.upButton.setIconSize(QSize(13, 13))
|
92
|
+
self.downButton.setIconSize(QSize(13, 13))
|
93
|
+
|
94
|
+
self.vBoxLayout.setContentsMargins(6, 6, 6, 6)
|
95
|
+
self.vBoxLayout.setSpacing(0)
|
96
|
+
self.vBoxLayout.addWidget(self.upButton)
|
97
|
+
self.vBoxLayout.addWidget(self.downButton)
|
98
|
+
|
99
|
+
def paintEvent(self, e):
|
100
|
+
painter = QPainter(self)
|
101
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
102
|
+
|
103
|
+
painter.setBrush(
|
104
|
+
QColor(46, 46, 46) if isDarkTheme() else QColor(249, 249, 249))
|
105
|
+
painter.setPen(
|
106
|
+
QColor(0, 0, 0, 51) if isDarkTheme() else QColor(0, 0, 0, 15))
|
107
|
+
|
108
|
+
rect = self.rect().adjusted(1, 1, -1, -1)
|
109
|
+
painter.drawRoundedRect(rect, 8, 8)
|
110
|
+
|
111
|
+
|
112
|
+
class SpinBoxBase:
|
113
|
+
""" Spin box ui """
|
114
|
+
|
115
|
+
def __init__(self, parent=None):
|
116
|
+
super().__init__(parent=parent)
|
117
|
+
self._isError = False
|
118
|
+
self.lightFocusedBorderColor = QColor()
|
119
|
+
self.darkFocusedBorderColor = QColor()
|
120
|
+
|
121
|
+
self.hBoxLayout = QHBoxLayout(self)
|
122
|
+
|
123
|
+
self.setProperty('transparent', True)
|
124
|
+
FluentStyleSheet.SPIN_BOX.apply(self)
|
125
|
+
self.setButtonSymbols(QSpinBox.NoButtons)
|
126
|
+
self.setFixedHeight(33)
|
127
|
+
setFont(self)
|
128
|
+
|
129
|
+
self.setAttribute(Qt.WA_MacShowFocusRect, False)
|
130
|
+
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
131
|
+
self.customContextMenuRequested.connect(self._showContextMenu)
|
132
|
+
|
133
|
+
def isError(self):
|
134
|
+
return self._isError
|
135
|
+
|
136
|
+
def setError(self, isError: bool):
|
137
|
+
""" set the error status """
|
138
|
+
if isError == self.isError():
|
139
|
+
return
|
140
|
+
|
141
|
+
self._isError = isError
|
142
|
+
self.update()
|
143
|
+
|
144
|
+
def setReadOnly(self, isReadOnly: bool):
|
145
|
+
super().setReadOnly(isReadOnly)
|
146
|
+
self.setSymbolVisible(not isReadOnly)
|
147
|
+
|
148
|
+
def setSymbolVisible(self, isVisible: bool):
|
149
|
+
""" set whether the spin symbol is visible """
|
150
|
+
self.setProperty("symbolVisible", isVisible)
|
151
|
+
self.setStyle(QApplication.style())
|
152
|
+
|
153
|
+
def setCustomFocusedBorderColor(self, light, dark):
|
154
|
+
""" set the border color in focused status
|
155
|
+
|
156
|
+
Parameters
|
157
|
+
----------
|
158
|
+
light, dark: str | QColor | Qt.GlobalColor
|
159
|
+
border color in light/dark theme mode
|
160
|
+
"""
|
161
|
+
self.lightFocusedBorderColor = QColor(light)
|
162
|
+
self.darkFocusedBorderColor = QColor(dark)
|
163
|
+
self.update()
|
164
|
+
|
165
|
+
def focusedBorderColor(self):
|
166
|
+
if self.isError():
|
167
|
+
return FluentSystemColor.CRITICAL_FOREGROUND.color()
|
168
|
+
|
169
|
+
return autoFallbackThemeColor(self.lightFocusedBorderColor, self.darkFocusedBorderColor)
|
170
|
+
|
171
|
+
def _showContextMenu(self, pos):
|
172
|
+
menu = LineEditMenu(self.lineEdit())
|
173
|
+
menu.exec_(self.mapToGlobal(pos))
|
174
|
+
|
175
|
+
def _drawBorderBottom(self):
|
176
|
+
if not self.hasFocus():
|
177
|
+
return
|
178
|
+
|
179
|
+
painter = QPainter(self)
|
180
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
181
|
+
painter.setPen(Qt.NoPen)
|
182
|
+
|
183
|
+
path = QPainterPath()
|
184
|
+
w, h = self.width(), self.height()
|
185
|
+
path.addRoundedRect(QRectF(0, h-10, w, 10), 5, 5)
|
186
|
+
|
187
|
+
rectPath = QPainterPath()
|
188
|
+
rectPath.addRect(0, h-10, w, 8)
|
189
|
+
path = path.subtracted(rectPath)
|
190
|
+
|
191
|
+
painter.fillPath(path, self.focusedBorderColor())
|
192
|
+
|
193
|
+
def paintEvent(self, e):
|
194
|
+
super().paintEvent(e)
|
195
|
+
self._drawBorderBottom()
|
196
|
+
|
197
|
+
|
198
|
+
class InlineSpinBoxBase(SpinBoxBase):
|
199
|
+
""" Inline spin box base """
|
200
|
+
|
201
|
+
def __init__(self, parent=None):
|
202
|
+
super().__init__(parent)
|
203
|
+
self.upButton = SpinButton(SpinIcon.UP, self)
|
204
|
+
self.downButton = SpinButton(SpinIcon.DOWN, self)
|
205
|
+
|
206
|
+
self.hBoxLayout.setContentsMargins(0, 4, 4, 4)
|
207
|
+
self.hBoxLayout.setSpacing(5)
|
208
|
+
self.hBoxLayout.addWidget(self.upButton, 0, Qt.AlignRight)
|
209
|
+
self.hBoxLayout.addWidget(self.downButton, 0, Qt.AlignRight)
|
210
|
+
self.hBoxLayout.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
211
|
+
|
212
|
+
self.upButton.clicked.connect(self.stepUp)
|
213
|
+
self.downButton.clicked.connect(self.stepDown)
|
214
|
+
|
215
|
+
def setSymbolVisible(self, isVisible: bool):
|
216
|
+
super().setSymbolVisible(isVisible)
|
217
|
+
self.upButton.setVisible(isVisible)
|
218
|
+
self.downButton.setVisible(isVisible)
|
219
|
+
|
220
|
+
def setAccelerated(self, on: bool):
|
221
|
+
super().setAccelerated(on)
|
222
|
+
self.upButton.setAutoRepeat(on)
|
223
|
+
self.downButton.setAutoRepeat(on)
|
224
|
+
|
225
|
+
|
226
|
+
class CompactSpinBoxBase(SpinBoxBase):
|
227
|
+
""" Compact spin box base """
|
228
|
+
|
229
|
+
def __init__(self, parent=None):
|
230
|
+
super().__init__(parent)
|
231
|
+
self.compactSpinButton = CompactSpinButton(self)
|
232
|
+
self.spinFlyoutView = SpinFlyoutView(self)
|
233
|
+
self.spinFlyout = Flyout(self.spinFlyoutView, self, False)
|
234
|
+
|
235
|
+
self.hBoxLayout.setContentsMargins(0, 0, 0, 0)
|
236
|
+
self.hBoxLayout.addWidget(self.compactSpinButton, 0, Qt.AlignRight)
|
237
|
+
self.hBoxLayout.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
238
|
+
|
239
|
+
self.compactSpinButton.clicked.connect(self._showFlyout)
|
240
|
+
self.spinFlyoutView.upButton.clicked.connect(self.stepUp)
|
241
|
+
self.spinFlyoutView.downButton.clicked.connect(self.stepDown)
|
242
|
+
|
243
|
+
self.spinFlyout.hide()
|
244
|
+
|
245
|
+
def setAccelerated(self, on: bool):
|
246
|
+
super().setAccelerated(on)
|
247
|
+
self.spinFlyoutView.upButton.setAutoRepeat(on)
|
248
|
+
self.spinFlyoutView.downButton.setAutoRepeat(on)
|
249
|
+
|
250
|
+
def focusInEvent(self, e):
|
251
|
+
super().focusInEvent(e)
|
252
|
+
self._showFlyout()
|
253
|
+
|
254
|
+
def setSymbolVisible(self, isVisible: bool):
|
255
|
+
super().setSymbolVisible(isVisible)
|
256
|
+
self.compactSpinButton.setVisible(isVisible)
|
257
|
+
|
258
|
+
def _showFlyout(self):
|
259
|
+
if self.spinFlyout.isVisible() or self.isReadOnly():
|
260
|
+
return
|
261
|
+
|
262
|
+
y = int(self.compactSpinButton.height() / 2 - 46)
|
263
|
+
pos = self.compactSpinButton.mapToGlobal(QPoint(-12, y))
|
264
|
+
|
265
|
+
self.spinFlyout.exec(pos, FlyoutAnimationType.FADE_IN)
|
266
|
+
|
267
|
+
|
268
|
+
class SpinBox(InlineSpinBoxBase, QSpinBox):
|
269
|
+
""" Spin box """
|
270
|
+
|
271
|
+
|
272
|
+
class CompactSpinBox(CompactSpinBoxBase, QSpinBox):
|
273
|
+
""" Compact spin box """
|
274
|
+
|
275
|
+
|
276
|
+
class DoubleSpinBox(InlineSpinBoxBase, QDoubleSpinBox):
|
277
|
+
""" Double spin box """
|
278
|
+
|
279
|
+
|
280
|
+
class CompactDoubleSpinBox(CompactSpinBoxBase, QDoubleSpinBox):
|
281
|
+
""" Compact double spin box """
|
282
|
+
|
283
|
+
|
284
|
+
class TimeEdit(InlineSpinBoxBase, QTimeEdit):
|
285
|
+
""" Time edit """
|
286
|
+
|
287
|
+
|
288
|
+
class CompactTimeEdit(CompactSpinBoxBase, QTimeEdit):
|
289
|
+
""" Compact time edit """
|
290
|
+
|
291
|
+
|
292
|
+
class DateTimeEdit(InlineSpinBoxBase, QDateTimeEdit):
|
293
|
+
""" Date time edit """
|
294
|
+
|
295
|
+
|
296
|
+
class CompactDateTimeEdit(CompactSpinBoxBase, QDateTimeEdit):
|
297
|
+
""" Compact date time edit """
|
298
|
+
|
299
|
+
|
300
|
+
class DateEdit(InlineSpinBoxBase, QDateEdit):
|
301
|
+
""" Date edit """
|
302
|
+
|
303
|
+
|
304
|
+
class CompactDateEdit(CompactSpinBoxBase, QDateEdit):
|
305
|
+
""" Compact date edit """
|
306
|
+
|