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,673 @@
1
+ # coding:utf-8
2
+ from enum import Enum
3
+ from PySide6.QtCore import (QEvent, QEasingCurve, Qt, Signal, QPropertyAnimation, Property, QRectF,
4
+ QTimer, QPoint, QObject)
5
+ from PySide6.QtGui import QPainter, QColor, QMouseEvent
6
+ from PySide6.QtWidgets import (QWidget, QToolButton, QAbstractScrollArea, QGraphicsOpacityEffect,
7
+ QHBoxLayout, QVBoxLayout, QApplication, QAbstractItemView, QListView)
8
+
9
+ from ...common.icon import FluentIcon
10
+ from ...common.style_sheet import isDarkTheme
11
+ from ...common.smooth_scroll import SmoothScroll
12
+
13
+ class ArrowButton(QToolButton):
14
+ """ Arrow button """
15
+
16
+ def __init__(self, icon: FluentIcon, parent=None):
17
+ super().__init__(parent=parent)
18
+ self.setFixedSize(10, 10)
19
+ self.lightColor = QColor(0, 0, 0, 114)
20
+ self.darkColor = QColor(255, 255, 255, 139)
21
+ self._icon = icon
22
+ self.opacity = 1
23
+
24
+ def setOpacity(self, opacity):
25
+ self.opacity = opacity
26
+ self.update()
27
+
28
+ def setLightColor(self, color):
29
+ self.lightColor = QColor(color)
30
+ self.update()
31
+
32
+ def setDarkColor(self, color):
33
+ self.darkColor = QColor(color)
34
+ self.update()
35
+
36
+ def paintEvent(self, e):
37
+ painter = QPainter(self)
38
+ painter.setRenderHints(QPainter.Antialiasing)
39
+
40
+ color = self.darkColor if isDarkTheme() else self.lightColor
41
+ painter.setOpacity(self.opacity * color.alpha() / 255)
42
+
43
+ s = 7 if self.isDown() else 8
44
+ x = (self.width() - s) / 2
45
+ self._icon.render(painter, QRectF(x, x, s, s), fill=color.name())
46
+
47
+
48
+ class ScrollBarGroove(QWidget):
49
+ """ Scroll bar groove """
50
+
51
+ def __init__(self, orient: Qt.Orientation, parent):
52
+ super().__init__(parent=parent)
53
+ self._opacity = 1
54
+ self.lightBackgroundColor = QColor(252, 252, 252, 217)
55
+ self.darkBackgroundColor = QColor(44, 44, 44, 245)
56
+
57
+ if orient == Qt.Vertical:
58
+ self.setFixedWidth(12)
59
+ self.upButton = ArrowButton(FluentIcon.CARE_UP_SOLID, self)
60
+ self.downButton = ArrowButton(FluentIcon.CARE_DOWN_SOLID, self)
61
+ self.setLayout(QVBoxLayout(self))
62
+ self.layout().addWidget(self.upButton, 0, Qt.AlignHCenter)
63
+ self.layout().addStretch(1)
64
+ self.layout().addWidget(self.downButton, 0, Qt.AlignHCenter)
65
+ self.layout().setContentsMargins(0, 3, 0, 3)
66
+ else:
67
+ self.setFixedHeight(12)
68
+ self.upButton = ArrowButton(FluentIcon.CARE_LEFT_SOLID, self)
69
+ self.downButton = ArrowButton(FluentIcon.CARE_RIGHT_SOLID, self)
70
+ self.setLayout(QHBoxLayout(self))
71
+ self.layout().addWidget(self.upButton, 0, Qt.AlignVCenter)
72
+ self.layout().addStretch(1)
73
+ self.layout().addWidget(self.downButton, 0, Qt.AlignVCenter)
74
+ self.layout().setContentsMargins(3, 0, 3, 0)
75
+
76
+ self.opacityAni = QPropertyAnimation(self, b'opacity', self)
77
+ self.setOpacity(0)
78
+
79
+ def setLightBackgroundColor(self, color):
80
+ self.lightBackgroundColor = QColor(color)
81
+ self.update()
82
+
83
+ def setDarkBackgroundColor(self, color):
84
+ self.darkBackgroundColor = QColor(color)
85
+ self.update()
86
+
87
+ def fadeIn(self):
88
+ self.opacityAni.stop()
89
+ self.opacityAni.setStartValue(self.opacity)
90
+ self.opacityAni.setEndValue(1)
91
+ self.opacityAni.setDuration(150)
92
+ self.opacityAni.start()
93
+
94
+ def fadeOut(self):
95
+ self.opacityAni.stop()
96
+ self.opacityAni.setStartValue(self.opacity)
97
+ self.opacityAni.setEndValue(0)
98
+ self.opacityAni.setDuration(150)
99
+ self.opacityAni.start()
100
+
101
+ def paintEvent(self, e):
102
+ painter = QPainter(self)
103
+ painter.setRenderHints(QPainter.Antialiasing)
104
+ painter.setOpacity(self.opacity)
105
+ painter.setPen(Qt.NoPen)
106
+
107
+ painter.setBrush(self.darkBackgroundColor if isDarkTheme() else self.lightBackgroundColor)
108
+ painter.drawRoundedRect(self.rect(), 6, 6)
109
+
110
+ def setOpacity(self, opacity: float):
111
+ self._opacity = opacity
112
+ self.upButton.setOpacity(opacity)
113
+ self.downButton.setOpacity(opacity)
114
+ self.update()
115
+
116
+ def getOpacity(self) -> float:
117
+ return self._opacity
118
+
119
+ opacity = Property(float, getOpacity, setOpacity)
120
+
121
+
122
+ class ScrollBarHandle(QWidget):
123
+ """ Scroll bar handle """
124
+
125
+ def __init__(self, orient: Qt.Orientation, parent=None):
126
+ super().__init__(parent)
127
+ self._opacity = 1
128
+ self.opacityAni = QPropertyAnimation(self, b'opacity', self)
129
+ self.lightColor = QColor(0, 0, 0, 114)
130
+ self.darkColor = QColor(255, 255, 255, 139)
131
+ self.orient = orient
132
+ if orient == Qt.Vertical:
133
+ self.setFixedWidth(3)
134
+ else:
135
+ self.setFixedHeight(3)
136
+
137
+ def setLightColor(self, color):
138
+ self.lightColor = QColor(color)
139
+ self.update()
140
+
141
+ def setDarkColor(self, color):
142
+ self.darkColor = QColor(color)
143
+ self.update()
144
+
145
+ def paintEvent(self, e):
146
+ painter = QPainter(self)
147
+ painter.setRenderHints(QPainter.Antialiasing)
148
+ painter.setPen(Qt.NoPen)
149
+
150
+ r = self.width() / 2 if self.orient == Qt.Vertical else self.height() / 2
151
+ painter.setOpacity(self.opacity)
152
+ painter.setBrush(self.darkColor if isDarkTheme() else self.lightColor)
153
+ painter.drawRoundedRect(self.rect(), r, r)
154
+
155
+ def fadeIn(self):
156
+ self.opacityAni.stop()
157
+ self.opacityAni.setStartValue(self.opacity)
158
+ self.opacityAni.setEndValue(1)
159
+ self.opacityAni.setDuration(150)
160
+ self.opacityAni.start()
161
+
162
+ def fadeOut(self):
163
+ self.opacityAni.stop()
164
+ self.opacityAni.setStartValue(self.opacity)
165
+ self.opacityAni.setEndValue(0)
166
+ self.opacityAni.setDuration(150)
167
+ self.opacityAni.start()
168
+
169
+ def setOpacity(self, opacity: float):
170
+ self._opacity = opacity
171
+ self.update()
172
+
173
+ def getOpacity(self) -> float:
174
+ return self._opacity
175
+
176
+ opacity = Property(float, getOpacity, setOpacity)
177
+
178
+
179
+ class ScrollBarHandleDisplayMode(Enum):
180
+ """Scroll bar handle display mode"""
181
+
182
+ ALWAYS = 0
183
+ ON_HOVER = 1
184
+
185
+
186
+ class ScrollBar(QWidget):
187
+ """ Fluent scroll bar """
188
+
189
+ rangeChanged = Signal(tuple)
190
+ valueChanged = Signal(int)
191
+ sliderPressed = Signal()
192
+ sliderReleased = Signal()
193
+ sliderMoved = Signal()
194
+
195
+ def __init__(self, orient: Qt.Orientation, parent: QAbstractScrollArea):
196
+ super().__init__(parent)
197
+ self.groove = ScrollBarGroove(orient, self)
198
+ self.handle = ScrollBarHandle(orient, self)
199
+ self.timer = QTimer(self)
200
+
201
+ self._orientation = orient
202
+ self._singleStep = 1
203
+ self._pageStep = 50
204
+ self._padding = 14
205
+
206
+ self._minimum = 0
207
+ self._maximum = 0
208
+ self._value = 0
209
+
210
+ self._isPressed = False
211
+ self._isEnter = False
212
+ self._isExpanded = False
213
+ self._pressedPos = QPoint()
214
+ self._isForceHidden = False
215
+ self.handleDisplayMode = ScrollBarHandleDisplayMode.ALWAYS
216
+
217
+ if orient == Qt.Vertical:
218
+ self.partnerBar = parent.verticalScrollBar()
219
+ QAbstractScrollArea.setVerticalScrollBarPolicy(parent, Qt.ScrollBarAlwaysOff)
220
+ else:
221
+ self.partnerBar = parent.horizontalScrollBar()
222
+ QAbstractScrollArea.setHorizontalScrollBarPolicy(parent, Qt.ScrollBarAlwaysOff)
223
+
224
+ self.__initWidget(parent)
225
+
226
+ def __initWidget(self, parent):
227
+ self.groove.upButton.clicked.connect(self._onPageUp)
228
+ self.groove.downButton.clicked.connect(self._onPageDown)
229
+ self.groove.opacityAni.valueChanged.connect(self._onOpacityAniValueChanged)
230
+
231
+ self.partnerBar.rangeChanged.connect(self.setRange)
232
+ self.partnerBar.valueChanged.connect(self._onValueChanged)
233
+ self.valueChanged.connect(self.partnerBar.setValue)
234
+
235
+ parent.installEventFilter(self)
236
+
237
+ self.setRange(self.partnerBar.minimum(), self.partnerBar.maximum())
238
+ self.setVisible(self.maximum() > 0 and not self._isForceHidden)
239
+ self._adjustPos(self.parent().size())
240
+
241
+ def _onPageUp(self):
242
+ self.setValue(self.value() - self.pageStep())
243
+
244
+ def _onPageDown(self):
245
+ self.setValue(self.value() + self.pageStep())
246
+
247
+ def _onValueChanged(self, value):
248
+ self.val = value
249
+
250
+ def value(self):
251
+ return self._value
252
+
253
+ @Property(int, notify=valueChanged)
254
+ def val(self):
255
+ return self._value
256
+
257
+ @val.setter
258
+ def val(self, value: int):
259
+ if value == self.value():
260
+ return
261
+
262
+ value = max(self.minimum(), min(value, self.maximum()))
263
+ self._value = value
264
+ self.valueChanged.emit(value)
265
+
266
+ # adjust the position of handle
267
+ self._adjustHandlePos()
268
+
269
+ def minimum(self):
270
+ return self._minimum
271
+
272
+ def maximum(self):
273
+ return self._maximum
274
+
275
+ def orientation(self):
276
+ return self._orientation
277
+
278
+ def pageStep(self):
279
+ return self._pageStep
280
+
281
+ def singleStep(self):
282
+ return self._singleStep
283
+
284
+ def isSliderDown(self):
285
+ return self._isPressed
286
+
287
+ def setValue(self, value: int):
288
+ self.val = value
289
+
290
+ def setMinimum(self, min: int):
291
+ if min == self.minimum():
292
+ return
293
+
294
+ self._minimum = min
295
+ self.rangeChanged.emit((min, self.maximum()))
296
+
297
+ def setMaximum(self, max: int):
298
+ if max == self.maximum():
299
+ return
300
+
301
+ self._maximum = max
302
+ self.rangeChanged.emit((self.minimum(), max))
303
+
304
+ def setRange(self, min: int, max: int):
305
+ if min > max or (min == self.minimum() and max == self.maximum()):
306
+ return
307
+
308
+ self.setMinimum(min)
309
+ self.setMaximum(max)
310
+
311
+ self._adjustHandleSize()
312
+ self._adjustHandlePos()
313
+ self.setVisible(max > 0 and not self._isForceHidden)
314
+
315
+ self.rangeChanged.emit((min, max))
316
+
317
+ def setPageStep(self, step: int):
318
+ if step >= 1:
319
+ self._pageStep = step
320
+
321
+ def setSingleStep(self, step: int):
322
+ if step >= 1:
323
+ self._singleStep = step
324
+
325
+ def setSliderDown(self, isDown: bool):
326
+ self._isPressed = True
327
+ if isDown:
328
+ self.sliderPressed.emit()
329
+ else:
330
+ self.sliderReleased.emit()
331
+
332
+ def setHandleColor(self, light, dark):
333
+ """set the color of handle
334
+
335
+ Parameters
336
+ ----------
337
+ light, dark: QColor | str | Qt.GlobalColor
338
+ the color in light/dark theme mode
339
+ """
340
+ self.handle.setLightColor(light)
341
+ self.handle.setDarkColor(dark)
342
+
343
+ def setArrowColor(self, light, dark):
344
+ """set the color of arrow button
345
+
346
+ Parameters
347
+ ----------
348
+ light, dark: QColor | str | Qt.GlobalColor
349
+ the color in light/dark theme mode
350
+ """
351
+ self.groove.upButton.setLightColor(light)
352
+ self.groove.upButton.setDarkColor(dark)
353
+ self.groove.downButton.setLightColor(light)
354
+ self.groove.downButton.setDarkColor(dark)
355
+
356
+ def setGrooveColor(self, light, dark):
357
+ """set the color of groove
358
+
359
+ Parameters
360
+ ----------
361
+ light, dark: QColor | str | Qt.GlobalColor
362
+ the color in light/dark theme mode
363
+ """
364
+ self.groove.setLightBackgroundColor(light)
365
+ self.groove.setDarkBackgroundColor(dark)
366
+
367
+ def setHandleDisplayMode(self, mode: ScrollBarHandleDisplayMode):
368
+ """set the display mode of handle"""
369
+ if mode == self.handleDisplayMode:
370
+ return
371
+
372
+ self.handleDisplayMode = mode
373
+ if mode == ScrollBarHandleDisplayMode.ON_HOVER and not self._isEnter:
374
+ self.handle.fadeOut()
375
+ elif mode == ScrollBarHandleDisplayMode.ALWAYS:
376
+ self.handle.fadeIn()
377
+
378
+ def expand(self):
379
+ """ expand scroll bar """
380
+ if self._isExpanded or not self._isEnter:
381
+ return
382
+
383
+ self._isExpanded = True
384
+ self.groove.fadeIn()
385
+ self.handle.fadeIn()
386
+
387
+ def collapse(self):
388
+ """ collapse scroll bar """
389
+ if not self._isExpanded or self._isEnter:
390
+ return
391
+
392
+ self._isExpanded = False
393
+ self.groove.fadeOut()
394
+
395
+ if self.handleDisplayMode == ScrollBarHandleDisplayMode.ON_HOVER:
396
+ self.handle.fadeOut()
397
+
398
+ def enterEvent(self, e):
399
+ self._isEnter = True
400
+ self.timer.stop()
401
+ self.timer.singleShot(200, self.expand)
402
+
403
+ def leaveEvent(self, e):
404
+ self._isEnter = False
405
+ self.timer.stop()
406
+ self.timer.singleShot(200, self.collapse)
407
+
408
+ def eventFilter(self, obj, e: QEvent):
409
+ if obj is not self.parent():
410
+ return super().eventFilter(obj, e)
411
+
412
+ # adjust the position of slider
413
+ if e.type() == QEvent.Resize:
414
+ self._adjustPos(e.size())
415
+
416
+ return super().eventFilter(obj, e)
417
+
418
+ def resizeEvent(self, e):
419
+ self.groove.resize(self.size())
420
+
421
+ def mousePressEvent(self, e: QMouseEvent):
422
+ super().mousePressEvent(e)
423
+ self._isPressed = True
424
+ self._pressedPos = e.pos()
425
+
426
+ if self.childAt(e.pos()) is self.handle or not self._isSlideResion(e.pos()):
427
+ return
428
+
429
+ if self.orientation() == Qt.Vertical:
430
+ if e.pos().y() > self.handle.geometry().bottom():
431
+ value = e.pos().y() - self.handle.height() - self._padding
432
+ else:
433
+ value = e.pos().y() - self._padding
434
+ else:
435
+ if e.pos().x() > self.handle.geometry().right():
436
+ value = e.pos().x() - self.handle.width() - self._padding
437
+ else:
438
+ value = e.pos().x() - self._padding
439
+
440
+ self.setValue(int(value / max(self._slideLength(), 1) * self.maximum()))
441
+ self.sliderPressed.emit()
442
+
443
+ def mouseReleaseEvent(self, e):
444
+ super().mouseReleaseEvent(e)
445
+ self._isPressed = False
446
+ self.sliderReleased.emit()
447
+
448
+ def mouseMoveEvent(self, e: QMouseEvent):
449
+ if self.orientation() == Qt.Vertical:
450
+ dv = e.pos().y() - self._pressedPos.y()
451
+ else:
452
+ dv = e.pos().x() - self._pressedPos.x()
453
+
454
+ # don't use `self.setValue()`, because it could be reimplemented
455
+ dv = int(dv / max(self._slideLength(), 1) * (self.maximum() - self.minimum()))
456
+ ScrollBar.setValue(self, self.value() + dv)
457
+
458
+ self._pressedPos = e.pos()
459
+ self.sliderMoved.emit()
460
+
461
+ def _adjustPos(self, size):
462
+ if self.orientation() == Qt.Vertical:
463
+ self.resize(12, size.height() - 2)
464
+ self.move(size.width() - 13, 1)
465
+ else:
466
+ self.resize(size.width() - 2, 12)
467
+ self.move(1, size.height() - 13)
468
+
469
+ def _adjustHandleSize(self):
470
+ p = self.parent()
471
+ if self.orientation() == Qt.Vertical:
472
+ total = self.maximum() - self.minimum() + p.height()
473
+ s = int(self._grooveLength() * p.height() / max(total, 1))
474
+ self.handle.setFixedHeight(max(30, s))
475
+ else:
476
+ total = self.maximum() - self.minimum() + p.width()
477
+ s = int(self._grooveLength() * p.width() / max(total, 1))
478
+ self.handle.setFixedWidth(max(30, s))
479
+
480
+ def _adjustHandlePos(self):
481
+ total = max(self.maximum() - self.minimum(), 1)
482
+ delta = int(self.value() / total * self._slideLength())
483
+
484
+ if self.orientation() == Qt.Vertical:
485
+ x = self.width() - self.handle.width() - 3
486
+ self.handle.move(x, self._padding + delta)
487
+ else:
488
+ y = self.height() - self.handle.height() - 3
489
+ self.handle.move(self._padding + delta, y)
490
+
491
+ def _grooveLength(self):
492
+ if self.orientation() == Qt.Vertical:
493
+ return self.height() - 2 * self._padding
494
+
495
+ return self.width() - 2 * self._padding
496
+
497
+ def _slideLength(self):
498
+ if self.orientation() == Qt.Vertical:
499
+ return self._grooveLength() - self.handle.height()
500
+
501
+ return self._grooveLength() - self.handle.width()
502
+
503
+ def _isSlideResion(self, pos: QPoint):
504
+ if self.orientation() == Qt.Vertical:
505
+ return self._padding <= pos.y() <= self.height() - self._padding
506
+
507
+ return self._padding <= pos.x() <= self.width() - self._padding
508
+
509
+ def _onOpacityAniValueChanged(self):
510
+ opacity = self.groove.opacity
511
+ if self.orientation() == Qt.Vertical:
512
+ self.handle.setFixedWidth(int(3 + opacity * 3))
513
+ else:
514
+ self.handle.setFixedHeight(int(3 + opacity * 3))
515
+
516
+ self._adjustHandlePos()
517
+
518
+ def setForceHidden(self, isHidden: bool):
519
+ """ whether to force the scrollbar to be hidden """
520
+ self._isForceHidden = isHidden
521
+ self.setVisible(self.maximum() > 0 and not isHidden)
522
+
523
+ def wheelEvent(self, e):
524
+ QApplication.sendEvent(self.parent().viewport(), e)
525
+
526
+
527
+ class SmoothScrollBar(ScrollBar):
528
+ """ Smooth scroll bar """
529
+
530
+ def __init__(self, orient: Qt.Orientation, parent):
531
+ super().__init__(orient, parent)
532
+ self.duration = 500
533
+ self.ani = QPropertyAnimation()
534
+ self.ani.setTargetObject(self)
535
+ self.ani.setPropertyName(b"val")
536
+ self.ani.setEasingCurve(QEasingCurve.OutCubic)
537
+ self.ani.setDuration(self.duration)
538
+
539
+ self.__value = self.value()
540
+
541
+ def setValue(self, value, useAni=True):
542
+ if value == self.value():
543
+ return
544
+
545
+ # stop running animation
546
+ self.ani.stop()
547
+
548
+ if not useAni:
549
+ self.val = value
550
+ return
551
+
552
+ # adjust the duration
553
+ dv = abs(value - self.value())
554
+ if dv < 50:
555
+ self.ani.setDuration(int(self.duration * dv / 70))
556
+ else:
557
+ self.ani.setDuration(self.duration)
558
+
559
+ self.ani.setStartValue(self.value())
560
+ self.ani.setEndValue(value)
561
+ self.ani.start()
562
+
563
+ def scrollValue(self, value, useAni=True):
564
+ """ scroll the specified distance """
565
+ self.__value += value
566
+ self.__value = max(self.minimum(), self.__value)
567
+ self.__value = min(self.maximum(), self.__value)
568
+ self.setValue(self.__value, useAni)
569
+
570
+ def scrollTo(self, value, useAni=True):
571
+ """ scroll to the specified position """
572
+ self.__value = value
573
+ self.__value = max(self.minimum(), self.__value)
574
+ self.__value = min(self.maximum(), self.__value)
575
+ self.setValue(self.__value, useAni)
576
+
577
+ def resetValue(self, value):
578
+ self.__value = value
579
+
580
+ def mousePressEvent(self, e):
581
+ self.ani.stop()
582
+ super().mousePressEvent(e)
583
+ self.__value = self.value()
584
+
585
+ def mouseMoveEvent(self, e):
586
+ self.ani.stop()
587
+ super().mouseMoveEvent(e)
588
+ self.__value = self.value()
589
+
590
+ def setScrollAnimation(self, duration, easing=QEasingCurve.OutCubic):
591
+ """ set scroll animation
592
+
593
+ Parameters
594
+ ----------
595
+ duration: int
596
+ scroll duration
597
+
598
+ easing: QEasingCurve
599
+ animation type
600
+ """
601
+ self.duration = duration
602
+ self.ani.setDuration(duration)
603
+ self.ani.setEasingCurve(easing)
604
+
605
+
606
+ class SmoothScrollDelegate(QObject):
607
+ """ Smooth scroll delegate """
608
+
609
+ def __init__(self, parent: QAbstractScrollArea, useAni=False):
610
+ """
611
+ Parameters
612
+ ----------
613
+ parent: QAbstractScrollArea
614
+ the scrolling area being delegated
615
+
616
+ useAni: bool
617
+ whether to use `QPropertyAnimation` to achieve smooth scrolling
618
+ """
619
+ super().__init__(parent)
620
+ self.useAni = useAni
621
+ self.vScrollBar = SmoothScrollBar(Qt.Vertical, parent)
622
+ self.hScrollBar = SmoothScrollBar(Qt.Horizontal, parent)
623
+ self.verticalSmoothScroll = SmoothScroll(parent, Qt.Vertical)
624
+ self.horizonSmoothScroll = SmoothScroll(parent, Qt.Horizontal)
625
+
626
+ if isinstance(parent, QAbstractItemView):
627
+ parent.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
628
+ parent.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
629
+ if isinstance(parent, QListView):
630
+ parent.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
631
+ parent.horizontalScrollBar().setStyleSheet("QScrollBar:horizontal{height: 0px}")
632
+
633
+ parent.viewport().installEventFilter(self)
634
+ parent.setVerticalScrollBarPolicy = self.setVerticalScrollBarPolicy
635
+ parent.setHorizontalScrollBarPolicy = self.setHorizontalScrollBarPolicy
636
+
637
+ def eventFilter(self, obj, e: QEvent):
638
+ if e.type() == QEvent.Type.Wheel:
639
+ # Check if the vertical scroll is at its limit
640
+ verticalAtEnd = (e.angleDelta().y() < 0 and self.vScrollBar.value() == self.vScrollBar.maximum()) or \
641
+ (e.angleDelta().y() > 0 and self.vScrollBar.value() == self.vScrollBar.minimum())
642
+
643
+ # Check if the horizontal scroll is at its limit
644
+ horizontalAtEnd = (e.angleDelta().x() < 0 and self.hScrollBar.value() == self.hScrollBar.maximum()) or \
645
+ (e.angleDelta().x() > 0 and self.hScrollBar.value() == self.hScrollBar.minimum())
646
+
647
+ if verticalAtEnd or horizontalAtEnd:
648
+ return False
649
+
650
+ if e.angleDelta().y() != 0:
651
+ if not self.useAni:
652
+ self.verticalSmoothScroll.wheelEvent(e)
653
+ else:
654
+ self.vScrollBar.scrollValue(-e.angleDelta().y())
655
+ else:
656
+ if not self.useAni:
657
+ self.horizonSmoothScroll.wheelEvent(e)
658
+ else:
659
+ self.hScrollBar.scrollValue(-e.angleDelta().x())
660
+
661
+ e.setAccepted(True)
662
+ return True
663
+
664
+ return super().eventFilter(obj, e)
665
+
666
+ def setVerticalScrollBarPolicy(self, policy):
667
+ QAbstractScrollArea.setVerticalScrollBarPolicy(self.parent(), Qt.ScrollBarAlwaysOff)
668
+ self.vScrollBar.setForceHidden(policy == Qt.ScrollBarAlwaysOff)
669
+
670
+ def setHorizontalScrollBarPolicy(self, policy):
671
+ QAbstractScrollArea.setHorizontalScrollBarPolicy(self.parent(), Qt.ScrollBarAlwaysOff)
672
+ self.hScrollBar.setForceHidden(policy == Qt.ScrollBarAlwaysOff)
673
+
@@ -0,0 +1,43 @@
1
+ # coding:utf-8
2
+ from PySide6.QtGui import QPixmap, QPainter, QColor
3
+ from PySide6.QtWidgets import QWidget
4
+
5
+ from ...common.style_sheet import isDarkTheme
6
+
7
+
8
+ class HorizontalSeparator(QWidget):
9
+ """ Horizontal separator """
10
+
11
+ def __init__(self, parent=None):
12
+ super().__init__(parent=parent)
13
+ self.setFixedHeight(3)
14
+
15
+ def paintEvent(self, e):
16
+ painter = QPainter(self)
17
+ painter.setRenderHints(QPainter.Antialiasing)
18
+
19
+ if isDarkTheme():
20
+ painter.setPen(QColor(255, 255, 255, 51))
21
+ else:
22
+ painter.setPen(QColor(0, 0, 0, 22))
23
+
24
+ painter.drawLine(0, 1, self.width(), 1)
25
+
26
+
27
+ class VerticalSeparator(QWidget):
28
+ """ Vertical separator """
29
+
30
+ def __init__(self, parent=None):
31
+ super().__init__(parent=parent)
32
+ self.setFixedWidth(3)
33
+
34
+ def paintEvent(self, e):
35
+ painter = QPainter(self)
36
+ painter.setRenderHints(QPainter.Antialiasing)
37
+
38
+ if isDarkTheme():
39
+ painter.setPen(QColor(255, 255, 255, 51))
40
+ else:
41
+ painter.setPen(QColor(0, 0, 0, 22))
42
+
43
+ painter.drawLine(1, 0, 1, self.height())