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,521 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from enum import Enum
|
3
|
+
import sys
|
4
|
+
from typing import Union
|
5
|
+
|
6
|
+
from PySide6.QtCore import (Qt, QPropertyAnimation, QPoint, QParallelAnimationGroup, QEasingCurve, QMargins,
|
7
|
+
QRectF, QObject, QSize, Signal, QEvent)
|
8
|
+
from PySide6.QtGui import QPixmap, QPainter, QColor, QCursor, QIcon, QImage, QPainterPath, QBrush, QMovie, QImageReader
|
9
|
+
from PySide6.QtWidgets import QWidget, QGraphicsDropShadowEffect, QLabel, QHBoxLayout, QVBoxLayout, QApplication
|
10
|
+
|
11
|
+
from ...common.auto_wrap import TextWrap
|
12
|
+
from ...common.style_sheet import isDarkTheme, FluentStyleSheet
|
13
|
+
from ...common.icon import FluentIconBase, drawIcon, FluentIcon
|
14
|
+
from ...common.screen import getCurrentScreenGeometry
|
15
|
+
from .button import TransparentToolButton
|
16
|
+
from .label import ImageLabel
|
17
|
+
|
18
|
+
|
19
|
+
class FlyoutAnimationType(Enum):
|
20
|
+
""" Flyout animation type """
|
21
|
+
PULL_UP = 0
|
22
|
+
DROP_DOWN = 1
|
23
|
+
SLIDE_LEFT = 2
|
24
|
+
SLIDE_RIGHT = 3
|
25
|
+
FADE_IN = 4
|
26
|
+
NONE = 5
|
27
|
+
|
28
|
+
|
29
|
+
class IconWidget(QWidget):
|
30
|
+
|
31
|
+
def __init__(self, icon, parent=None):
|
32
|
+
super().__init__(parent=parent)
|
33
|
+
self.setFixedSize(36, 54)
|
34
|
+
self.icon = icon
|
35
|
+
|
36
|
+
def paintEvent(self, e):
|
37
|
+
if not self.icon:
|
38
|
+
return
|
39
|
+
|
40
|
+
painter = QPainter(self)
|
41
|
+
painter.setRenderHints(QPainter.Antialiasing |
|
42
|
+
QPainter.SmoothPixmapTransform)
|
43
|
+
|
44
|
+
rect = QRectF(8, (self.height()-20)/2, 20, 20)
|
45
|
+
drawIcon(self.icon, painter, rect)
|
46
|
+
|
47
|
+
|
48
|
+
class FlyoutViewBase(QWidget):
|
49
|
+
""" Flyout view base class """
|
50
|
+
|
51
|
+
def __init__(self, parent=None):
|
52
|
+
super().__init__(parent=parent)
|
53
|
+
|
54
|
+
def addWidget(self, widget: QWidget, stretch=0, align=Qt.AlignLeft):
|
55
|
+
raise NotImplementedError
|
56
|
+
|
57
|
+
def backgroundColor(self):
|
58
|
+
return QColor(40, 40, 40) if isDarkTheme() else QColor(248, 248, 248)
|
59
|
+
|
60
|
+
def borderColor(self):
|
61
|
+
return QColor(0, 0, 0, 45) if isDarkTheme() else QColor(0, 0, 0, 17)
|
62
|
+
|
63
|
+
def paintEvent(self, e):
|
64
|
+
painter = QPainter(self)
|
65
|
+
painter.setRenderHints(QPainter.Antialiasing)
|
66
|
+
|
67
|
+
painter.setBrush(self.backgroundColor())
|
68
|
+
painter.setPen(self.borderColor())
|
69
|
+
|
70
|
+
rect = self.rect().adjusted(1, 1, -1, -1)
|
71
|
+
painter.drawRoundedRect(rect, 8, 8)
|
72
|
+
|
73
|
+
|
74
|
+
class FlyoutView(FlyoutViewBase):
|
75
|
+
""" Flyout view """
|
76
|
+
|
77
|
+
closed = Signal()
|
78
|
+
|
79
|
+
def __init__(self, title: str, content: str, icon: Union[FluentIconBase, QIcon, str] = None,
|
80
|
+
image: Union[str, QPixmap, QImage] = None, isClosable=False, parent=None):
|
81
|
+
super().__init__(parent=parent)
|
82
|
+
"""
|
83
|
+
Parameters
|
84
|
+
----------
|
85
|
+
title: str
|
86
|
+
the title of teaching tip
|
87
|
+
|
88
|
+
content: str
|
89
|
+
the content of teaching tip
|
90
|
+
|
91
|
+
icon: InfoBarIcon | FluentIconBase | QIcon | str
|
92
|
+
the icon of teaching tip
|
93
|
+
|
94
|
+
image: str | QPixmap | QImage
|
95
|
+
the image of teaching tip
|
96
|
+
|
97
|
+
isClosable: bool
|
98
|
+
whether to show the close button
|
99
|
+
|
100
|
+
parent: QWidget
|
101
|
+
parent widget
|
102
|
+
"""
|
103
|
+
self.icon = icon
|
104
|
+
self.title = title
|
105
|
+
self.image = image
|
106
|
+
self.content = content
|
107
|
+
self.isClosable = isClosable
|
108
|
+
|
109
|
+
self.vBoxLayout = QVBoxLayout(self)
|
110
|
+
self.viewLayout = QHBoxLayout()
|
111
|
+
self.widgetLayout = QVBoxLayout()
|
112
|
+
|
113
|
+
self.titleLabel = QLabel(title, self)
|
114
|
+
self.contentLabel = QLabel(content, self)
|
115
|
+
self.iconWidget = IconWidget(icon, self)
|
116
|
+
self.imageLabel = ImageLabel(self)
|
117
|
+
self.closeButton = TransparentToolButton(FluentIcon.CLOSE, self)
|
118
|
+
|
119
|
+
self.__initWidgets()
|
120
|
+
|
121
|
+
def __initWidgets(self):
|
122
|
+
self.imageLabel.setImage(self.image)
|
123
|
+
|
124
|
+
self.closeButton.setFixedSize(32, 32)
|
125
|
+
self.closeButton.setIconSize(QSize(12, 12))
|
126
|
+
self.closeButton.setVisible(self.isClosable)
|
127
|
+
self.titleLabel.setVisible(bool(self.title))
|
128
|
+
self.contentLabel.setVisible(bool(self.content))
|
129
|
+
self.iconWidget.setHidden(self.icon is None)
|
130
|
+
|
131
|
+
self.closeButton.clicked.connect(self.closed)
|
132
|
+
|
133
|
+
self.titleLabel.setObjectName('titleLabel')
|
134
|
+
self.contentLabel.setObjectName('contentLabel')
|
135
|
+
FluentStyleSheet.TEACHING_TIP.apply(self)
|
136
|
+
|
137
|
+
self.__initLayout()
|
138
|
+
|
139
|
+
def __initLayout(self):
|
140
|
+
self.vBoxLayout.setContentsMargins(1, 1, 1, 1)
|
141
|
+
self.widgetLayout.setContentsMargins(0, 8, 0, 8)
|
142
|
+
self.viewLayout.setSpacing(4)
|
143
|
+
self.widgetLayout.setSpacing(0)
|
144
|
+
self.vBoxLayout.setSpacing(0)
|
145
|
+
|
146
|
+
# add icon widget
|
147
|
+
if not self.title or not self.content:
|
148
|
+
self.iconWidget.setFixedHeight(36)
|
149
|
+
|
150
|
+
self.vBoxLayout.addLayout(self.viewLayout)
|
151
|
+
self.viewLayout.addWidget(self.iconWidget, 0, Qt.AlignTop)
|
152
|
+
|
153
|
+
# add text
|
154
|
+
self._adjustText()
|
155
|
+
self.widgetLayout.addWidget(self.titleLabel)
|
156
|
+
self.widgetLayout.addWidget(self.contentLabel)
|
157
|
+
self.viewLayout.addLayout(self.widgetLayout)
|
158
|
+
|
159
|
+
# add close button
|
160
|
+
self.closeButton.setVisible(self.isClosable)
|
161
|
+
self.viewLayout.addWidget(
|
162
|
+
self.closeButton, 0, Qt.AlignRight | Qt.AlignTop)
|
163
|
+
|
164
|
+
# adjust content margins
|
165
|
+
margins = QMargins(6, 5, 6, 5)
|
166
|
+
margins.setLeft(20 if not self.icon else 5)
|
167
|
+
margins.setRight(20 if not self.isClosable else 6)
|
168
|
+
self.viewLayout.setContentsMargins(margins)
|
169
|
+
|
170
|
+
# add image
|
171
|
+
self._adjustImage()
|
172
|
+
self._addImageToLayout()
|
173
|
+
|
174
|
+
def addWidget(self, widget: QWidget, stretch=0, align=Qt.AlignLeft):
|
175
|
+
""" add widget to view """
|
176
|
+
self.widgetLayout.addSpacing(8)
|
177
|
+
self.widgetLayout.addWidget(widget, stretch, align)
|
178
|
+
|
179
|
+
def _addImageToLayout(self):
|
180
|
+
self.imageLabel.setBorderRadius(8, 8, 0, 0)
|
181
|
+
self.imageLabel.setHidden(self.imageLabel.isNull())
|
182
|
+
self.vBoxLayout.insertWidget(0, self.imageLabel)
|
183
|
+
|
184
|
+
def _adjustText(self):
|
185
|
+
w = min(900, QApplication.screenAt(
|
186
|
+
QCursor.pos()).geometry().width() - 200)
|
187
|
+
|
188
|
+
# adjust title
|
189
|
+
chars = max(min(w / 10, 120), 30)
|
190
|
+
self.titleLabel.setText(TextWrap.wrap(self.title, chars, False)[0])
|
191
|
+
|
192
|
+
# adjust content
|
193
|
+
chars = max(min(w / 9, 120), 30)
|
194
|
+
self.contentLabel.setText(TextWrap.wrap(self.content, chars, False)[0])
|
195
|
+
|
196
|
+
def _adjustImage(self):
|
197
|
+
w = self.vBoxLayout.sizeHint().width() - 2
|
198
|
+
self.imageLabel.scaledToWidth(w)
|
199
|
+
|
200
|
+
def showEvent(self, e):
|
201
|
+
super().showEvent(e)
|
202
|
+
self._adjustImage()
|
203
|
+
self.adjustSize()
|
204
|
+
|
205
|
+
|
206
|
+
class Flyout(QWidget):
|
207
|
+
""" Flyout """
|
208
|
+
|
209
|
+
closed = Signal()
|
210
|
+
|
211
|
+
def __init__(self, view: FlyoutViewBase, parent=None, isDeleteOnClose=True, isMacInputMethodEnabled=False):
|
212
|
+
super().__init__(parent=parent)
|
213
|
+
self.view = view
|
214
|
+
self.hBoxLayout = QHBoxLayout(self)
|
215
|
+
self.aniManager = None # type: FlyoutAnimationManager
|
216
|
+
self.isDeleteOnClose = isDeleteOnClose
|
217
|
+
self.isMacInputMethodEnabled = isMacInputMethodEnabled
|
218
|
+
|
219
|
+
self.hBoxLayout.setContentsMargins(15, 8, 15, 20)
|
220
|
+
self.hBoxLayout.addWidget(self.view)
|
221
|
+
self.setShadowEffect()
|
222
|
+
|
223
|
+
self.setAttribute(Qt.WA_TranslucentBackground)
|
224
|
+
|
225
|
+
if sys.platform != "darwin" or not isMacInputMethodEnabled:
|
226
|
+
self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint |
|
227
|
+
Qt.NoDropShadowWindowHint)
|
228
|
+
else:
|
229
|
+
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint)
|
230
|
+
QApplication.instance().installEventFilter(self)
|
231
|
+
|
232
|
+
def eventFilter(self, watched, event):
|
233
|
+
if sys.platform == "darwin" and self.isMacInputMethodEnabled:
|
234
|
+
if self.isVisible() and event.type() == QEvent.MouseButtonPress:
|
235
|
+
if not self.rect().contains(self.mapFromGlobal(event.globalPos())):
|
236
|
+
self.close()
|
237
|
+
|
238
|
+
return super().eventFilter(watched, event)
|
239
|
+
|
240
|
+
def setShadowEffect(self, blurRadius=35, offset=(0, 8)):
|
241
|
+
""" add shadow to dialog """
|
242
|
+
color = QColor(0, 0, 0, 80 if isDarkTheme() else 30)
|
243
|
+
self.shadowEffect = QGraphicsDropShadowEffect(self.view)
|
244
|
+
self.shadowEffect.setBlurRadius(blurRadius)
|
245
|
+
self.shadowEffect.setOffset(*offset)
|
246
|
+
self.shadowEffect.setColor(color)
|
247
|
+
self.view.setGraphicsEffect(None)
|
248
|
+
self.view.setGraphicsEffect(self.shadowEffect)
|
249
|
+
|
250
|
+
def closeEvent(self, e):
|
251
|
+
if self.isDeleteOnClose:
|
252
|
+
self.deleteLater()
|
253
|
+
|
254
|
+
super().closeEvent(e)
|
255
|
+
self.closed.emit()
|
256
|
+
|
257
|
+
def showEvent(self, e):
|
258
|
+
# fixes #780
|
259
|
+
self.activateWindow()
|
260
|
+
super().showEvent(e)
|
261
|
+
|
262
|
+
def exec(self, pos: QPoint, aniType=FlyoutAnimationType.PULL_UP):
|
263
|
+
""" show calendar view """
|
264
|
+
self.aniManager = FlyoutAnimationManager.make(aniType, self)
|
265
|
+
self.show()
|
266
|
+
self.aniManager.exec(pos)
|
267
|
+
|
268
|
+
@classmethod
|
269
|
+
def make(cls, view: FlyoutViewBase, target: Union[QWidget, QPoint] = None, parent=None,
|
270
|
+
aniType=FlyoutAnimationType.PULL_UP, isDeleteOnClose=True, isMacInputMethodEnabled=False):
|
271
|
+
""" create and show a flyout
|
272
|
+
|
273
|
+
Parameters
|
274
|
+
----------
|
275
|
+
view: FlyoutViewBase
|
276
|
+
flyout view
|
277
|
+
|
278
|
+
target: QWidget | QPoint
|
279
|
+
the target widget or position to show flyout
|
280
|
+
|
281
|
+
parent: QWidget
|
282
|
+
parent window
|
283
|
+
|
284
|
+
aniType: FlyoutAnimationType
|
285
|
+
flyout animation type
|
286
|
+
|
287
|
+
isDeleteOnClose: bool
|
288
|
+
whether delete flyout automatically when flyout is closed
|
289
|
+
"""
|
290
|
+
w = cls(view, parent, isDeleteOnClose, isMacInputMethodEnabled)
|
291
|
+
|
292
|
+
if target is None:
|
293
|
+
return w
|
294
|
+
|
295
|
+
# show flyout first so that we can get the correct size
|
296
|
+
w.show()
|
297
|
+
|
298
|
+
# move flyout to the top of target
|
299
|
+
if isinstance(target, QWidget):
|
300
|
+
target = FlyoutAnimationManager.make(aniType, w).position(target)
|
301
|
+
|
302
|
+
w.exec(target, aniType)
|
303
|
+
return w
|
304
|
+
|
305
|
+
@classmethod
|
306
|
+
def create(cls, title: str, content: str, icon: Union[FluentIconBase, QIcon, str] = None,
|
307
|
+
image: Union[str, QPixmap, QImage] = None, isClosable=False, target: Union[QWidget, QPoint] = None,
|
308
|
+
parent=None, aniType=FlyoutAnimationType.PULL_UP, isDeleteOnClose=True, isMacInputMethodEnabled=False):
|
309
|
+
""" create and show a flyout using the default view
|
310
|
+
|
311
|
+
Parameters
|
312
|
+
----------
|
313
|
+
title: str
|
314
|
+
the title of teaching tip
|
315
|
+
|
316
|
+
content: str
|
317
|
+
the content of teaching tip
|
318
|
+
|
319
|
+
icon: InfoBarIcon | FluentIconBase | QIcon | str
|
320
|
+
the icon of teaching tip
|
321
|
+
|
322
|
+
image: str | QPixmap | QImage
|
323
|
+
the image of teaching tip
|
324
|
+
|
325
|
+
isClosable: bool
|
326
|
+
whether to show the close button
|
327
|
+
|
328
|
+
target: QWidget | QPoint
|
329
|
+
the target widget or position to show flyout
|
330
|
+
|
331
|
+
parent: QWidget
|
332
|
+
parent window
|
333
|
+
|
334
|
+
aniType: FlyoutAnimationType
|
335
|
+
flyout animation type
|
336
|
+
|
337
|
+
isDeleteOnClose: bool
|
338
|
+
whether delete flyout automatically when flyout is closed
|
339
|
+
"""
|
340
|
+
view = FlyoutView(title, content, icon, image, isClosable)
|
341
|
+
w = cls.make(view, target, parent, aniType, isDeleteOnClose, isMacInputMethodEnabled)
|
342
|
+
view.closed.connect(w.close)
|
343
|
+
return w
|
344
|
+
|
345
|
+
def fadeOut(self):
|
346
|
+
self.fadeOutAni = QPropertyAnimation(self, b'windowOpacity', self)
|
347
|
+
self.fadeOutAni.finished.connect(self.close)
|
348
|
+
self.fadeOutAni.setStartValue(1)
|
349
|
+
self.fadeOutAni.setEndValue(0)
|
350
|
+
self.fadeOutAni.setDuration(120)
|
351
|
+
self.fadeOutAni.start()
|
352
|
+
|
353
|
+
|
354
|
+
class FlyoutAnimationManager(QObject):
|
355
|
+
""" Flyout animation manager """
|
356
|
+
|
357
|
+
managers = {}
|
358
|
+
|
359
|
+
def __init__(self, flyout: Flyout):
|
360
|
+
super().__init__()
|
361
|
+
self.flyout = flyout
|
362
|
+
self.aniGroup = QParallelAnimationGroup(self)
|
363
|
+
self.slideAni = QPropertyAnimation(flyout, b'pos', self)
|
364
|
+
self.opacityAni = QPropertyAnimation(flyout, b'windowOpacity', self)
|
365
|
+
|
366
|
+
self.slideAni.setDuration(187)
|
367
|
+
self.opacityAni.setDuration(187)
|
368
|
+
|
369
|
+
self.opacityAni.setStartValue(0)
|
370
|
+
self.opacityAni.setEndValue(1)
|
371
|
+
|
372
|
+
self.slideAni.setEasingCurve(QEasingCurve.OutQuad)
|
373
|
+
self.opacityAni.setEasingCurve(QEasingCurve.OutQuad)
|
374
|
+
self.aniGroup.addAnimation(self.slideAni)
|
375
|
+
self.aniGroup.addAnimation(self.opacityAni)
|
376
|
+
|
377
|
+
@classmethod
|
378
|
+
def register(cls, name):
|
379
|
+
""" register menu animation manager
|
380
|
+
|
381
|
+
Parameters
|
382
|
+
----------
|
383
|
+
name: Any
|
384
|
+
the name of manager, it should be unique
|
385
|
+
"""
|
386
|
+
def wrapper(Manager):
|
387
|
+
if name not in cls.managers:
|
388
|
+
cls.managers[name] = Manager
|
389
|
+
|
390
|
+
return Manager
|
391
|
+
|
392
|
+
return wrapper
|
393
|
+
|
394
|
+
def exec(self, pos: QPoint):
|
395
|
+
""" start animation """
|
396
|
+
raise NotImplementedError
|
397
|
+
|
398
|
+
def _adjustPosition(self, pos):
|
399
|
+
rect = getCurrentScreenGeometry()
|
400
|
+
w, h = self.flyout.sizeHint().width() + 5, self.flyout.sizeHint().height()
|
401
|
+
x = max(rect.left(), min(pos.x(), rect.right() - w))
|
402
|
+
y = max(rect.top(), min(pos.y() - 4, rect.bottom() - h + 5))
|
403
|
+
return QPoint(x, y)
|
404
|
+
|
405
|
+
def position(self, target: QWidget):
|
406
|
+
""" return the top left position relative to the target """
|
407
|
+
raise NotImplementedError
|
408
|
+
|
409
|
+
@classmethod
|
410
|
+
def make(cls, aniType: FlyoutAnimationType, flyout: Flyout) -> "FlyoutAnimationManager":
|
411
|
+
""" mask animation manager """
|
412
|
+
if aniType not in cls.managers:
|
413
|
+
raise ValueError(f'`{aniType}` is an invalid animation type.')
|
414
|
+
|
415
|
+
return cls.managers[aniType](flyout)
|
416
|
+
|
417
|
+
|
418
|
+
@FlyoutAnimationManager.register(FlyoutAnimationType.PULL_UP)
|
419
|
+
class PullUpFlyoutAnimationManager(FlyoutAnimationManager):
|
420
|
+
""" Pull up flyout animation manager """
|
421
|
+
|
422
|
+
def position(self, target: QWidget):
|
423
|
+
w = self.flyout
|
424
|
+
pos = target.mapToGlobal(QPoint())
|
425
|
+
x = pos.x() + target.width()//2 - w.sizeHint().width()//2
|
426
|
+
y = pos.y() - w.sizeHint().height() + w.layout().contentsMargins().bottom()
|
427
|
+
return QPoint(x, y)
|
428
|
+
|
429
|
+
def exec(self, pos: QPoint):
|
430
|
+
pos = self._adjustPosition(pos)
|
431
|
+
self.slideAni.setStartValue(pos+QPoint(0, 8))
|
432
|
+
self.slideAni.setEndValue(pos)
|
433
|
+
self.aniGroup.start()
|
434
|
+
|
435
|
+
|
436
|
+
@FlyoutAnimationManager.register(FlyoutAnimationType.DROP_DOWN)
|
437
|
+
class DropDownFlyoutAnimationManager(FlyoutAnimationManager):
|
438
|
+
""" Drop down flyout animation manager """
|
439
|
+
|
440
|
+
def position(self, target: QWidget):
|
441
|
+
w = self.flyout
|
442
|
+
pos = target.mapToGlobal(QPoint(0, target.height()))
|
443
|
+
x = pos.x() + target.width()//2 - w.sizeHint().width()//2
|
444
|
+
y = pos.y() - w.layout().contentsMargins().top() + 8
|
445
|
+
return QPoint(x, y)
|
446
|
+
|
447
|
+
def exec(self, pos: QPoint):
|
448
|
+
pos = self._adjustPosition(pos)
|
449
|
+
self.slideAni.setStartValue(pos-QPoint(0, 8))
|
450
|
+
self.slideAni.setEndValue(pos)
|
451
|
+
self.aniGroup.start()
|
452
|
+
|
453
|
+
|
454
|
+
@FlyoutAnimationManager.register(FlyoutAnimationType.SLIDE_LEFT)
|
455
|
+
class SlideLeftFlyoutAnimationManager(FlyoutAnimationManager):
|
456
|
+
""" Slide left flyout animation manager """
|
457
|
+
|
458
|
+
def position(self, target: QWidget):
|
459
|
+
w = self.flyout
|
460
|
+
pos = target.mapToGlobal(QPoint(0, 0))
|
461
|
+
x = pos.x() - w.sizeHint().width() + 8
|
462
|
+
y = pos.y() - w.sizeHint().height()//2 + target.height()//2 + \
|
463
|
+
w.layout().contentsMargins().top()
|
464
|
+
return QPoint(x, y)
|
465
|
+
|
466
|
+
def exec(self, pos: QPoint):
|
467
|
+
pos = self._adjustPosition(pos)
|
468
|
+
self.slideAni.setStartValue(pos+QPoint(8, 0))
|
469
|
+
self.slideAni.setEndValue(pos)
|
470
|
+
self.aniGroup.start()
|
471
|
+
|
472
|
+
|
473
|
+
@FlyoutAnimationManager.register(FlyoutAnimationType.SLIDE_RIGHT)
|
474
|
+
class SlideRightFlyoutAnimationManager(FlyoutAnimationManager):
|
475
|
+
""" Slide right flyout animation manager """
|
476
|
+
|
477
|
+
def position(self, target: QWidget):
|
478
|
+
w = self.flyout
|
479
|
+
pos = target.mapToGlobal(QPoint(0, 0))
|
480
|
+
x = pos.x() + target.width() - 8
|
481
|
+
y = pos.y() - w.sizeHint().height()//2 + target.height()//2 + \
|
482
|
+
w.layout().contentsMargins().top()
|
483
|
+
return QPoint(x, y)
|
484
|
+
|
485
|
+
def exec(self, pos: QPoint):
|
486
|
+
pos = self._adjustPosition(pos)
|
487
|
+
self.slideAni.setStartValue(pos-QPoint(8, 0))
|
488
|
+
self.slideAni.setEndValue(pos)
|
489
|
+
self.aniGroup.start()
|
490
|
+
|
491
|
+
|
492
|
+
@FlyoutAnimationManager.register(FlyoutAnimationType.FADE_IN)
|
493
|
+
class FadeInFlyoutAnimationManager(FlyoutAnimationManager):
|
494
|
+
""" Fade in flyout animation manager """
|
495
|
+
|
496
|
+
def position(self, target: QWidget):
|
497
|
+
w = self.flyout
|
498
|
+
pos = target.mapToGlobal(QPoint())
|
499
|
+
x = pos.x() + target.width()//2 - w.sizeHint().width()//2
|
500
|
+
y = pos.y() - w.sizeHint().height() + w.layout().contentsMargins().bottom()
|
501
|
+
return QPoint(x, y)
|
502
|
+
|
503
|
+
def exec(self, pos: QPoint):
|
504
|
+
self.flyout.move(self._adjustPosition(pos))
|
505
|
+
self.aniGroup.removeAnimation(self.slideAni)
|
506
|
+
self.aniGroup.start()
|
507
|
+
|
508
|
+
|
509
|
+
|
510
|
+
@FlyoutAnimationManager.register(FlyoutAnimationType.NONE)
|
511
|
+
class DummyFlyoutAnimationManager(FlyoutAnimationManager):
|
512
|
+
""" Dummy flyout animation manager """
|
513
|
+
|
514
|
+
def exec(self, pos: QPoint):
|
515
|
+
""" start animation """
|
516
|
+
self.flyout.move(self._adjustPosition(pos))
|
517
|
+
|
518
|
+
def position(self, target: QWidget):
|
519
|
+
""" return the top left position relative to the target """
|
520
|
+
m = self.flyout.hBoxLayout.contentsMargins()
|
521
|
+
return target.mapToGlobal(QPoint(-m.left(), -self.flyout.sizeHint().height()+m.bottom()-8))
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import sys
|
2
|
+
|
3
|
+
if sys.platform != "win32" or sys.getwindowsversion().build < 22000:
|
4
|
+
from fframelesswindow import FramelessWindow
|
5
|
+
else:
|
6
|
+
from ctypes.wintypes import MSG
|
7
|
+
|
8
|
+
import win32con
|
9
|
+
from PySide6.QtCore import QPoint, QEvent, Qt
|
10
|
+
from PySide6.QtGui import QCursor, QMouseEvent
|
11
|
+
from PySide6.QtWidgets import QApplication
|
12
|
+
|
13
|
+
from fframelesswindow import AcrylicWindow as Window
|
14
|
+
from fframelesswindow.titlebar.title_bar_buttons import TitleBarButtonState
|
15
|
+
|
16
|
+
|
17
|
+
class FramelessWindow(Window):
|
18
|
+
""" Frameless window """
|
19
|
+
|
20
|
+
def __init__(self, parent=None):
|
21
|
+
super().__init__(parent)
|
22
|
+
self.windowEffect.setMicaEffect(self.winId())
|
23
|
+
|
24
|
+
def nativeEvent(self, eventType, message):
|
25
|
+
""" Handle the Windows message """
|
26
|
+
msg = MSG.from_address(message.__int__())
|
27
|
+
if not msg.hWnd:
|
28
|
+
return super().nativeEvent(eventType, message)
|
29
|
+
|
30
|
+
if msg.message == win32con.WM_NCHITTEST and self._isResizeEnabled:
|
31
|
+
if self._isHoverMaxBtn():
|
32
|
+
self.titleBar.maxBtn.setState(TitleBarButtonState.HOVER)
|
33
|
+
return True, win32con.HTMAXBUTTON
|
34
|
+
|
35
|
+
elif msg.message in [0x2A2, win32con.WM_MOUSELEAVE]:
|
36
|
+
self.titleBar.maxBtn.setState(TitleBarButtonState.NORMAL)
|
37
|
+
elif msg.message in [win32con.WM_NCLBUTTONDOWN, win32con.WM_NCLBUTTONDBLCLK] and self._isHoverMaxBtn():
|
38
|
+
e = QMouseEvent(QEvent.MouseButtonPress, QPoint(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
|
39
|
+
QApplication.sendEvent(self.titleBar.maxBtn, e)
|
40
|
+
return True, 0
|
41
|
+
elif msg.message in [win32con.WM_NCLBUTTONUP, win32con.WM_NCRBUTTONUP] and self._isHoverMaxBtn():
|
42
|
+
e = QMouseEvent(QEvent.MouseButtonRelease, QPoint(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
|
43
|
+
QApplication.sendEvent(self.titleBar.maxBtn, e)
|
44
|
+
|
45
|
+
return super().nativeEvent(eventType, message)
|
46
|
+
|
47
|
+
def _isHoverMaxBtn(self):
|
48
|
+
pos = QCursor.pos() - self.geometry().topLeft() - self.titleBar.pos()
|
49
|
+
return self.titleBar.childAt(pos) is self.titleBar.maxBtn
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from typing import Union
|
3
|
+
|
4
|
+
from PySide6.QtCore import Property
|
5
|
+
from PySide6.QtGui import QIcon, QPainter
|
6
|
+
from PySide6.QtWidgets import QWidget
|
7
|
+
|
8
|
+
from ...common.icon import FluentIconBase, drawIcon, toQIcon
|
9
|
+
from ...common.overload import singledispatchmethod
|
10
|
+
|
11
|
+
|
12
|
+
class IconWidget(QWidget):
|
13
|
+
""" Icon widget
|
14
|
+
|
15
|
+
Constructors
|
16
|
+
------------
|
17
|
+
* IconWidget(`parent`: QWidget = None)
|
18
|
+
* IconWidget(`icon`: QIcon | str | FluentIconBase, `parent`: QWidget = None)
|
19
|
+
"""
|
20
|
+
|
21
|
+
@singledispatchmethod
|
22
|
+
def __init__(self, parent=None):
|
23
|
+
super().__init__(parent)
|
24
|
+
self.setIcon(QIcon())
|
25
|
+
|
26
|
+
@__init__.register
|
27
|
+
def _(self, icon: FluentIconBase, parent: QWidget = None):
|
28
|
+
self.__init__(parent)
|
29
|
+
self.setIcon(icon)
|
30
|
+
|
31
|
+
@__init__.register
|
32
|
+
def _(self, icon: QIcon, parent: QWidget = None):
|
33
|
+
self.__init__(parent)
|
34
|
+
self.setIcon(icon)
|
35
|
+
|
36
|
+
@__init__.register
|
37
|
+
def _(self, icon: str, parent: QWidget = None):
|
38
|
+
self.__init__(parent)
|
39
|
+
self.setIcon(icon)
|
40
|
+
|
41
|
+
def getIcon(self):
|
42
|
+
return toQIcon(self._icon)
|
43
|
+
|
44
|
+
def setIcon(self, icon: Union[str, QIcon, FluentIconBase]):
|
45
|
+
self._icon = icon
|
46
|
+
self.update()
|
47
|
+
|
48
|
+
def paintEvent(self, e):
|
49
|
+
painter = QPainter(self)
|
50
|
+
painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
|
51
|
+
drawIcon(self._icon, painter, self.rect())
|
52
|
+
|
53
|
+
icon = Property(QIcon, getIcon, setIcon)
|