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,632 @@
1
+ # coding:utf-8
2
+ from typing import Iterable, List
3
+
4
+ from PySide6.QtCore import Qt, Signal, QSize, QRectF, QPoint, QPropertyAnimation, QEasingCurve, QObject
5
+ from PySide6.QtGui import QColor, QPainter, QCursor, QRegion
6
+ from PySide6.QtWidgets import (QApplication, QWidget, QFrame, QVBoxLayout, QHBoxLayout,
7
+ QGraphicsDropShadowEffect, QSizePolicy, QPushButton, QListWidgetItem)
8
+
9
+ from ..widgets.cycle_list_widget import CycleListWidget
10
+ from ..widgets.button import TransparentToolButton
11
+ from ...common.icon import FluentIcon
12
+ from ...common.screen import getCurrentScreenGeometry
13
+ from ...common.style_sheet import FluentStyleSheet, themeColor, isDarkTheme
14
+ from ...common.color import autoFallbackThemeColor
15
+
16
+
17
+ class SeparatorWidget(QWidget):
18
+ """ Separator widget """
19
+
20
+ def __init__(self, orient: Qt.Orientation, parent=None):
21
+ super().__init__(parent=parent)
22
+ if orient == Qt.Horizontal:
23
+ self.setFixedHeight(1)
24
+ else:
25
+ self.setFixedWidth(1)
26
+
27
+ self.setAttribute(Qt.WA_StyledBackground)
28
+ FluentStyleSheet.TIME_PICKER.apply(self)
29
+
30
+
31
+ class ItemMaskWidget(QWidget):
32
+ """ Item mask widget """
33
+
34
+ def __init__(self, listWidgets: List[CycleListWidget], parent=None):
35
+ super().__init__(parent=parent)
36
+ self.listWidgets = listWidgets
37
+ self.setFixedHeight(37)
38
+ self.lightBackgroundColor = QColor()
39
+ self.darkBackgroundColor = QColor()
40
+ FluentStyleSheet.TIME_PICKER.apply(self)
41
+
42
+ def setCustomBackgroundColor(self, light, dark):
43
+ self.lightBackgroundColor = QColor(light)
44
+ self.darkBackgroundColor = QColor(dark)
45
+ self.update()
46
+
47
+ def paintEvent(self, e):
48
+ painter = QPainter(self)
49
+ painter.setRenderHints(QPainter.Antialiasing |
50
+ QPainter.TextAntialiasing)
51
+
52
+ # draw background
53
+ painter.setPen(Qt.NoPen)
54
+ painter.setBrush(autoFallbackThemeColor(self.lightBackgroundColor, self.darkBackgroundColor))
55
+ painter.drawRoundedRect(self.rect().adjusted(4, 0, -3, 0), 5, 5)
56
+
57
+ # draw text
58
+ painter.setPen(Qt.black if isDarkTheme() else Qt.white)
59
+ painter.setFont(self.font())
60
+ w, h = 0, self.height()
61
+ for i, p in enumerate(self.listWidgets):
62
+ painter.save()
63
+
64
+ # draw first item's text
65
+ x = p.itemSize.width()//2 + 4 + self.x()
66
+ item1 = p.itemAt(QPoint(x, self.y() + 6))
67
+ if not item1:
68
+ painter.restore()
69
+ continue
70
+
71
+ iw = item1.sizeHint().width()
72
+ y = p.visualItemRect(item1).y()
73
+ painter.translate(w, y - self.y() + 7)
74
+ self._drawText(item1, painter, 0)
75
+
76
+ # draw second item's text
77
+ item2 = p.itemAt(self.pos() + QPoint(x, h - 6))
78
+ self._drawText(item2, painter, h)
79
+
80
+ painter.restore()
81
+ w += (iw + 8) # margin: 0 4px;
82
+
83
+ def _drawText(self, item: QListWidgetItem, painter: QPainter, y: int):
84
+ align = item.textAlignment()
85
+ w, h = item.sizeHint().width(), item.sizeHint().height()
86
+ if align & Qt.AlignLeft:
87
+ rect = QRectF(15, y, w, h) # padding-left: 11px
88
+ elif align & Qt.AlignRight:
89
+ rect = QRectF(4, y, w-15, h) # padding-right: 11px
90
+ elif align & Qt.AlignCenter:
91
+ rect = QRectF(4, y, w, h)
92
+
93
+ painter.drawText(rect, align, item.text())
94
+
95
+
96
+ class PickerColumnFormatter(QObject):
97
+ """ Picker column formatter """
98
+
99
+ def __init__(self):
100
+ super().__init__()
101
+
102
+ def encode(self, value):
103
+ """ convert original value to formatted value """
104
+ return str(value)
105
+
106
+ def decode(self, value: str):
107
+ """ convert formatted value to original value """
108
+ return str(value)
109
+
110
+
111
+ class DigitFormatter(PickerColumnFormatter):
112
+ """ Digit formatter """
113
+
114
+ def decode(self, value):
115
+ return int(value)
116
+
117
+
118
+ class PickerColumnButton(QPushButton):
119
+ """ Picker column button """
120
+
121
+ def __init__(self, name: str, items: Iterable, width: int, align=Qt.AlignLeft, formatter=None, parent=None):
122
+ super().__init__(text=name, parent=parent)
123
+ self._name = name
124
+ self._value = None # type: str
125
+
126
+ self.setItems(items)
127
+ self.setAlignment(align)
128
+ self.setFormatter(formatter)
129
+ self.setFixedSize(width, 30)
130
+ self.setObjectName('pickerButton')
131
+ self.setProperty('hasBorder', False)
132
+ self.setAttribute(Qt.WA_TransparentForMouseEvents)
133
+
134
+ def align(self):
135
+ return self._align
136
+
137
+ def setAlignment(self, align=Qt.AlignCenter):
138
+ """ set the text alignment """
139
+ if align == Qt.AlignLeft:
140
+ self.setProperty('align', 'left')
141
+ elif align == Qt.AlignRight:
142
+ self.setProperty('align', 'right')
143
+ else:
144
+ self.setProperty('align', 'center')
145
+
146
+ self._align = align
147
+ self.setStyle(QApplication.style())
148
+
149
+ def value(self) -> str:
150
+ if self._value is None:
151
+ return None
152
+
153
+ return self.formatter().encode(self._value)
154
+
155
+ def setValue(self, v):
156
+ self._value = v
157
+ if v is None:
158
+ self.setText(self.name())
159
+ self.setProperty('hasValue', False)
160
+ else:
161
+ self.setText(self.value())
162
+ self.setProperty('hasValue', True)
163
+
164
+ self.setStyle(QApplication.style())
165
+
166
+ def items(self):
167
+ return [self._formatter.encode(i) for i in self._items]
168
+
169
+ def setItems(self, items: Iterable):
170
+ self._items = list(items)
171
+
172
+ def formatter(self):
173
+ return self._formatter
174
+
175
+ def setFormatter(self, formatter):
176
+ self._formatter = formatter or PickerColumnFormatter()
177
+
178
+ def name(self):
179
+ return self._name
180
+
181
+ def setName(self, name: str):
182
+ if self.text() == self.name():
183
+ self.setText(name)
184
+
185
+ self._name = name
186
+
187
+
188
+ def checkColumnIndex(func):
189
+ """ check whether the index is out of range """
190
+
191
+ def wrapper(picker, index: int, *args, **kwargs):
192
+ if not 0 <= index < len(picker.columns):
193
+ return
194
+
195
+ return func(picker, index, *args, **kwargs)
196
+
197
+ return wrapper
198
+
199
+
200
+ class PickerBase(QPushButton):
201
+ """ Picker base class """
202
+
203
+ def __init__(self, parent=None):
204
+ super().__init__(parent=parent)
205
+ self.columns = [] # type: List[PickerColumnButton]
206
+
207
+ self.lightSelectedBackgroundColor = QColor()
208
+ self.darkSelectedBackgroundColor = QColor()
209
+
210
+ self._isResetEnabled = False
211
+ self.hBoxLayout = QHBoxLayout(self)
212
+
213
+ self.hBoxLayout.setSpacing(0)
214
+ self.hBoxLayout.setContentsMargins(0, 0, 0, 0)
215
+ self.hBoxLayout.setSizeConstraint(QHBoxLayout.SetFixedSize)
216
+
217
+ FluentStyleSheet.TIME_PICKER.apply(self)
218
+ self.clicked.connect(self._showPanel)
219
+
220
+ def setSelectedBackgroundColor(self, light, dark):
221
+ """ set the background color of selected row """
222
+ self.lightSelectedBackgroundColor = QColor(light)
223
+ self.darkSelectedBackgroundColor = QColor(dark)
224
+
225
+ def addColumn(self, name: str, items: Iterable, width: int, align=Qt.AlignCenter,
226
+ formatter: PickerColumnFormatter = None):
227
+ """ add column
228
+
229
+ Parameters
230
+ ----------
231
+ name: str
232
+ the name of column
233
+
234
+ items: Iterable
235
+ the items of column
236
+
237
+ width: int
238
+ the width of column
239
+
240
+ align: Qt.AlignmentFlag
241
+ the text alignment of button
242
+
243
+ formatter: PickerColumnFormatter
244
+ the formatter of column
245
+ """
246
+ # create column button
247
+ button = PickerColumnButton(name, items, width, align, formatter, self)
248
+ self.columns.append(button)
249
+
250
+ self.hBoxLayout.addWidget(button, 0, Qt.AlignLeft)
251
+
252
+ # update the style of buttons
253
+ for btn in self.columns[:-1]:
254
+ btn.setProperty('hasBorder', True)
255
+ btn.setStyle(QApplication.style())
256
+
257
+ @checkColumnIndex
258
+ def setColumnAlignment(self, index: int, align=Qt.AlignCenter):
259
+ """ set the text alignment of specified column """
260
+ self.columns[index].setAlignment(align)
261
+
262
+ @checkColumnIndex
263
+ def setColumnWidth(self, index: int, width: int):
264
+ """ set the width of specified column """
265
+ self.columns[index].setFixedWidth(width)
266
+
267
+ @checkColumnIndex
268
+ def setColumnTight(self, index: int):
269
+ """ make the specified column to be tight """
270
+ fm = self.fontMetrics()
271
+ w = max(fm.width(i) for i in self.columns[index].items) + 30
272
+ self.setColumnWidth(index, w)
273
+
274
+ @checkColumnIndex
275
+ def setColumnVisible(self, index: int, isVisible: bool):
276
+ """ set the text alignment of specified column """
277
+ self.columns[index].setVisible(isVisible)
278
+
279
+ def value(self):
280
+ return [c.value() for c in self.columns if c.isVisible()]
281
+
282
+ def initialValue(self):
283
+ return [c.initialValue() for c in self.columns if c.isVisible()]
284
+
285
+ @checkColumnIndex
286
+ def setColumnValue(self, index: int, value):
287
+ self.columns[index].setValue(value)
288
+
289
+ @checkColumnIndex
290
+ def setColumnInitialValue(self, index: int, value):
291
+ self.columns[index].setInitialValue(value)
292
+
293
+ @checkColumnIndex
294
+ def setColumnFormatter(self, index: int, formatter: PickerColumnFormatter):
295
+ self.columns[index].setFormatter(formatter)
296
+
297
+ @checkColumnIndex
298
+ def setColumnItems(self, index: int, items: Iterable):
299
+ self.columns[index].setItems(items)
300
+
301
+ @checkColumnIndex
302
+ def encodeValue(self, index: int, value):
303
+ """ convert original value to formatted value """
304
+ return self.columns[index].formatter().encode(value)
305
+
306
+ @checkColumnIndex
307
+ def decodeValue(self, index: int, value):
308
+ """ convert formatted value to origin value """
309
+ return self.columns[index].formatter().decode(value)
310
+
311
+ @checkColumnIndex
312
+ def setColumn(self, index: int, name: str, items: Iterable, width: int, align=Qt.AlignCenter):
313
+ """ set column
314
+
315
+ Parameters
316
+ ----------
317
+ index: int
318
+ the index of column
319
+
320
+ name: str
321
+ the name of column
322
+
323
+ items: Iterable
324
+ the items of column
325
+
326
+ width: int
327
+ the width of column
328
+
329
+ align: Qt.AlignmentFlag
330
+ the text alignment of button
331
+ """
332
+ button = self.columns[index]
333
+ button.setText(name)
334
+ button.setFixedWidth(width)
335
+ button.setAlignment(align)
336
+
337
+ def clearColumns(self):
338
+ """ clear columns """
339
+ while self.columns:
340
+ btn = self.columns.pop()
341
+ self.hBoxLayout.removeWidget(btn)
342
+ # The parent of btn should be explicitly set to None to remove references from its parent.
343
+ # Otherwise, GC will not collect and remove it until the end of it parent life-cycle
344
+ btn.setParent(None)
345
+ btn.deleteLater()
346
+
347
+ def enterEvent(self, e):
348
+ self._setButtonProperty('enter', True)
349
+
350
+ def leaveEvent(self, e):
351
+ self._setButtonProperty('enter', False)
352
+
353
+ def mousePressEvent(self, e):
354
+ self._setButtonProperty('pressed', True)
355
+ super().mousePressEvent(e)
356
+
357
+ def mouseReleaseEvent(self, e):
358
+ self._setButtonProperty('pressed', False)
359
+ super().mouseReleaseEvent(e)
360
+
361
+ def _setButtonProperty(self, name, value):
362
+ """ send event to picker buttons """
363
+ for button in self.columns:
364
+ button.setProperty(name, value)
365
+ button.setStyle(QApplication.style())
366
+
367
+ def panelInitialValue(self):
368
+ """ initial value of panel """
369
+ return self.value()
370
+
371
+ def _showPanel(self):
372
+ """ show panel """
373
+ panel = PickerPanel(self)
374
+ for column in self.columns:
375
+ if column.isVisible():
376
+ panel.addColumn(column.items(), column.width(), column.align())
377
+
378
+ panel.setValue(self.panelInitialValue())
379
+ panel.setResetEnabled(self.isRestEnabled())
380
+ panel.setSelectedBackgroundColor(
381
+ self.lightSelectedBackgroundColor, self.darkSelectedBackgroundColor)
382
+
383
+ panel.confirmed.connect(self._onConfirmed)
384
+ panel.resetted.connect(self.reset)
385
+ panel.columnValueChanged.connect(
386
+ lambda i, v: self._onColumnValueChanged(panel, i, v))
387
+
388
+ w = panel.vBoxLayout.sizeHint().width() - self.width()
389
+ panel.exec(self.mapToGlobal(QPoint(-w//2, -37 * 4)))
390
+
391
+ def _onConfirmed(self, value: list):
392
+ for i, v in enumerate(value):
393
+ self.setColumnValue(i, v)
394
+
395
+ def reset(self):
396
+ for i in range(len(self.columns)):
397
+ self.setColumnValue(i, None)
398
+
399
+ def _onColumnValueChanged(self, panel, index: int, value: str):
400
+ """ column value changed slot """
401
+ pass
402
+
403
+ def isRestEnabled(self):
404
+ return self._isResetEnabled
405
+
406
+ def setResetEnabled(self, isEnabled: bool):
407
+ """ set the visibility of reset button """
408
+ self._isResetEnabled = isEnabled
409
+
410
+
411
+ class PickerToolButton(TransparentToolButton):
412
+ """ Picker tool button """
413
+
414
+ def _drawIcon(self, icon, painter, rect):
415
+ if self.isPressed:
416
+ painter.setOpacity(1)
417
+
418
+ super()._drawIcon(icon, painter, rect)
419
+
420
+
421
+ class PickerPanel(QWidget):
422
+ """ picker panel """
423
+
424
+ confirmed = Signal(list)
425
+ resetted = Signal()
426
+ columnValueChanged = Signal(int, str)
427
+
428
+ def __init__(self, parent=None):
429
+ super().__init__(parent=parent)
430
+ self.itemHeight = 37
431
+ self.listWidgets = [] # type: List[CycleListWidget]
432
+
433
+ self.view = QFrame(self)
434
+ self.itemMaskWidget = ItemMaskWidget(self.listWidgets, self)
435
+ self.hSeparatorWidget = SeparatorWidget(Qt.Horizontal, self.view)
436
+ self.yesButton = PickerToolButton(FluentIcon.ACCEPT, self.view)
437
+ self.resetButton = PickerToolButton(FluentIcon.CANCEL, self.view)
438
+ self.cancelButton = PickerToolButton(FluentIcon.CLOSE, self.view)
439
+
440
+ self.hBoxLayout = QHBoxLayout(self)
441
+ self.listLayout = QHBoxLayout()
442
+ self.buttonLayout = QHBoxLayout()
443
+ self.vBoxLayout = QVBoxLayout(self.view)
444
+
445
+ self.__initWidget()
446
+
447
+ def __initWidget(self):
448
+ self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint |
449
+ Qt.NoDropShadowWindowHint)
450
+ self.setAttribute(Qt.WA_TranslucentBackground)
451
+
452
+ self.setShadowEffect()
453
+ self.yesButton.setIconSize(QSize(16, 16))
454
+ self.resetButton.setIconSize(QSize(16, 16))
455
+ self.cancelButton.setIconSize(QSize(13, 13))
456
+ self.yesButton.setFixedHeight(33)
457
+ self.cancelButton.setFixedHeight(33)
458
+ self.resetButton.setFixedHeight(33)
459
+
460
+ self.hBoxLayout.setContentsMargins(12, 8, 12, 20)
461
+ self.hBoxLayout.addWidget(self.view, 1, Qt.AlignCenter)
462
+ self.hBoxLayout.setSizeConstraint(QHBoxLayout.SetMinimumSize)
463
+
464
+ self.vBoxLayout.setSpacing(0)
465
+ self.vBoxLayout.setContentsMargins(0, 0, 0, 0)
466
+ self.vBoxLayout.addLayout(self.listLayout, 1)
467
+ self.vBoxLayout.addWidget(self.hSeparatorWidget)
468
+ self.vBoxLayout.addLayout(self.buttonLayout, 1)
469
+ self.vBoxLayout.setSizeConstraint(QVBoxLayout.SetMinimumSize)
470
+
471
+ self.buttonLayout.setSpacing(6)
472
+ self.buttonLayout.setContentsMargins(3, 3, 3, 3)
473
+ self.buttonLayout.addWidget(self.yesButton)
474
+ self.buttonLayout.addWidget(self.resetButton)
475
+ self.buttonLayout.addWidget(self.cancelButton)
476
+ self.yesButton.setSizePolicy(
477
+ QSizePolicy.Expanding, QSizePolicy.Expanding)
478
+ self.resetButton.setSizePolicy(
479
+ QSizePolicy.Expanding, QSizePolicy.Expanding)
480
+ self.cancelButton.setSizePolicy(
481
+ QSizePolicy.Expanding, QSizePolicy.Expanding)
482
+
483
+ self.yesButton.clicked.connect(self._fadeOut)
484
+ self.yesButton.clicked.connect(
485
+ lambda: self.confirmed.emit(self.value()))
486
+ self.cancelButton.clicked.connect(self._fadeOut)
487
+ self.resetButton.clicked.connect(self.resetted)
488
+ self.resetButton.clicked.connect(self._fadeOut)
489
+
490
+ self.setResetEnabled(False)
491
+
492
+ self.view.setObjectName('view')
493
+ FluentStyleSheet.TIME_PICKER.apply(self)
494
+
495
+ def setShadowEffect(self, blurRadius=30, offset=(0, 8), color=QColor(0, 0, 0, 30)):
496
+ """ add shadow to dialog """
497
+ self.shadowEffect = QGraphicsDropShadowEffect(self.view)
498
+ self.shadowEffect.setBlurRadius(blurRadius)
499
+ self.shadowEffect.setOffset(*offset)
500
+ self.shadowEffect.setColor(color)
501
+ self.view.setGraphicsEffect(None)
502
+ self.view.setGraphicsEffect(self.shadowEffect)
503
+
504
+ def setResetEnabled(self, isEnabled: bool):
505
+ """ set the visibility of reset button """
506
+ self.resetButton.setVisible(isEnabled)
507
+
508
+ def setSelectedBackgroundColor(self, light, dark):
509
+ self.itemMaskWidget.setCustomBackgroundColor(light, dark)
510
+
511
+ def isResetEnabled(self):
512
+ return self.resetButton.isVisible()
513
+
514
+ def addColumn(self, items: Iterable, width: int, align=Qt.AlignCenter):
515
+ """ add one column to view
516
+
517
+ Parameters
518
+ ----------
519
+ items: Iterable[Any]
520
+ the items to be added
521
+
522
+ width: int
523
+ the width of item
524
+
525
+ align: Qt.AlignmentFlag
526
+ the text alignment of item
527
+ """
528
+ if self.listWidgets:
529
+ self.listLayout.addWidget(SeparatorWidget(Qt.Vertical))
530
+
531
+ w = CycleListWidget(items, QSize(width, self.itemHeight), align, self)
532
+ w.vScrollBar.valueChanged.connect(self.itemMaskWidget.update)
533
+
534
+ N = len(self.listWidgets)
535
+ w.currentItemChanged.connect(
536
+ lambda i, n=N: self.columnValueChanged.emit(n, i.text()))
537
+
538
+ self.listWidgets.append(w)
539
+ self.listLayout.addWidget(w)
540
+
541
+ def resizeEvent(self, e):
542
+ self.itemMaskWidget.resize(self.view.width()-3, self.itemHeight)
543
+ m = self.hBoxLayout.contentsMargins()
544
+ self.itemMaskWidget.move(m.left()+2, m.top() + 148)
545
+
546
+ def value(self):
547
+ """ return the value of columns """
548
+ return [i.currentItem().text() for i in self.listWidgets]
549
+
550
+ def setValue(self, value: list):
551
+ """ set the value of columns """
552
+ if len(value) != len(self.listWidgets):
553
+ return
554
+
555
+ for v, w in zip(value, self.listWidgets):
556
+ w.setSelectedItem(v)
557
+
558
+ def columnValue(self, index: int) -> str:
559
+ """ return the value of specified column """
560
+ if not 0 <= index < len(self.listWidgets):
561
+ return
562
+
563
+ return self.listWidgets[index].currentItem().text()
564
+
565
+ def setColumnValue(self, index: int, value: str):
566
+ """ set the value of specified column """
567
+ if not 0 <= index < len(self.listWidgets):
568
+ return
569
+
570
+ self.listWidgets[index].setSelectedItem(value)
571
+
572
+ def column(self, index: int):
573
+ """ return the list widget of specified column """
574
+ return self.listWidgets[index]
575
+
576
+ def exec(self, pos, ani=True):
577
+ """ show panel
578
+
579
+ Parameters
580
+ ----------
581
+ pos: QPoint
582
+ pop-up position
583
+
584
+ ani: bool
585
+ Whether to show pop-up animation
586
+ """
587
+ if self.isVisible():
588
+ return
589
+
590
+ # show before running animation, or the height calculation will be wrong
591
+ self.show()
592
+
593
+ rect = getCurrentScreenGeometry()
594
+ w, h = self.width() + 5, self.height()
595
+ pos.setX(
596
+ min(pos.x() - self.layout().contentsMargins().left(), rect.right() - w))
597
+ pos.setY(max(rect.top(), min(pos.y() - 4, rect.bottom() - h + 5)))
598
+ self.move(pos)
599
+
600
+ if not ani:
601
+ return
602
+
603
+ self.isExpanded = False
604
+ self.ani = QPropertyAnimation(self.view, b'windowOpacity', self)
605
+ self.ani.valueChanged.connect(self._onAniValueChanged)
606
+ self.ani.setStartValue(0)
607
+ self.ani.setEndValue(1)
608
+ self.ani.setDuration(150)
609
+ self.ani.setEasingCurve(QEasingCurve.OutQuad)
610
+ self.ani.start()
611
+
612
+ def _onAniValueChanged(self, opacity):
613
+ m = self.layout().contentsMargins()
614
+ w = self.view.width() + m.left() + m.right() + 120
615
+ h = self.view.height() + m.top() + m.bottom() + 12
616
+ if not self.isExpanded:
617
+ y = int(h / 2 * (1 - opacity))
618
+ self.setMask(QRegion(0, y, w, h-y*2))
619
+ else:
620
+ y = int(h / 3 * (1 - opacity))
621
+ self.setMask(QRegion(0, y, w, h-y*2))
622
+
623
+ def _fadeOut(self):
624
+ self.isExpanded = True
625
+ self.ani = QPropertyAnimation(self, b'windowOpacity', self)
626
+ self.ani.valueChanged.connect(self._onAniValueChanged)
627
+ self.ani.finished.connect(self.deleteLater)
628
+ self.ani.setStartValue(1)
629
+ self.ani.setEndValue(0)
630
+ self.ani.setDuration(150)
631
+ self.ani.setEasingCurve(QEasingCurve.OutQuad)
632
+ self.ani.start()