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,331 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from enum import Enum
|
3
|
+
from PySide6.QtCore import Qt, Signal, QModelIndex, QPoint, Property, QSize, QRectF
|
4
|
+
from PySide6.QtGui import QPixmap, QPainter, QColor
|
5
|
+
from PySide6.QtWidgets import (QStyleOptionViewItem, QStyle, QListWidget, QListWidgetItem, QStyledItemDelegate,
|
6
|
+
QToolButton)
|
7
|
+
|
8
|
+
from ...common.overload import singledispatchmethod
|
9
|
+
from ...common.icon import FluentIcon, drawIcon
|
10
|
+
from ...common.style_sheet import isDarkTheme, FluentStyleSheet
|
11
|
+
from .button import ToolButton
|
12
|
+
from .tool_tip import ToolTipFilter, ToolTipPosition
|
13
|
+
from .scroll_bar import SmoothScrollBar
|
14
|
+
|
15
|
+
|
16
|
+
class PipsScrollButtonDisplayMode(Enum):
|
17
|
+
""" Pips pager scroll button display mode """
|
18
|
+
ALWAYS = 0
|
19
|
+
ON_HOVER = 1
|
20
|
+
NEVER = 2
|
21
|
+
|
22
|
+
|
23
|
+
class ScrollButton(ToolButton):
|
24
|
+
""" Scroll button """
|
25
|
+
|
26
|
+
def _postInit(self):
|
27
|
+
self.setFixedSize(12, 12)
|
28
|
+
|
29
|
+
def paintEvent(self, e):
|
30
|
+
painter = QPainter(self)
|
31
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
32
|
+
painter.setPen(Qt.NoPen)
|
33
|
+
|
34
|
+
if isDarkTheme():
|
35
|
+
color = QColor(255, 255, 255)
|
36
|
+
painter.setOpacity(0.773 if self.isHover or self.isPressed else 0.541)
|
37
|
+
else:
|
38
|
+
color = QColor(0, 0, 0)
|
39
|
+
painter.setOpacity(0.616 if self.isHover or self.isPressed else 0.45)
|
40
|
+
|
41
|
+
if self.isPressed:
|
42
|
+
rect = QRectF(3, 3, 6, 6)
|
43
|
+
else:
|
44
|
+
rect = QRectF(2, 2, 8, 8)
|
45
|
+
|
46
|
+
drawIcon(self._icon, painter, rect, fill=color.name())
|
47
|
+
|
48
|
+
|
49
|
+
class PipsDelegate(QStyledItemDelegate):
|
50
|
+
""" Pips delegate """
|
51
|
+
|
52
|
+
def __init__(self, parent=None):
|
53
|
+
super().__init__(parent=parent)
|
54
|
+
self.hoveredRow = -1
|
55
|
+
self.pressedRow = -1
|
56
|
+
|
57
|
+
def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex) -> None:
|
58
|
+
painter.save()
|
59
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
60
|
+
painter.setPen(Qt.NoPen)
|
61
|
+
|
62
|
+
isHover = index.row() == self.hoveredRow
|
63
|
+
isPressed = index.row() == self.pressedRow
|
64
|
+
|
65
|
+
# draw pip
|
66
|
+
if isDarkTheme():
|
67
|
+
if isHover or isPressed:
|
68
|
+
color = QColor(255, 255, 255, 197)
|
69
|
+
else:
|
70
|
+
color = QColor(255, 255, 255, 138)
|
71
|
+
else:
|
72
|
+
if isHover or isPressed:
|
73
|
+
color = QColor(0, 0, 0, 157)
|
74
|
+
else:
|
75
|
+
color = QColor(0, 0, 0, 114)
|
76
|
+
|
77
|
+
painter.setBrush(color)
|
78
|
+
|
79
|
+
if option.state & QStyle.State_Selected or (isHover and not isPressed):
|
80
|
+
r = 3
|
81
|
+
else:
|
82
|
+
r = 2
|
83
|
+
|
84
|
+
x = option.rect.x() + 6 - r
|
85
|
+
y = option.rect.y() + 6 - r
|
86
|
+
painter.drawEllipse(QRectF(x, y, 2*r, 2*r))
|
87
|
+
|
88
|
+
painter.restore()
|
89
|
+
|
90
|
+
def setPressedRow(self, row: int):
|
91
|
+
self.pressedRow = row
|
92
|
+
self.parent().viewport().update()
|
93
|
+
|
94
|
+
def setHoveredRow(self, row: bool):
|
95
|
+
self.hoveredRow = row
|
96
|
+
self.parent().viewport().update()
|
97
|
+
|
98
|
+
|
99
|
+
class PipsPager(QListWidget):
|
100
|
+
""" Pips pager
|
101
|
+
|
102
|
+
Constructors
|
103
|
+
------------
|
104
|
+
* PipsPager(`parent`: QWidget = None)
|
105
|
+
* PipsPager(`orient`: Qt.Orientation, `parent`: QWidget = None)
|
106
|
+
"""
|
107
|
+
|
108
|
+
currentIndexChanged = Signal(int)
|
109
|
+
|
110
|
+
@singledispatchmethod
|
111
|
+
def __init__(self, parent=None):
|
112
|
+
super().__init__(parent=parent)
|
113
|
+
self.orientation = Qt.Horizontal
|
114
|
+
self._postInit()
|
115
|
+
|
116
|
+
@__init__.register
|
117
|
+
def _(self, orientation: Qt.Orientation, parent=None):
|
118
|
+
super().__init__(parent=parent)
|
119
|
+
self.orientation = orientation
|
120
|
+
self._postInit()
|
121
|
+
|
122
|
+
def _postInit(self):
|
123
|
+
self._visibleNumber = 5
|
124
|
+
self.isHover = False
|
125
|
+
|
126
|
+
self.delegate = PipsDelegate(self)
|
127
|
+
self.scrollBar = SmoothScrollBar(self.orientation, self)
|
128
|
+
|
129
|
+
self.scrollBar.setScrollAnimation(500)
|
130
|
+
self.scrollBar.setForceHidden(True)
|
131
|
+
|
132
|
+
self.setMouseTracking(True)
|
133
|
+
self.setUniformItemSizes(True)
|
134
|
+
self.setGridSize(QSize(12, 12))
|
135
|
+
self.setItemDelegate(self.delegate)
|
136
|
+
self.setMovement(QListWidget.Static)
|
137
|
+
self.setVerticalScrollMode(self.ScrollMode.ScrollPerPixel)
|
138
|
+
self.setHorizontalScrollMode(self.ScrollMode.ScrollPerPixel)
|
139
|
+
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
140
|
+
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
141
|
+
|
142
|
+
FluentStyleSheet.PIPS_PAGER.apply(self)
|
143
|
+
|
144
|
+
if self.isHorizontal():
|
145
|
+
self.setFlow(QListWidget.LeftToRight)
|
146
|
+
self.setViewportMargins(15, 0, 15, 0)
|
147
|
+
self.preButton = ScrollButton(FluentIcon.CARE_LEFT_SOLID, self)
|
148
|
+
self.nextButton = ScrollButton(FluentIcon.CARE_RIGHT_SOLID, self)
|
149
|
+
self.setFixedHeight(12)
|
150
|
+
|
151
|
+
self.preButton.installEventFilter(ToolTipFilter(self.preButton, 1000, ToolTipPosition.LEFT))
|
152
|
+
self.nextButton.installEventFilter(ToolTipFilter(self.nextButton, 1000, ToolTipPosition.RIGHT))
|
153
|
+
|
154
|
+
else:
|
155
|
+
self.setViewportMargins(0, 15, 0, 15)
|
156
|
+
self.preButton = ScrollButton(FluentIcon.CARE_UP_SOLID, self)
|
157
|
+
self.nextButton = ScrollButton(FluentIcon.CARE_DOWN_SOLID, self)
|
158
|
+
self.setFixedWidth(12)
|
159
|
+
|
160
|
+
self.preButton.installEventFilter(ToolTipFilter(self.preButton, 1000, ToolTipPosition.TOP))
|
161
|
+
self.nextButton.installEventFilter(ToolTipFilter(self.nextButton, 1000, ToolTipPosition.BOTTOM))
|
162
|
+
|
163
|
+
self.setPreviousButtonDisplayMode(PipsScrollButtonDisplayMode.NEVER)
|
164
|
+
self.setNextButtonDisplayMode(PipsScrollButtonDisplayMode.NEVER)
|
165
|
+
self.preButton.setToolTip(self.tr('Previous Page'))
|
166
|
+
self.nextButton.setToolTip(self.tr('Next Page'))
|
167
|
+
|
168
|
+
# connect signal to slot
|
169
|
+
self.preButton.clicked.connect(self.scrollPrevious)
|
170
|
+
self.nextButton.clicked.connect(self.scrollNext)
|
171
|
+
self.itemPressed.connect(self._setPressedItem)
|
172
|
+
self.itemEntered.connect(self._setHoveredItem)
|
173
|
+
|
174
|
+
def _setPressedItem(self, item: QListWidgetItem):
|
175
|
+
self.delegate.setPressedRow(self.row(item))
|
176
|
+
self.setCurrentIndex(self.row(item))
|
177
|
+
|
178
|
+
def _setHoveredItem(self, item: QListWidgetItem):
|
179
|
+
self.delegate.setHoveredRow(self.row(item))
|
180
|
+
|
181
|
+
def setPageNumber(self, n: int):
|
182
|
+
""" set the number of page """
|
183
|
+
self.clear()
|
184
|
+
self.addItems(['15555'] * n)
|
185
|
+
|
186
|
+
for i in range(n):
|
187
|
+
item = self.item(i)
|
188
|
+
item.setData(Qt.UserRole, i + 1)
|
189
|
+
item.setSizeHint(self.gridSize())
|
190
|
+
|
191
|
+
self.setCurrentIndex(0)
|
192
|
+
self.adjustSize()
|
193
|
+
|
194
|
+
def getPageNumber(self):
|
195
|
+
""" get the number of page """
|
196
|
+
return self.count()
|
197
|
+
|
198
|
+
def getVisibleNumber(self):
|
199
|
+
""" get the number of visible pips """
|
200
|
+
return self._visibleNumber
|
201
|
+
|
202
|
+
def setVisibleNumber(self, n: int):
|
203
|
+
self._visibleNumber = n
|
204
|
+
self.adjustSize()
|
205
|
+
|
206
|
+
def scrollNext(self):
|
207
|
+
""" scroll down an item """
|
208
|
+
self.setCurrentIndex(self.currentIndex() + 1)
|
209
|
+
|
210
|
+
def scrollPrevious(self):
|
211
|
+
""" scroll up an item """
|
212
|
+
self.setCurrentIndex(self.currentIndex() - 1)
|
213
|
+
|
214
|
+
def scrollToItem(self, item: QListWidgetItem, hint=QListWidget.PositionAtCenter):
|
215
|
+
""" scroll to item """
|
216
|
+
# scroll to center position
|
217
|
+
index = self.row(item)
|
218
|
+
size = item.sizeHint()
|
219
|
+
s = size.width() if self.isHorizontal() else size.height()
|
220
|
+
self.scrollBar.scrollTo(s * (index - self.visibleNumber // 2))
|
221
|
+
|
222
|
+
# clear selection
|
223
|
+
self.clearSelection()
|
224
|
+
item.setSelected(False)
|
225
|
+
|
226
|
+
self.currentIndexChanged.emit(index)
|
227
|
+
|
228
|
+
def adjustSize(self) -> None:
|
229
|
+
m = self.viewportMargins()
|
230
|
+
|
231
|
+
if self.isHorizontal():
|
232
|
+
w = self.visibleNumber * self.gridSize().width() + m.left() + m.right()
|
233
|
+
self.setFixedWidth(w)
|
234
|
+
else:
|
235
|
+
h = self.visibleNumber * self.gridSize().height() + m.top() + m.bottom()
|
236
|
+
self.setFixedHeight(h)
|
237
|
+
|
238
|
+
def isHorizontal(self):
|
239
|
+
return self.orientation == Qt.Horizontal
|
240
|
+
|
241
|
+
def setCurrentIndex(self, index: int):
|
242
|
+
""" set current index """
|
243
|
+
if not 0 <= index < self.count():
|
244
|
+
return
|
245
|
+
|
246
|
+
item = self.item(index)
|
247
|
+
self.scrollToItem(item)
|
248
|
+
super().setCurrentItem(item)
|
249
|
+
|
250
|
+
self._updateScrollButtonVisibility()
|
251
|
+
|
252
|
+
def isPreviousButtonVisible(self):
|
253
|
+
if self.currentIndex() <= 0 or self.previousButtonDisplayMode == PipsScrollButtonDisplayMode.NEVER:
|
254
|
+
return False
|
255
|
+
|
256
|
+
if self.previousButtonDisplayMode == PipsScrollButtonDisplayMode.ON_HOVER:
|
257
|
+
return self.isHover
|
258
|
+
|
259
|
+
return True
|
260
|
+
|
261
|
+
def isNextButtonVisible(self):
|
262
|
+
if self.currentIndex() >= self.count() - 1 or self.nextButtonDisplayMode == PipsScrollButtonDisplayMode.NEVER:
|
263
|
+
return False
|
264
|
+
|
265
|
+
if self.nextButtonDisplayMode == PipsScrollButtonDisplayMode.ON_HOVER:
|
266
|
+
return self.isHover
|
267
|
+
|
268
|
+
return True
|
269
|
+
|
270
|
+
def currentIndex(self):
|
271
|
+
return super().currentIndex().row()
|
272
|
+
|
273
|
+
def setPreviousButtonDisplayMode(self, mode: PipsScrollButtonDisplayMode):
|
274
|
+
""" set the display mode of previous button """
|
275
|
+
self.previousButtonDisplayMode = mode
|
276
|
+
self.preButton.setVisible(self.isPreviousButtonVisible())
|
277
|
+
|
278
|
+
def setNextButtonDisplayMode(self, mode: PipsScrollButtonDisplayMode):
|
279
|
+
""" set the display mode of next button """
|
280
|
+
self.nextButtonDisplayMode = mode
|
281
|
+
self.nextButton.setVisible(self.isNextButtonVisible())
|
282
|
+
|
283
|
+
def mouseReleaseEvent(self, e):
|
284
|
+
super().mouseReleaseEvent(e)
|
285
|
+
self.delegate.setPressedRow(-1)
|
286
|
+
|
287
|
+
def enterEvent(self, e):
|
288
|
+
super().enterEvent(e)
|
289
|
+
self.isHover = True
|
290
|
+
self._updateScrollButtonVisibility()
|
291
|
+
|
292
|
+
def leaveEvent(self, e):
|
293
|
+
super().leaveEvent(e)
|
294
|
+
self.isHover = False
|
295
|
+
self.delegate.setHoveredRow(-1)
|
296
|
+
self._updateScrollButtonVisibility()
|
297
|
+
|
298
|
+
def _updateScrollButtonVisibility(self):
|
299
|
+
self.preButton.setVisible(self.isPreviousButtonVisible())
|
300
|
+
self.nextButton.setVisible(self.isNextButtonVisible())
|
301
|
+
|
302
|
+
def wheelEvent(self, e):
|
303
|
+
pass
|
304
|
+
|
305
|
+
def resizeEvent(self, e):
|
306
|
+
w, h = self.width(), self.height()
|
307
|
+
bw, bh = self.preButton.width(), self.preButton.height()
|
308
|
+
|
309
|
+
if self.isHorizontal():
|
310
|
+
self.preButton.move(0, int(h/2 - bh/2))
|
311
|
+
self.nextButton.move(w - bw, int(h/2 - bh/2))
|
312
|
+
else:
|
313
|
+
self.preButton.move(int(w/2-bw/2), 0)
|
314
|
+
self.nextButton.move(int(w/2-bw/2), h-bh)
|
315
|
+
|
316
|
+
visibleNumber = Property(int, getVisibleNumber, setVisibleNumber)
|
317
|
+
pageNumber = Property(int, getPageNumber, setPageNumber)
|
318
|
+
|
319
|
+
|
320
|
+
class HorizontalPipsPager(PipsPager):
|
321
|
+
""" Horizontal pips pager """
|
322
|
+
|
323
|
+
def __init__(self, parent=None):
|
324
|
+
super().__init__(Qt.Horizontal, parent)
|
325
|
+
|
326
|
+
|
327
|
+
class VerticalPipsPager(PipsPager):
|
328
|
+
""" Vertical pips pager """
|
329
|
+
|
330
|
+
def __init__(self, parent=None):
|
331
|
+
super().__init__(Qt.Vertical, parent)
|
@@ -0,0 +1,311 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from math import floor
|
3
|
+
|
4
|
+
from PySide6.QtCore import (QEasingCurve, Qt, QPropertyAnimation, Property,
|
5
|
+
QParallelAnimationGroup, QSequentialAnimationGroup, QLocale)
|
6
|
+
from PySide6.QtGui import QPainter, QColor
|
7
|
+
from PySide6.QtWidgets import QProgressBar
|
8
|
+
|
9
|
+
from ...common.style_sheet import themeColor, isDarkTheme
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
class ProgressBar(QProgressBar):
|
14
|
+
|
15
|
+
def __init__(self, parent=None, useAni=True):
|
16
|
+
super().__init__(parent)
|
17
|
+
self._val = 0
|
18
|
+
self.setFixedHeight(4)
|
19
|
+
|
20
|
+
self._useAni = useAni
|
21
|
+
self.lightBackgroundColor = QColor(0, 0, 0, 155)
|
22
|
+
self.darkBackgroundColor = QColor(255, 255, 255, 155)
|
23
|
+
self._lightBarColor = QColor()
|
24
|
+
self._darkBarColor = QColor()
|
25
|
+
self.ani = QPropertyAnimation(self, b'val', self)
|
26
|
+
|
27
|
+
self._isPaused = False
|
28
|
+
self._isError = False
|
29
|
+
self.valueChanged.connect(self._onValueChanged)
|
30
|
+
self.setValue(0)
|
31
|
+
|
32
|
+
def getVal(self):
|
33
|
+
return self._val
|
34
|
+
|
35
|
+
def setVal(self, v: float):
|
36
|
+
self._val = v
|
37
|
+
self.update()
|
38
|
+
|
39
|
+
def isUseAni(self):
|
40
|
+
return self._useAni
|
41
|
+
|
42
|
+
def setUseAni(self, isUSe: bool):
|
43
|
+
self._useAni = isUSe
|
44
|
+
|
45
|
+
def _onValueChanged(self, value):
|
46
|
+
if not self.useAni:
|
47
|
+
self._val = value
|
48
|
+
return
|
49
|
+
|
50
|
+
self.ani.stop()
|
51
|
+
self.ani.setEndValue(value)
|
52
|
+
self.ani.setDuration(150)
|
53
|
+
self.ani.start()
|
54
|
+
super().setValue(value)
|
55
|
+
|
56
|
+
def lightBarColor(self):
|
57
|
+
return self._lightBarColor if self._lightBarColor.isValid() else themeColor()
|
58
|
+
|
59
|
+
def darkBarColor(self):
|
60
|
+
return self._darkBarColor if self._darkBarColor.isValid() else themeColor()
|
61
|
+
|
62
|
+
def setCustomBarColor(self, light, dark):
|
63
|
+
""" set the custom bar color
|
64
|
+
|
65
|
+
Parameters
|
66
|
+
----------
|
67
|
+
light, dark: str | Qt.GlobalColor | QColor
|
68
|
+
bar color in light/dark theme mode
|
69
|
+
"""
|
70
|
+
self._lightBarColor = QColor(light)
|
71
|
+
self._darkBarColor = QColor(dark)
|
72
|
+
self.update()
|
73
|
+
|
74
|
+
def setCustomBackgroundColor(self, light, dark):
|
75
|
+
""" set the custom background color
|
76
|
+
|
77
|
+
Parameters
|
78
|
+
----------
|
79
|
+
light, dark: str | Qt.GlobalColor | QColor
|
80
|
+
background color in light/dark theme mode
|
81
|
+
"""
|
82
|
+
self.lightBackgroundColor = QColor(light)
|
83
|
+
self.darkBackgroundColor = QColor(dark)
|
84
|
+
self.update()
|
85
|
+
|
86
|
+
def resume(self):
|
87
|
+
self._isPaused = False
|
88
|
+
self._isError = False
|
89
|
+
self.update()
|
90
|
+
|
91
|
+
def pause(self):
|
92
|
+
self._isPaused = True
|
93
|
+
self.update()
|
94
|
+
|
95
|
+
def setPaused(self, isPaused: bool):
|
96
|
+
self._isPaused = isPaused
|
97
|
+
self.update()
|
98
|
+
|
99
|
+
def isPaused(self):
|
100
|
+
return self._isPaused
|
101
|
+
|
102
|
+
def error(self):
|
103
|
+
self._isError = True
|
104
|
+
self.update()
|
105
|
+
|
106
|
+
def setError(self, isError: bool):
|
107
|
+
self._isError = isError
|
108
|
+
if isError:
|
109
|
+
self.error()
|
110
|
+
else:
|
111
|
+
self.resume()
|
112
|
+
|
113
|
+
def isError(self):
|
114
|
+
return self._isError
|
115
|
+
|
116
|
+
def barColor(self):
|
117
|
+
if self.isPaused():
|
118
|
+
return QColor(252, 225, 0) if isDarkTheme() else QColor(157, 93, 0)
|
119
|
+
|
120
|
+
if self.isError():
|
121
|
+
return QColor(255, 153, 164) if isDarkTheme() else QColor(196, 43, 28)
|
122
|
+
|
123
|
+
return self.darkBarColor() if isDarkTheme() else self.lightBarColor()
|
124
|
+
|
125
|
+
def valText(self):
|
126
|
+
if self.maximum() <= self.minimum():
|
127
|
+
return ""
|
128
|
+
|
129
|
+
total = self.maximum() - self.minimum()
|
130
|
+
result = self.format()
|
131
|
+
locale = self.locale()
|
132
|
+
locale.setNumberOptions(locale.numberOptions()
|
133
|
+
| QLocale.OmitGroupSeparator)
|
134
|
+
result = result.replace("%m", locale.toString(total))
|
135
|
+
result = result.replace("%v", locale.toString(self.val))
|
136
|
+
|
137
|
+
if total == 0:
|
138
|
+
return result.replace("%p", locale.toString(100))
|
139
|
+
|
140
|
+
progress = int((self.val - self.minimum()) * 100 / total)
|
141
|
+
return result.replace("%p", locale.toString(progress))
|
142
|
+
|
143
|
+
def paintEvent(self, e):
|
144
|
+
painter = QPainter(self)
|
145
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
146
|
+
|
147
|
+
# draw background
|
148
|
+
bc = self.darkBackgroundColor if isDarkTheme() else self.lightBackgroundColor
|
149
|
+
painter.setPen(bc)
|
150
|
+
y = floor(self.height() / 2)
|
151
|
+
painter.drawLine(0, y, self.width(), y)
|
152
|
+
|
153
|
+
if self.minimum() >= self.maximum():
|
154
|
+
return
|
155
|
+
|
156
|
+
# draw bar
|
157
|
+
painter.setPen(Qt.NoPen)
|
158
|
+
painter.setBrush(self.barColor())
|
159
|
+
w = int(self.val / (self.maximum() - self.minimum()) * self.width())
|
160
|
+
r = self.height() / 2
|
161
|
+
painter.drawRoundedRect(0, 0, w, self.height(), r, r)
|
162
|
+
|
163
|
+
useAni = Property(bool, isUseAni, setUseAni)
|
164
|
+
val = Property(float, getVal, setVal)
|
165
|
+
|
166
|
+
|
167
|
+
class IndeterminateProgressBar(QProgressBar):
|
168
|
+
""" Indeterminate progress bar """
|
169
|
+
|
170
|
+
def __init__(self, parent=None, start=True):
|
171
|
+
super().__init__(parent=parent)
|
172
|
+
self._shortPos = 0
|
173
|
+
self._longPos = 0
|
174
|
+
self.shortBarAni = QPropertyAnimation(self, b'shortPos', self)
|
175
|
+
self.longBarAni = QPropertyAnimation(self, b'longPos', self)
|
176
|
+
|
177
|
+
self._lightBarColor = QColor()
|
178
|
+
self._darkBarColor = QColor()
|
179
|
+
|
180
|
+
self._isError = False
|
181
|
+
|
182
|
+
self.aniGroup = QParallelAnimationGroup(self)
|
183
|
+
self.longBarAniGroup = QSequentialAnimationGroup(self)
|
184
|
+
|
185
|
+
self.shortBarAni.setDuration(833)
|
186
|
+
self.longBarAni.setDuration(1167)
|
187
|
+
self.shortBarAni.setStartValue(0)
|
188
|
+
self.longBarAni.setStartValue(0)
|
189
|
+
self.shortBarAni.setEndValue(1.45)
|
190
|
+
self.longBarAni.setEndValue(1.75)
|
191
|
+
self.longBarAni.setEasingCurve(QEasingCurve.OutQuad)
|
192
|
+
|
193
|
+
self.aniGroup.addAnimation(self.shortBarAni)
|
194
|
+
self.longBarAniGroup.addPause(785)
|
195
|
+
self.longBarAniGroup.addAnimation(self.longBarAni)
|
196
|
+
self.aniGroup.addAnimation(self.longBarAniGroup)
|
197
|
+
self.aniGroup.setLoopCount(-1)
|
198
|
+
|
199
|
+
self.setFixedHeight(4)
|
200
|
+
|
201
|
+
if start:
|
202
|
+
self.start()
|
203
|
+
|
204
|
+
def lightBarColor(self):
|
205
|
+
return self._lightBarColor if self._lightBarColor.isValid() else themeColor()
|
206
|
+
|
207
|
+
def darkBarColor(self):
|
208
|
+
return self._darkBarColor if self._darkBarColor.isValid() else themeColor()
|
209
|
+
|
210
|
+
def setCustomBarColor(self, light, dark):
|
211
|
+
""" set the custom bar color
|
212
|
+
|
213
|
+
Parameters
|
214
|
+
----------
|
215
|
+
light, dark: str | Qt.GlobalColor | QColor
|
216
|
+
bar color in light/dark theme mode
|
217
|
+
"""
|
218
|
+
self._lightBarColor = QColor(light)
|
219
|
+
self._darkBarColor = QColor(dark)
|
220
|
+
self.update()
|
221
|
+
|
222
|
+
@Property(float)
|
223
|
+
def shortPos(self):
|
224
|
+
return self._shortPos
|
225
|
+
|
226
|
+
@shortPos.setter
|
227
|
+
def shortPos(self, p):
|
228
|
+
self._shortPos = p
|
229
|
+
self.update()
|
230
|
+
|
231
|
+
@Property(float)
|
232
|
+
def longPos(self):
|
233
|
+
return self._longPos
|
234
|
+
|
235
|
+
@longPos.setter
|
236
|
+
def longPos(self, p):
|
237
|
+
self._longPos = p
|
238
|
+
self.update()
|
239
|
+
|
240
|
+
def start(self):
|
241
|
+
self.shortPos = 0
|
242
|
+
self.longPos = 0
|
243
|
+
self.aniGroup.start()
|
244
|
+
self.update()
|
245
|
+
|
246
|
+
def stop(self):
|
247
|
+
self.aniGroup.stop()
|
248
|
+
self.shortPos = 0
|
249
|
+
self.longPos = 0
|
250
|
+
self.update()
|
251
|
+
|
252
|
+
def isStarted(self):
|
253
|
+
return self.aniGroup.state() == QParallelAnimationGroup.Running
|
254
|
+
|
255
|
+
def pause(self):
|
256
|
+
self.aniGroup.pause()
|
257
|
+
self.update()
|
258
|
+
|
259
|
+
def resume(self):
|
260
|
+
self.aniGroup.resume()
|
261
|
+
self.update()
|
262
|
+
|
263
|
+
def setPaused(self, isPaused: bool):
|
264
|
+
self.aniGroup.setPaused(isPaused)
|
265
|
+
self.update()
|
266
|
+
|
267
|
+
def isPaused(self):
|
268
|
+
return self.aniGroup.state() == QParallelAnimationGroup.Paused
|
269
|
+
|
270
|
+
def error(self):
|
271
|
+
self._isError = True
|
272
|
+
self.aniGroup.stop()
|
273
|
+
self.update()
|
274
|
+
|
275
|
+
def setError(self, isError: bool):
|
276
|
+
self._isError = isError
|
277
|
+
if isError:
|
278
|
+
self.error()
|
279
|
+
else:
|
280
|
+
self.start()
|
281
|
+
|
282
|
+
def isError(self):
|
283
|
+
return self._isError
|
284
|
+
|
285
|
+
def barColor(self):
|
286
|
+
if self.isError():
|
287
|
+
return QColor(255, 153, 164) if isDarkTheme() else QColor(196, 43, 28)
|
288
|
+
|
289
|
+
if self.isPaused():
|
290
|
+
return QColor(252, 225, 0) if isDarkTheme() else QColor(157, 93, 0)
|
291
|
+
|
292
|
+
return self.darkBarColor() if isDarkTheme() else self.lightBarColor()
|
293
|
+
|
294
|
+
def paintEvent(self, e):
|
295
|
+
painter = QPainter(self)
|
296
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
297
|
+
|
298
|
+
painter.setPen(Qt.NoPen)
|
299
|
+
painter.setBrush(self.barColor())
|
300
|
+
|
301
|
+
# draw short bar
|
302
|
+
x = int((self.shortPos - 0.4) * self.width())
|
303
|
+
w = int(0.4 * self.width())
|
304
|
+
r = self.height() / 2
|
305
|
+
painter.drawRoundedRect(x, 0, w, self.height(), r, r)
|
306
|
+
|
307
|
+
# draw long bar
|
308
|
+
x = int((self.longPos - 0.6) * self.width())
|
309
|
+
w = int(0.6 * self.width())
|
310
|
+
r = self.height() / 2
|
311
|
+
painter.drawRoundedRect(x, 0, w, self.height(), r, r)
|