Python-FastUI-Widgets 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastuiwidgets/__init__.py +12 -0
- fastuiwidgets/_rc/__init__.py +0 -0
- fastuiwidgets/_rc/resource.py +98835 -0
- fastuiwidgets/common/__init__.py +12 -0
- fastuiwidgets/common/animation.py +530 -0
- fastuiwidgets/common/auto_wrap.py +164 -0
- fastuiwidgets/common/color.py +95 -0
- fastuiwidgets/common/config.py +423 -0
- fastuiwidgets/common/exception_handler.py +31 -0
- fastuiwidgets/common/font.py +38 -0
- fastuiwidgets/common/icon.py +703 -0
- fastuiwidgets/common/image_utils.py +198 -0
- fastuiwidgets/common/overload.py +47 -0
- fastuiwidgets/common/router.py +133 -0
- fastuiwidgets/common/screen.py +25 -0
- fastuiwidgets/common/smooth_scroll.py +141 -0
- fastuiwidgets/common/style_sheet.py +512 -0
- fastuiwidgets/common/theme_listener.py +27 -0
- fastuiwidgets/common/translator.py +14 -0
- fastuiwidgets/components/__init__.py +6 -0
- fastuiwidgets/components/date_time/__init__.py +4 -0
- fastuiwidgets/components/date_time/calendar_picker.py +121 -0
- fastuiwidgets/components/date_time/calendar_view.py +671 -0
- fastuiwidgets/components/date_time/date_picker.py +245 -0
- fastuiwidgets/components/date_time/fast_calendar_view.py +487 -0
- fastuiwidgets/components/date_time/picker_base.py +632 -0
- fastuiwidgets/components/date_time/time_picker.py +223 -0
- fastuiwidgets/components/dialog_box/__init__.py +6 -0
- fastuiwidgets/components/dialog_box/color_dialog.py +414 -0
- fastuiwidgets/components/dialog_box/dialog.py +167 -0
- fastuiwidgets/components/dialog_box/folder_list_dialog.py +307 -0
- fastuiwidgets/components/dialog_box/mask_dialog_base.py +120 -0
- fastuiwidgets/components/dialog_box/message_box_base.py +92 -0
- fastuiwidgets/components/dialog_box/message_dialog.py +65 -0
- fastuiwidgets/components/layout/__init__.py +3 -0
- fastuiwidgets/components/layout/expand_layout.py +96 -0
- fastuiwidgets/components/layout/flow_layout.py +236 -0
- fastuiwidgets/components/layout/v_box_layout.py +41 -0
- fastuiwidgets/components/material/__init__.py +6 -0
- fastuiwidgets/components/material/acrylic_combo_box.py +96 -0
- fastuiwidgets/components/material/acrylic_flyout.py +105 -0
- fastuiwidgets/components/material/acrylic_line_edit.py +27 -0
- fastuiwidgets/components/material/acrylic_menu.py +204 -0
- fastuiwidgets/components/material/acrylic_tool_tip.py +39 -0
- fastuiwidgets/components/material/acrylic_widget.py +42 -0
- fastuiwidgets/components/navigation/__init__.py +9 -0
- fastuiwidgets/components/navigation/breadcrumb.py +350 -0
- fastuiwidgets/components/navigation/navigation_bar.py +416 -0
- fastuiwidgets/components/navigation/navigation_interface.py +268 -0
- fastuiwidgets/components/navigation/navigation_panel.py +657 -0
- fastuiwidgets/components/navigation/navigation_widget.py +686 -0
- fastuiwidgets/components/navigation/pivot.py +272 -0
- fastuiwidgets/components/navigation/segmented_widget.py +174 -0
- fastuiwidgets/components/settings/__init__.py +8 -0
- fastuiwidgets/components/settings/custom_color_setting_card.py +139 -0
- fastuiwidgets/components/settings/expand_setting_card.py +390 -0
- fastuiwidgets/components/settings/folder_list_setting_card.py +134 -0
- fastuiwidgets/components/settings/options_setting_card.py +86 -0
- fastuiwidgets/components/settings/setting_card.py +449 -0
- fastuiwidgets/components/settings/setting_card_group.py +48 -0
- fastuiwidgets/components/widgets/__init__.py +41 -0
- fastuiwidgets/components/widgets/acrylic_label.py +261 -0
- fastuiwidgets/components/widgets/button.py +1059 -0
- fastuiwidgets/components/widgets/card_widget.py +369 -0
- fastuiwidgets/components/widgets/check_box.py +203 -0
- fastuiwidgets/components/widgets/combo_box.py +556 -0
- fastuiwidgets/components/widgets/command_bar.py +636 -0
- fastuiwidgets/components/widgets/cycle_list_widget.py +251 -0
- fastuiwidgets/components/widgets/flip_view.py +430 -0
- fastuiwidgets/components/widgets/flyout.py +521 -0
- fastuiwidgets/components/widgets/frameless_window.py +49 -0
- fastuiwidgets/components/widgets/icon_widget.py +53 -0
- fastuiwidgets/components/widgets/info_badge.py +483 -0
- fastuiwidgets/components/widgets/info_bar.py +596 -0
- fastuiwidgets/components/widgets/label.py +553 -0
- fastuiwidgets/components/widgets/line_edit.py +551 -0
- fastuiwidgets/components/widgets/list_view.py +158 -0
- fastuiwidgets/components/widgets/menu.py +1318 -0
- fastuiwidgets/components/widgets/pips_pager.py +331 -0
- fastuiwidgets/components/widgets/progress_bar.py +311 -0
- fastuiwidgets/components/widgets/progress_ring.py +212 -0
- fastuiwidgets/components/widgets/scroll_area.py +125 -0
- fastuiwidgets/components/widgets/scroll_bar.py +673 -0
- fastuiwidgets/components/widgets/separator.py +43 -0
- fastuiwidgets/components/widgets/slider.py +307 -0
- fastuiwidgets/components/widgets/spin_box.py +306 -0
- fastuiwidgets/components/widgets/stacked_widget.py +211 -0
- fastuiwidgets/components/widgets/state_tool_tip.py +188 -0
- fastuiwidgets/components/widgets/switch_button.py +312 -0
- fastuiwidgets/components/widgets/tab_view.py +804 -0
- fastuiwidgets/components/widgets/table_view.py +360 -0
- fastuiwidgets/components/widgets/teaching_tip.py +657 -0
- fastuiwidgets/components/widgets/tool_tip.py +460 -0
- fastuiwidgets/components/widgets/tree_view.py +216 -0
- fastuiwidgets/multimedia/__init__.py +3 -0
- fastuiwidgets/multimedia/media_play_bar.py +319 -0
- fastuiwidgets/multimedia/media_player.py +124 -0
- fastuiwidgets/multimedia/video_widget.py +93 -0
- fastuiwidgets/window/__init__.py +2 -0
- fastuiwidgets/window/fluent_window.py +413 -0
- fastuiwidgets/window/splash_screen.py +92 -0
- fastuiwidgets/window/stacked_widget.py +66 -0
- python_fastui_widgets-1.0.0.dist-info/METADATA +30 -0
- python_fastui_widgets-1.0.0.dist-info/RECORD +107 -0
- python_fastui_widgets-1.0.0.dist-info/WHEEL +5 -0
- python_fastui_widgets-1.0.0.dist-info/licenses/LICENSE +674 -0
- python_fastui_widgets-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,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())
|