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,596 @@
1
+ # coding:utf-8
2
+ from enum import Enum
3
+ import sys
4
+ from typing import Union
5
+ import weakref
6
+
7
+ from PySide6.QtCore import (Qt, QEvent, QSize, QRectF, QObject, QPropertyAnimation,
8
+ QEasingCurve, QTimer, Signal, QParallelAnimationGroup, QPoint)
9
+ from PySide6.QtGui import QPainter, QIcon, QColor
10
+ from PySide6.QtWidgets import (QWidget, QFrame, QLabel, QHBoxLayout, QVBoxLayout,
11
+ QToolButton, QGraphicsOpacityEffect, QApplication)
12
+
13
+ from ...common.auto_wrap import TextWrap
14
+ from ...common.style_sheet import FluentStyleSheet, themeColor
15
+ from ...common.icon import FluentIconBase, Theme, isDarkTheme, writeSvg, drawSvgIcon, drawIcon
16
+ from ...common.icon import FluentIcon as FIF
17
+ from .button import TransparentToolButton
18
+
19
+
20
+
21
+ class InfoBarIcon(FluentIconBase, Enum):
22
+ """ Info bar icon """
23
+
24
+ INFORMATION = "Info"
25
+ SUCCESS = "Success"
26
+ WARNING = "Warning"
27
+ ERROR = "Error"
28
+
29
+ def path(self, theme=Theme.AUTO):
30
+ if theme == Theme.AUTO:
31
+ color = "dark" if isDarkTheme() else "light"
32
+ else:
33
+ color = theme.value.lower()
34
+
35
+ return f':/fastuiwidgets/images/info_bar/{self.value}_{color}.svg'
36
+
37
+
38
+ class InfoBarPosition(Enum):
39
+ """ Info bar position """
40
+ TOP = 0
41
+ BOTTOM = 1
42
+ TOP_LEFT = 2
43
+ TOP_RIGHT = 3
44
+ BOTTOM_LEFT = 4
45
+ BOTTOM_RIGHT = 5
46
+ NONE = 6
47
+
48
+
49
+ class InfoIconWidget(QWidget):
50
+ """ Icon widget """
51
+
52
+ def __init__(self, icon: InfoBarIcon, parent=None):
53
+ super().__init__(parent=parent)
54
+ self.setFixedSize(36, 36)
55
+ self.icon = icon
56
+
57
+ def paintEvent(self, e):
58
+ painter = QPainter(self)
59
+ painter.setRenderHints(QPainter.Antialiasing |
60
+ QPainter.SmoothPixmapTransform)
61
+
62
+ rect = QRectF(10, 10, 15, 15)
63
+ if self.icon != InfoBarIcon.INFORMATION:
64
+ drawIcon(self.icon, painter, rect)
65
+ else:
66
+ drawIcon(self.icon, painter, rect, indexes=[0], fill=themeColor().name())
67
+
68
+
69
+ class InfoBar(QFrame):
70
+ """ Information bar """
71
+
72
+ closedSignal = Signal()
73
+ _desktopView = None # type: DesktopInfoBarView
74
+
75
+ def __init__(self, icon: Union[InfoBarIcon, FluentIconBase, QIcon, str], title: str, content: str,
76
+ orient=Qt.Horizontal, isClosable=True, duration=1000, position=InfoBarPosition.TOP_RIGHT,
77
+ parent=None):
78
+ """
79
+ Parameters
80
+ ----------
81
+ icon: InfoBarIcon | FluentIconBase | QIcon | str
82
+ the icon of info bar
83
+
84
+ title: str
85
+ the title of info bar
86
+
87
+ content: str
88
+ the content of info bar
89
+
90
+ orient: Qt.Orientation
91
+ the layout direction of info bar, use `Qt.Horizontal` for short content
92
+
93
+ isClosable: bool
94
+ whether to show the close button
95
+
96
+ duraction: int
97
+ the time for info bar to display in milliseconds. If duration is less than zero,
98
+ info bar will never disappear.
99
+
100
+ parent: QWidget
101
+ parent widget
102
+ """
103
+ super().__init__(parent=parent)
104
+ self.title = title
105
+ self.content = content
106
+ self.orient = orient
107
+ self.icon = icon
108
+ self.duration = duration
109
+ self.isClosable = isClosable
110
+ self.position = position
111
+
112
+ self.titleLabel = QLabel(self)
113
+ self.contentLabel = QLabel(self)
114
+ self.closeButton = TransparentToolButton(FIF.CLOSE, self)
115
+ self.iconWidget = InfoIconWidget(icon)
116
+
117
+ self.hBoxLayout = QHBoxLayout(self)
118
+ self.textLayout = QHBoxLayout() if self.orient == Qt.Horizontal else QVBoxLayout()
119
+ self.widgetLayout = QHBoxLayout() if self.orient == Qt.Horizontal else QVBoxLayout()
120
+
121
+ self.opacityEffect = QGraphicsOpacityEffect(self)
122
+ self.opacityAni = QPropertyAnimation(
123
+ self.opacityEffect, b'opacity', self)
124
+
125
+ self.lightBackgroundColor = None
126
+ self.darkBackgroundColor = None
127
+
128
+ self.__initWidget()
129
+
130
+ def __initWidget(self):
131
+ self.opacityEffect.setOpacity(1)
132
+ self.setGraphicsEffect(self.opacityEffect)
133
+
134
+ self.closeButton.setFixedSize(36, 36)
135
+ self.closeButton.setIconSize(QSize(12, 12))
136
+ self.closeButton.setCursor(Qt.PointingHandCursor)
137
+ self.closeButton.setVisible(self.isClosable)
138
+
139
+ self.__setQss()
140
+ self.__initLayout()
141
+
142
+ self.closeButton.clicked.connect(self.close)
143
+
144
+ def __initLayout(self):
145
+ self.hBoxLayout.setContentsMargins(6, 6, 6, 6)
146
+ self.hBoxLayout.setSizeConstraint(QVBoxLayout.SetMinimumSize)
147
+ self.textLayout.setSizeConstraint(QHBoxLayout.SetMinimumSize)
148
+ self.textLayout.setAlignment(Qt.AlignTop)
149
+ self.textLayout.setContentsMargins(1, 8, 0, 8)
150
+
151
+ self.hBoxLayout.setSpacing(0)
152
+ self.textLayout.setSpacing(5)
153
+
154
+ # add icon to layout
155
+ self.hBoxLayout.addWidget(self.iconWidget, 0, Qt.AlignTop | Qt.AlignLeft)
156
+
157
+ # add title to layout
158
+ self.textLayout.addWidget(self.titleLabel, 1, Qt.AlignTop)
159
+ self.titleLabel.setVisible(bool(self.title))
160
+
161
+ # add content label to layout
162
+ if self.orient == Qt.Horizontal:
163
+ self.textLayout.addSpacing(7)
164
+
165
+ self.textLayout.addWidget(self.contentLabel, 1, Qt.AlignTop)
166
+ self.contentLabel.setVisible(bool(self.content))
167
+ self.hBoxLayout.addLayout(self.textLayout)
168
+
169
+ # add widget layout
170
+ if self.orient == Qt.Horizontal:
171
+ self.hBoxLayout.addLayout(self.widgetLayout)
172
+ self.widgetLayout.setSpacing(10)
173
+ else:
174
+ self.textLayout.addLayout(self.widgetLayout)
175
+
176
+ # add close button to layout
177
+ self.hBoxLayout.addSpacing(12)
178
+ self.hBoxLayout.addWidget(self.closeButton, 0, Qt.AlignTop | Qt.AlignLeft)
179
+
180
+ self._adjustText()
181
+
182
+ def __setQss(self):
183
+ self.titleLabel.setObjectName('titleLabel')
184
+ self.contentLabel.setObjectName('contentLabel')
185
+ if isinstance(self.icon, Enum):
186
+ self.setProperty('type', self.icon.value)
187
+
188
+ FluentStyleSheet.INFO_BAR.apply(self)
189
+
190
+ def __fadeOut(self):
191
+ """ fade out """
192
+ self.opacityAni.setDuration(200)
193
+ self.opacityAni.setStartValue(1)
194
+ self.opacityAni.setEndValue(0)
195
+ self.opacityAni.finished.connect(self.close)
196
+ self.opacityAni.start()
197
+
198
+ def _adjustText(self):
199
+ w = 900 if not self.parent() else (self.parent().width() - 50)
200
+
201
+ # adjust title
202
+ chars = max(min(w / 10, 120), 30)
203
+ self.titleLabel.setText(TextWrap.wrap(self.title, chars, False)[0])
204
+
205
+ # adjust content
206
+ chars = max(min(w / 9, 120), 30)
207
+ self.contentLabel.setText(TextWrap.wrap(self.content, chars, False)[0])
208
+ self.adjustSize()
209
+
210
+ def addWidget(self, widget: QWidget, stretch=0):
211
+ """ add widget to info bar """
212
+ self.widgetLayout.addSpacing(6)
213
+ align = Qt.AlignTop if self.orient == Qt.Vertical else Qt.AlignVCenter
214
+ self.widgetLayout.addWidget(widget, stretch, Qt.AlignLeft | align)
215
+
216
+ def setCustomBackgroundColor(self, light, dark):
217
+ """ set the custom background color
218
+
219
+ Parameters
220
+ ----------
221
+ light, dark: str | Qt.GlobalColor | QColor
222
+ background color in light/dark theme mode
223
+ """
224
+ self.lightBackgroundColor = QColor(light)
225
+ self.darkBackgroundColor = QColor(dark)
226
+ self.update()
227
+
228
+ def eventFilter(self, obj, e: QEvent):
229
+ if obj is self.parent():
230
+ if e.type() in [QEvent.Resize, QEvent.WindowStateChange]:
231
+ self._adjustText()
232
+
233
+ return super().eventFilter(obj, e)
234
+
235
+ def closeEvent(self, e):
236
+ self.closedSignal.emit()
237
+ self.deleteLater()
238
+ e.ignore()
239
+
240
+ def showEvent(self, e):
241
+ self._adjustText()
242
+ super().showEvent(e)
243
+
244
+ if self.duration >= 0:
245
+ QTimer.singleShot(self.duration, self.__fadeOut)
246
+
247
+ if self.position != InfoBarPosition.NONE:
248
+ manager = InfoBarManager.make(self.position)
249
+ manager.add(self)
250
+
251
+ if self.parent():
252
+ self.parent().installEventFilter(self)
253
+
254
+ def paintEvent(self, e):
255
+ super().paintEvent(e)
256
+ if self.lightBackgroundColor is None:
257
+ return
258
+
259
+ painter = QPainter(self)
260
+ painter.setRenderHints(QPainter.Antialiasing)
261
+ painter.setPen(Qt.NoPen)
262
+
263
+ if isDarkTheme():
264
+ painter.setBrush(self.darkBackgroundColor)
265
+ else:
266
+ painter.setBrush(self.lightBackgroundColor)
267
+
268
+ rect = self.rect().adjusted(1, 1, -1, -1)
269
+ painter.drawRoundedRect(rect, 6, 6)
270
+
271
+ @classmethod
272
+ def new(cls, icon, title, content, orient=Qt.Horizontal, isClosable=True, duration=1000,
273
+ position=InfoBarPosition.TOP_RIGHT, parent=None):
274
+ w = InfoBar(icon, title, content, orient,
275
+ isClosable, duration, position, parent)
276
+ w.show()
277
+ return w
278
+
279
+ @classmethod
280
+ def info(cls, title, content, orient=Qt.Horizontal, isClosable=True, duration=1000,
281
+ position=InfoBarPosition.TOP_RIGHT, parent=None):
282
+ return cls.new(InfoBarIcon.INFORMATION, title, content, orient, isClosable, duration, position, parent)
283
+
284
+ @classmethod
285
+ def success(cls, title, content, orient=Qt.Horizontal, isClosable=True, duration=1000,
286
+ position=InfoBarPosition.TOP_RIGHT, parent=None):
287
+ return cls.new(InfoBarIcon.SUCCESS, title, content, orient, isClosable, duration, position, parent)
288
+
289
+ @classmethod
290
+ def warning(cls, title, content, orient=Qt.Horizontal, isClosable=True, duration=1000,
291
+ position=InfoBarPosition.TOP_RIGHT, parent=None):
292
+ return cls.new(InfoBarIcon.WARNING, title, content, orient, isClosable, duration, position, parent)
293
+
294
+ @classmethod
295
+ def error(cls, title, content, orient=Qt.Horizontal, isClosable=True, duration=1000,
296
+ position=InfoBarPosition.TOP_RIGHT, parent=None):
297
+ return cls.new(InfoBarIcon.ERROR, title, content, orient, isClosable, duration, position, parent)
298
+
299
+ @classmethod
300
+ def desktopView(cls):
301
+ """ Returns the desktop container """
302
+ if not cls._desktopView:
303
+ cls._desktopView = DesktopInfoBarView()
304
+ cls._desktopView.show()
305
+
306
+ return cls._desktopView
307
+
308
+
309
+ class InfoBarManager(QObject):
310
+ """ Info bar manager """
311
+
312
+ _instance = None
313
+ managers = {}
314
+
315
+ def __new__(cls, *args, **kwargs):
316
+ if cls._instance is None:
317
+ cls._instance = super(InfoBarManager, cls).__new__(
318
+ cls, *args, **kwargs)
319
+ cls._instance.__initialized = False
320
+
321
+ return cls._instance
322
+
323
+ def __init__(self):
324
+ if self.__initialized:
325
+ return
326
+
327
+ super().__init__()
328
+ self.spacing = 16
329
+ self.margin = 24
330
+ self.infoBars = weakref.WeakKeyDictionary()
331
+ self.aniGroups = weakref.WeakKeyDictionary()
332
+ self.slideAnis = []
333
+ self.dropAnis = []
334
+ self.__initialized = True
335
+
336
+ def add(self, infoBar: InfoBar):
337
+ """ add info bar """
338
+ p = infoBar.parent() # type:QWidget
339
+ if not p:
340
+ return
341
+
342
+ if p not in self.infoBars:
343
+ p.installEventFilter(self)
344
+ self.infoBars[p] = []
345
+ self.aniGroups[p] = QParallelAnimationGroup(self)
346
+
347
+ if infoBar in self.infoBars[p]:
348
+ return
349
+
350
+ # add drop animation
351
+ if self.infoBars[p]:
352
+ dropAni = QPropertyAnimation(infoBar, b'pos')
353
+ dropAni.setDuration(200)
354
+
355
+ self.aniGroups[p].addAnimation(dropAni)
356
+ self.dropAnis.append(dropAni)
357
+
358
+ infoBar.setProperty('dropAni', dropAni)
359
+
360
+ # add slide animation
361
+ self.infoBars[p].append(infoBar)
362
+ slideAni = self._createSlideAni(infoBar)
363
+ self.slideAnis.append(slideAni)
364
+
365
+ infoBar.setProperty('slideAni', slideAni)
366
+ infoBar.closedSignal.connect(lambda: self.remove(infoBar))
367
+
368
+ slideAni.start()
369
+
370
+ def remove(self, infoBar: InfoBar):
371
+ """ remove info bar """
372
+ p = infoBar.parent()
373
+ if p not in self.infoBars:
374
+ return
375
+
376
+ if infoBar not in self.infoBars[p]:
377
+ return
378
+
379
+ self.infoBars[p].remove(infoBar)
380
+
381
+ # remove drop animation
382
+ dropAni = infoBar.property('dropAni') # type: QPropertyAnimation
383
+ if dropAni:
384
+ self.aniGroups[p].removeAnimation(dropAni)
385
+ self.dropAnis.remove(dropAni)
386
+
387
+ # remove slider animation
388
+ slideAni = infoBar.property('slideAni')
389
+ if slideAni:
390
+ self.slideAnis.remove(slideAni)
391
+
392
+ # adjust the position of the remaining info bars
393
+ self._updateDropAni(p)
394
+ self.aniGroups[p].start()
395
+
396
+ def _createSlideAni(self, infoBar: InfoBar):
397
+ slideAni = QPropertyAnimation(infoBar, b'pos')
398
+ slideAni.setEasingCurve(QEasingCurve.OutQuad)
399
+ slideAni.setDuration(200)
400
+
401
+ slideAni.setStartValue(self._slideStartPos(infoBar))
402
+ slideAni.setEndValue(self._pos(infoBar))
403
+
404
+ return slideAni
405
+
406
+ def _updateDropAni(self, parent):
407
+ for bar in self.infoBars[parent]:
408
+ ani = bar.property('dropAni')
409
+ if not ani:
410
+ continue
411
+
412
+ ani.setStartValue(bar.pos())
413
+ ani.setEndValue(self._pos(bar))
414
+
415
+ def _pos(self, infoBar: InfoBar, parentSize=None) -> QPoint:
416
+ """ return the position of info bar """
417
+ raise NotImplementedError
418
+
419
+ def _slideStartPos(self, infoBar: InfoBar) -> QPoint:
420
+ """ return the start position of slide animation """
421
+ raise NotImplementedError
422
+
423
+ def eventFilter(self, obj, e: QEvent):
424
+ if obj not in self.infoBars:
425
+ return False
426
+
427
+ if e.type() in [QEvent.Resize, QEvent.WindowStateChange]:
428
+ size = e.size() if e.type() == QEvent.Resize else None
429
+ for bar in self.infoBars[obj]:
430
+ bar.move(self._pos(bar, size))
431
+
432
+ return super().eventFilter(obj, e)
433
+
434
+ @classmethod
435
+ def register(cls, name):
436
+ """ register menu animation manager
437
+
438
+ Parameters
439
+ ----------
440
+ name: Any
441
+ the name of manager, it should be unique
442
+ """
443
+ def wrapper(Manager):
444
+ if name not in cls.managers:
445
+ cls.managers[name] = Manager
446
+
447
+ return Manager
448
+
449
+ return wrapper
450
+
451
+ @classmethod
452
+ def make(cls, position: InfoBarPosition):
453
+ """ mask info bar manager according to the display position """
454
+ if position not in cls.managers:
455
+ raise ValueError(f'`{position}` is an invalid animation type.')
456
+
457
+ return cls.managers[position]()
458
+
459
+
460
+ @InfoBarManager.register(InfoBarPosition.TOP)
461
+ class TopInfoBarManager(InfoBarManager):
462
+ """ Top position info bar manager """
463
+
464
+ def _pos(self, infoBar: InfoBar, parentSize=None):
465
+ p = infoBar.parent()
466
+ parentSize = parentSize or p.size()
467
+
468
+ x = (infoBar.parent().width() - infoBar.width()) // 2
469
+ y = self.margin
470
+ index = self.infoBars[p].index(infoBar)
471
+ for bar in self.infoBars[p][0:index]:
472
+ y += (bar.height() + self.spacing)
473
+
474
+ return QPoint(x, y)
475
+
476
+ def _slideStartPos(self, infoBar: InfoBar):
477
+ pos = self._pos(infoBar)
478
+ return QPoint(pos.x(), pos.y() - 16)
479
+
480
+
481
+ @InfoBarManager.register(InfoBarPosition.TOP_RIGHT)
482
+ class TopRightInfoBarManager(InfoBarManager):
483
+ """ Top right position info bar manager """
484
+
485
+ def _pos(self, infoBar: InfoBar, parentSize=None):
486
+ p = infoBar.parent()
487
+ parentSize = parentSize or p.size()
488
+
489
+ x = parentSize.width() - infoBar.width() - self.margin
490
+ y = self.margin
491
+ index = self.infoBars[p].index(infoBar)
492
+ for bar in self.infoBars[p][0:index]:
493
+ y += (bar.height() + self.spacing)
494
+
495
+ return QPoint(x, y)
496
+
497
+ def _slideStartPos(self, infoBar: InfoBar):
498
+ return QPoint(infoBar.parent().width(), self._pos(infoBar).y())
499
+
500
+
501
+ @InfoBarManager.register(InfoBarPosition.BOTTOM_RIGHT)
502
+ class BottomRightInfoBarManager(InfoBarManager):
503
+ """ Bottom right position info bar manager """
504
+
505
+ def _pos(self, infoBar: InfoBar, parentSize=None) -> QPoint:
506
+ p = infoBar.parent()
507
+ parentSize = parentSize or p.size()
508
+
509
+ x = parentSize.width() - infoBar.width() - self.margin
510
+ y = parentSize.height() - infoBar.height() - self.margin
511
+
512
+ index = self.infoBars[p].index(infoBar)
513
+ for bar in self.infoBars[p][0:index]:
514
+ y -= (bar.height() + self.spacing)
515
+
516
+ return QPoint(x, y)
517
+
518
+ def _slideStartPos(self, infoBar: InfoBar):
519
+ return QPoint(infoBar.parent().width(), self._pos(infoBar).y())
520
+
521
+
522
+ @InfoBarManager.register(InfoBarPosition.TOP_LEFT)
523
+ class TopLeftInfoBarManager(InfoBarManager):
524
+ """ Top left position info bar manager """
525
+
526
+ def _pos(self, infoBar: InfoBar, parentSize=None) -> QPoint:
527
+ p = infoBar.parent()
528
+ parentSize = parentSize or p.size()
529
+
530
+ y = self.margin
531
+ index = self.infoBars[p].index(infoBar)
532
+
533
+ for bar in self.infoBars[p][0:index]:
534
+ y += (bar.height() + self.spacing)
535
+
536
+ return QPoint(self.margin, y)
537
+
538
+ def _slideStartPos(self, infoBar: InfoBar):
539
+ return QPoint(-infoBar.width(), self._pos(infoBar).y())
540
+
541
+
542
+ @InfoBarManager.register(InfoBarPosition.BOTTOM_LEFT)
543
+ class BottomLeftInfoBarManager(InfoBarManager):
544
+ """ Bottom left position info bar manager """
545
+
546
+ def _pos(self, infoBar: InfoBar, parentSize: QSize = None) -> QPoint:
547
+ p = infoBar.parent()
548
+ parentSize = parentSize or p.size()
549
+
550
+ y = parentSize.height() - infoBar.height() - self.margin
551
+ index = self.infoBars[p].index(infoBar)
552
+
553
+ for bar in self.infoBars[p][0:index]:
554
+ y -= (bar.height() + self.spacing)
555
+
556
+ return QPoint(self.margin, y)
557
+
558
+ def _slideStartPos(self, infoBar: InfoBar):
559
+ return QPoint(-infoBar.width(), self._pos(infoBar).y())
560
+
561
+
562
+ @InfoBarManager.register(InfoBarPosition.BOTTOM)
563
+ class BottomInfoBarManager(InfoBarManager):
564
+ """ Bottom position info bar manager """
565
+
566
+ def _pos(self, infoBar: InfoBar, parentSize: QSize = None) -> QPoint:
567
+ p = infoBar.parent()
568
+ parentSize = parentSize or p.size()
569
+
570
+ x = (parentSize.width() - infoBar.width()) // 2
571
+ y = parentSize.height() - infoBar.height() - self.margin
572
+ index = self.infoBars[p].index(infoBar)
573
+
574
+ for bar in self.infoBars[p][0:index]:
575
+ y -= (bar.height() + self.spacing)
576
+
577
+ return QPoint(x, y)
578
+
579
+ def _slideStartPos(self, infoBar: InfoBar):
580
+ pos = self._pos(infoBar)
581
+ return QPoint(pos.x(), pos.y() + 16)
582
+
583
+
584
+ class DesktopInfoBarView(QWidget):
585
+
586
+ def __init__(self, parent=None):
587
+ super().__init__(parent)
588
+
589
+ if sys.platform == "win32":
590
+ self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.SubWindow)
591
+ else:
592
+ self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool | Qt.WindowTransparentForInput)
593
+
594
+ self.setAttribute(Qt.WA_TransparentForMouseEvents)
595
+ self.setAttribute(Qt.WA_TranslucentBackground)
596
+ self.setGeometry(QApplication.primaryScreen().availableGeometry())