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.
Files changed (107) hide show
  1. fastuiwidgets/__init__.py +12 -0
  2. fastuiwidgets/_rc/__init__.py +0 -0
  3. fastuiwidgets/_rc/resource.py +98835 -0
  4. fastuiwidgets/common/__init__.py +12 -0
  5. fastuiwidgets/common/animation.py +530 -0
  6. fastuiwidgets/common/auto_wrap.py +164 -0
  7. fastuiwidgets/common/color.py +95 -0
  8. fastuiwidgets/common/config.py +423 -0
  9. fastuiwidgets/common/exception_handler.py +31 -0
  10. fastuiwidgets/common/font.py +38 -0
  11. fastuiwidgets/common/icon.py +703 -0
  12. fastuiwidgets/common/image_utils.py +198 -0
  13. fastuiwidgets/common/overload.py +47 -0
  14. fastuiwidgets/common/router.py +133 -0
  15. fastuiwidgets/common/screen.py +25 -0
  16. fastuiwidgets/common/smooth_scroll.py +141 -0
  17. fastuiwidgets/common/style_sheet.py +512 -0
  18. fastuiwidgets/common/theme_listener.py +27 -0
  19. fastuiwidgets/common/translator.py +14 -0
  20. fastuiwidgets/components/__init__.py +6 -0
  21. fastuiwidgets/components/date_time/__init__.py +4 -0
  22. fastuiwidgets/components/date_time/calendar_picker.py +121 -0
  23. fastuiwidgets/components/date_time/calendar_view.py +671 -0
  24. fastuiwidgets/components/date_time/date_picker.py +245 -0
  25. fastuiwidgets/components/date_time/fast_calendar_view.py +487 -0
  26. fastuiwidgets/components/date_time/picker_base.py +632 -0
  27. fastuiwidgets/components/date_time/time_picker.py +223 -0
  28. fastuiwidgets/components/dialog_box/__init__.py +6 -0
  29. fastuiwidgets/components/dialog_box/color_dialog.py +414 -0
  30. fastuiwidgets/components/dialog_box/dialog.py +167 -0
  31. fastuiwidgets/components/dialog_box/folder_list_dialog.py +307 -0
  32. fastuiwidgets/components/dialog_box/mask_dialog_base.py +120 -0
  33. fastuiwidgets/components/dialog_box/message_box_base.py +92 -0
  34. fastuiwidgets/components/dialog_box/message_dialog.py +65 -0
  35. fastuiwidgets/components/layout/__init__.py +3 -0
  36. fastuiwidgets/components/layout/expand_layout.py +96 -0
  37. fastuiwidgets/components/layout/flow_layout.py +236 -0
  38. fastuiwidgets/components/layout/v_box_layout.py +41 -0
  39. fastuiwidgets/components/material/__init__.py +6 -0
  40. fastuiwidgets/components/material/acrylic_combo_box.py +96 -0
  41. fastuiwidgets/components/material/acrylic_flyout.py +105 -0
  42. fastuiwidgets/components/material/acrylic_line_edit.py +27 -0
  43. fastuiwidgets/components/material/acrylic_menu.py +204 -0
  44. fastuiwidgets/components/material/acrylic_tool_tip.py +39 -0
  45. fastuiwidgets/components/material/acrylic_widget.py +42 -0
  46. fastuiwidgets/components/navigation/__init__.py +9 -0
  47. fastuiwidgets/components/navigation/breadcrumb.py +350 -0
  48. fastuiwidgets/components/navigation/navigation_bar.py +416 -0
  49. fastuiwidgets/components/navigation/navigation_interface.py +268 -0
  50. fastuiwidgets/components/navigation/navigation_panel.py +657 -0
  51. fastuiwidgets/components/navigation/navigation_widget.py +686 -0
  52. fastuiwidgets/components/navigation/pivot.py +272 -0
  53. fastuiwidgets/components/navigation/segmented_widget.py +174 -0
  54. fastuiwidgets/components/settings/__init__.py +8 -0
  55. fastuiwidgets/components/settings/custom_color_setting_card.py +139 -0
  56. fastuiwidgets/components/settings/expand_setting_card.py +390 -0
  57. fastuiwidgets/components/settings/folder_list_setting_card.py +134 -0
  58. fastuiwidgets/components/settings/options_setting_card.py +86 -0
  59. fastuiwidgets/components/settings/setting_card.py +449 -0
  60. fastuiwidgets/components/settings/setting_card_group.py +48 -0
  61. fastuiwidgets/components/widgets/__init__.py +41 -0
  62. fastuiwidgets/components/widgets/acrylic_label.py +261 -0
  63. fastuiwidgets/components/widgets/button.py +1059 -0
  64. fastuiwidgets/components/widgets/card_widget.py +369 -0
  65. fastuiwidgets/components/widgets/check_box.py +203 -0
  66. fastuiwidgets/components/widgets/combo_box.py +556 -0
  67. fastuiwidgets/components/widgets/command_bar.py +636 -0
  68. fastuiwidgets/components/widgets/cycle_list_widget.py +251 -0
  69. fastuiwidgets/components/widgets/flip_view.py +430 -0
  70. fastuiwidgets/components/widgets/flyout.py +521 -0
  71. fastuiwidgets/components/widgets/frameless_window.py +49 -0
  72. fastuiwidgets/components/widgets/icon_widget.py +53 -0
  73. fastuiwidgets/components/widgets/info_badge.py +483 -0
  74. fastuiwidgets/components/widgets/info_bar.py +596 -0
  75. fastuiwidgets/components/widgets/label.py +553 -0
  76. fastuiwidgets/components/widgets/line_edit.py +551 -0
  77. fastuiwidgets/components/widgets/list_view.py +158 -0
  78. fastuiwidgets/components/widgets/menu.py +1318 -0
  79. fastuiwidgets/components/widgets/pips_pager.py +331 -0
  80. fastuiwidgets/components/widgets/progress_bar.py +311 -0
  81. fastuiwidgets/components/widgets/progress_ring.py +212 -0
  82. fastuiwidgets/components/widgets/scroll_area.py +125 -0
  83. fastuiwidgets/components/widgets/scroll_bar.py +673 -0
  84. fastuiwidgets/components/widgets/separator.py +43 -0
  85. fastuiwidgets/components/widgets/slider.py +307 -0
  86. fastuiwidgets/components/widgets/spin_box.py +306 -0
  87. fastuiwidgets/components/widgets/stacked_widget.py +211 -0
  88. fastuiwidgets/components/widgets/state_tool_tip.py +188 -0
  89. fastuiwidgets/components/widgets/switch_button.py +312 -0
  90. fastuiwidgets/components/widgets/tab_view.py +804 -0
  91. fastuiwidgets/components/widgets/table_view.py +360 -0
  92. fastuiwidgets/components/widgets/teaching_tip.py +657 -0
  93. fastuiwidgets/components/widgets/tool_tip.py +460 -0
  94. fastuiwidgets/components/widgets/tree_view.py +216 -0
  95. fastuiwidgets/multimedia/__init__.py +3 -0
  96. fastuiwidgets/multimedia/media_play_bar.py +319 -0
  97. fastuiwidgets/multimedia/media_player.py +124 -0
  98. fastuiwidgets/multimedia/video_widget.py +93 -0
  99. fastuiwidgets/window/__init__.py +2 -0
  100. fastuiwidgets/window/fluent_window.py +413 -0
  101. fastuiwidgets/window/splash_screen.py +92 -0
  102. fastuiwidgets/window/stacked_widget.py +66 -0
  103. python_fastui_widgets-1.0.0.dist-info/METADATA +30 -0
  104. python_fastui_widgets-1.0.0.dist-info/RECORD +107 -0
  105. python_fastui_widgets-1.0.0.dist-info/WHEEL +5 -0
  106. python_fastui_widgets-1.0.0.dist-info/licenses/LICENSE +674 -0
  107. 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)