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,460 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from enum import Enum
|
3
|
+
|
4
|
+
from PySide6.QtCore import QEvent, QObject, QPoint, QTimer, Qt, QPropertyAnimation, QModelIndex, QRect
|
5
|
+
from PySide6.QtGui import QColor, QHelpEvent
|
6
|
+
from PySide6.QtWidgets import (QApplication, QFrame, QGraphicsDropShadowEffect,
|
7
|
+
QHBoxLayout, QLabel, QWidget, QAbstractItemView, QStyleOptionViewItem,
|
8
|
+
QTableView)
|
9
|
+
|
10
|
+
from ...common import FluentStyleSheet
|
11
|
+
from ...common.screen import getCurrentScreenGeometry
|
12
|
+
|
13
|
+
|
14
|
+
class ToolTipPosition(Enum):
|
15
|
+
""" Info bar position """
|
16
|
+
|
17
|
+
TOP = 0
|
18
|
+
BOTTOM = 1
|
19
|
+
LEFT = 2
|
20
|
+
RIGHT = 3
|
21
|
+
TOP_LEFT = 4
|
22
|
+
TOP_RIGHT = 5
|
23
|
+
BOTTOM_LEFT = 6
|
24
|
+
BOTTOM_RIGHT = 7
|
25
|
+
|
26
|
+
|
27
|
+
class ItemViewToolTipType(Enum):
|
28
|
+
""" Info bar position """
|
29
|
+
|
30
|
+
LIST = 0
|
31
|
+
TABLE = 1
|
32
|
+
|
33
|
+
|
34
|
+
class ToolTip(QFrame):
|
35
|
+
""" Tool tip """
|
36
|
+
|
37
|
+
def __init__(self, text='', parent=None):
|
38
|
+
"""
|
39
|
+
Parameters
|
40
|
+
----------
|
41
|
+
text: str
|
42
|
+
the text of tool tip
|
43
|
+
|
44
|
+
parent: QWidget
|
45
|
+
parent widget
|
46
|
+
"""
|
47
|
+
super().__init__(parent=parent)
|
48
|
+
self.__text = text
|
49
|
+
self.__duration = 1000
|
50
|
+
|
51
|
+
self.container = self._createContainer()
|
52
|
+
self.timer = QTimer(self)
|
53
|
+
|
54
|
+
self.setLayout(QHBoxLayout())
|
55
|
+
self.containerLayout = QHBoxLayout(self.container)
|
56
|
+
self.label = QLabel(text, self)
|
57
|
+
|
58
|
+
# set layout
|
59
|
+
self.layout().setContentsMargins(12, 8, 12, 12)
|
60
|
+
self.layout().addWidget(self.container)
|
61
|
+
self.containerLayout.addWidget(self.label)
|
62
|
+
self.containerLayout.setContentsMargins(8, 6, 8, 6)
|
63
|
+
|
64
|
+
# add opacity effect
|
65
|
+
self.opacityAni = QPropertyAnimation(self, b'windowOpacity', self)
|
66
|
+
self.opacityAni.setDuration(150)
|
67
|
+
|
68
|
+
# add shadow
|
69
|
+
self.shadowEffect = QGraphicsDropShadowEffect(self)
|
70
|
+
self.shadowEffect.setBlurRadius(25)
|
71
|
+
self.shadowEffect.setColor(QColor(0, 0, 0, 50))
|
72
|
+
self.shadowEffect.setOffset(0, 5)
|
73
|
+
self.container.setGraphicsEffect(self.shadowEffect)
|
74
|
+
|
75
|
+
self.timer.setSingleShot(True)
|
76
|
+
self.timer.timeout.connect(self.hide)
|
77
|
+
|
78
|
+
# set style
|
79
|
+
self.setAttribute(Qt.WA_TransparentForMouseEvents)
|
80
|
+
self.setAttribute(Qt.WA_TranslucentBackground)
|
81
|
+
self.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint)
|
82
|
+
self.__setQss()
|
83
|
+
|
84
|
+
def text(self):
|
85
|
+
return self.__text
|
86
|
+
|
87
|
+
def setText(self, text):
|
88
|
+
""" set text on tooltip """
|
89
|
+
self.__text = text
|
90
|
+
self.label.setText(text)
|
91
|
+
self.container.adjustSize()
|
92
|
+
self.adjustSize()
|
93
|
+
|
94
|
+
def duration(self):
|
95
|
+
return self.__duration
|
96
|
+
|
97
|
+
def setDuration(self, duration: int):
|
98
|
+
""" set tooltip duration in milliseconds
|
99
|
+
|
100
|
+
Parameters
|
101
|
+
----------
|
102
|
+
duration: int
|
103
|
+
display duration in milliseconds, if `duration <= 0`, tooltip won't disappear automatically
|
104
|
+
"""
|
105
|
+
self.__duration = duration
|
106
|
+
|
107
|
+
def __setQss(self):
|
108
|
+
""" set style sheet """
|
109
|
+
self.container.setObjectName("container")
|
110
|
+
self.label.setObjectName("contentLabel")
|
111
|
+
FluentStyleSheet.TOOL_TIP.apply(self)
|
112
|
+
self.label.adjustSize()
|
113
|
+
self.adjustSize()
|
114
|
+
|
115
|
+
def _createContainer(self):
|
116
|
+
return QFrame(self)
|
117
|
+
|
118
|
+
def showEvent(self, e):
|
119
|
+
self.opacityAni.setStartValue(0)
|
120
|
+
self.opacityAni.setEndValue(1)
|
121
|
+
self.opacityAni.start()
|
122
|
+
|
123
|
+
self.timer.stop()
|
124
|
+
if self.duration() > 0:
|
125
|
+
self.timer.start(self.__duration + self.opacityAni.duration())
|
126
|
+
|
127
|
+
super().showEvent(e)
|
128
|
+
|
129
|
+
def hideEvent(self, e):
|
130
|
+
self.timer.stop()
|
131
|
+
super().hideEvent(e)
|
132
|
+
|
133
|
+
def adjustPos(self, widget, position: ToolTipPosition):
|
134
|
+
""" adjust the position of tooltip relative to widget """
|
135
|
+
manager = ToolTipPositionManager.make(position)
|
136
|
+
self.move(manager.position(self, widget))
|
137
|
+
|
138
|
+
|
139
|
+
class ToolTipPositionManager:
|
140
|
+
""" Tooltip position manager """
|
141
|
+
|
142
|
+
def position(self, tooltip: ToolTip, parent: QWidget) -> QPoint:
|
143
|
+
pos = self._pos(tooltip, parent)
|
144
|
+
x, y = pos.x(), pos.y()
|
145
|
+
|
146
|
+
rect = getCurrentScreenGeometry()
|
147
|
+
x = max(rect.left(), min(pos.x(), rect.right() - tooltip.width() - 4))
|
148
|
+
y = max(rect.top(), min(pos.y(), rect.bottom() - tooltip.height() - 4))
|
149
|
+
|
150
|
+
return QPoint(x, y)
|
151
|
+
|
152
|
+
def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint:
|
153
|
+
raise NotImplementedError
|
154
|
+
|
155
|
+
@staticmethod
|
156
|
+
def make(position: ToolTipPosition):
|
157
|
+
""" mask info bar manager according to the display position """
|
158
|
+
managers = {
|
159
|
+
ToolTipPosition.TOP: TopToolTipManager,
|
160
|
+
ToolTipPosition.BOTTOM: BottomToolTipManager,
|
161
|
+
ToolTipPosition.LEFT: LeftToolTipManager,
|
162
|
+
ToolTipPosition.RIGHT: RightToolTipManager,
|
163
|
+
ToolTipPosition.TOP_RIGHT: TopRightToolTipManager,
|
164
|
+
ToolTipPosition.BOTTOM_RIGHT: BottomRightToolTipManager,
|
165
|
+
ToolTipPosition.TOP_LEFT: TopLeftToolTipManager,
|
166
|
+
ToolTipPosition.BOTTOM_LEFT: BottomLeftToolTipManager,
|
167
|
+
}
|
168
|
+
|
169
|
+
if position not in managers:
|
170
|
+
raise ValueError(f'`{position}` is an invalid info bar position.')
|
171
|
+
|
172
|
+
return managers[position]()
|
173
|
+
|
174
|
+
|
175
|
+
class TopToolTipManager(ToolTipPositionManager):
|
176
|
+
""" Top tooltip position manager """
|
177
|
+
|
178
|
+
def _pos(self, tooltip: ToolTip, parent: QWidget):
|
179
|
+
pos = parent.mapToGlobal(QPoint())
|
180
|
+
x = pos.x() + parent.width()//2 - tooltip.width()//2
|
181
|
+
y = pos.y() - tooltip.height()
|
182
|
+
return QPoint(x, y)
|
183
|
+
|
184
|
+
|
185
|
+
class BottomToolTipManager(ToolTipPositionManager):
|
186
|
+
""" Bottom tooltip position manager """
|
187
|
+
|
188
|
+
def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint:
|
189
|
+
pos = parent.mapToGlobal(QPoint())
|
190
|
+
x = pos.x() + parent.width()//2 - tooltip.width()//2
|
191
|
+
y = pos.y() + parent.height()
|
192
|
+
return QPoint(x, y)
|
193
|
+
|
194
|
+
|
195
|
+
class LeftToolTipManager(ToolTipPositionManager):
|
196
|
+
""" Left tooltip position manager """
|
197
|
+
|
198
|
+
def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint:
|
199
|
+
pos = parent.mapToGlobal(QPoint())
|
200
|
+
x = pos.x() - tooltip.width()
|
201
|
+
y = pos.y() + (parent.height() - tooltip.height()) // 2
|
202
|
+
return QPoint(x, y)
|
203
|
+
|
204
|
+
|
205
|
+
class RightToolTipManager(ToolTipPositionManager):
|
206
|
+
""" Right tooltip position manager """
|
207
|
+
|
208
|
+
def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint:
|
209
|
+
pos = parent.mapToGlobal(QPoint())
|
210
|
+
x = pos.x() + parent.width()
|
211
|
+
y = pos.y() + (parent.height() - tooltip.height()) // 2
|
212
|
+
return QPoint(x, y)
|
213
|
+
|
214
|
+
|
215
|
+
class TopRightToolTipManager(ToolTipPositionManager):
|
216
|
+
""" Top right tooltip position manager """
|
217
|
+
|
218
|
+
def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint:
|
219
|
+
pos = parent.mapToGlobal(QPoint())
|
220
|
+
x = pos.x() + parent.width() - tooltip.width() + \
|
221
|
+
tooltip.layout().contentsMargins().right()
|
222
|
+
y = pos.y() - tooltip.height()
|
223
|
+
return QPoint(x, y)
|
224
|
+
|
225
|
+
|
226
|
+
class TopLeftToolTipManager(ToolTipPositionManager):
|
227
|
+
""" Top left tooltip position manager """
|
228
|
+
|
229
|
+
def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint:
|
230
|
+
pos = parent.mapToGlobal(QPoint())
|
231
|
+
x = pos.x() - tooltip.layout().contentsMargins().left()
|
232
|
+
y = pos.y() - tooltip.height()
|
233
|
+
return QPoint(x, y)
|
234
|
+
|
235
|
+
|
236
|
+
class BottomRightToolTipManager(ToolTipPositionManager):
|
237
|
+
""" Bottom right tooltip position manager """
|
238
|
+
|
239
|
+
def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint:
|
240
|
+
pos = parent.mapToGlobal(QPoint())
|
241
|
+
x = pos.x() + parent.width() - tooltip.width() + \
|
242
|
+
tooltip.layout().contentsMargins().right()
|
243
|
+
y = pos.y() + parent.height()
|
244
|
+
return QPoint(x, y)
|
245
|
+
|
246
|
+
|
247
|
+
class BottomLeftToolTipManager(ToolTipPositionManager):
|
248
|
+
""" Bottom left tooltip position manager """
|
249
|
+
|
250
|
+
def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint:
|
251
|
+
pos = parent.mapToGlobal(QPoint())
|
252
|
+
x = pos.x() - tooltip.layout().contentsMargins().left()
|
253
|
+
y = pos.y() + parent.height()
|
254
|
+
return QPoint(x, y)
|
255
|
+
|
256
|
+
|
257
|
+
class ItemViewToolTipManager(ToolTipPositionManager):
|
258
|
+
""" Item view tooltip position manager """
|
259
|
+
|
260
|
+
def __init__(self, itemRect=QRect()):
|
261
|
+
super().__init__()
|
262
|
+
self.itemRect = itemRect
|
263
|
+
|
264
|
+
def _pos(self, tooltip: ToolTip, view: QAbstractItemView) -> QPoint:
|
265
|
+
pos = view.mapToGlobal(self.itemRect.topLeft())
|
266
|
+
x = pos.x()
|
267
|
+
y = pos.y() - tooltip.height() + 10
|
268
|
+
return QPoint(x, y)
|
269
|
+
|
270
|
+
@staticmethod
|
271
|
+
def make(tipType: ItemViewToolTipType, itemRect: QRect):
|
272
|
+
""" mask info bar manager according to the display tipType """
|
273
|
+
managers = {
|
274
|
+
ItemViewToolTipType.LIST: ItemViewToolTipManager,
|
275
|
+
ItemViewToolTipType.TABLE: TableItemToolTipManager,
|
276
|
+
}
|
277
|
+
|
278
|
+
if tipType not in managers:
|
279
|
+
raise ValueError(f'`{tipType}` is an invalid info bar tipType.')
|
280
|
+
|
281
|
+
return managers[tipType](itemRect)
|
282
|
+
|
283
|
+
|
284
|
+
class TableItemToolTipManager(ItemViewToolTipManager):
|
285
|
+
""" Table item view tooltip position manager """
|
286
|
+
|
287
|
+
def _pos(self, tooltip: ToolTip, view: QTableView) -> QPoint:
|
288
|
+
pos = view.mapToGlobal(self.itemRect.topLeft())
|
289
|
+
x = pos.x() + view.verticalHeader().isVisible() * view.verticalHeader().width()
|
290
|
+
y = pos.y() - tooltip.height() + view.horizontalHeader().isVisible() * view.horizontalHeader().height() + 10
|
291
|
+
return QPoint(x, y)
|
292
|
+
|
293
|
+
|
294
|
+
|
295
|
+
class ToolTipFilter(QObject):
|
296
|
+
""" Tool button with a tool tip """
|
297
|
+
|
298
|
+
def __init__(self, parent: QWidget, showDelay=300, position=ToolTipPosition.TOP):
|
299
|
+
"""
|
300
|
+
Parameters
|
301
|
+
----------
|
302
|
+
parent: QWidget
|
303
|
+
the widget to install tool tip
|
304
|
+
|
305
|
+
showDelay: int
|
306
|
+
show tool tip after how long the mouse hovers in milliseconds
|
307
|
+
|
308
|
+
position: TooltipPosition
|
309
|
+
where to show the tooltip
|
310
|
+
"""
|
311
|
+
super().__init__(parent=parent)
|
312
|
+
self.isEnter = False
|
313
|
+
self._tooltip = None
|
314
|
+
self._tooltipDelay = showDelay
|
315
|
+
self.position = position
|
316
|
+
self.timer = QTimer(self)
|
317
|
+
self.timer.setSingleShot(True)
|
318
|
+
self.timer.timeout.connect(self.showToolTip)
|
319
|
+
|
320
|
+
def eventFilter(self, obj: QObject, e: QEvent) -> bool:
|
321
|
+
if e.type() == QEvent.ToolTip:
|
322
|
+
return True
|
323
|
+
elif e.type() in [QEvent.Hide, QEvent.Leave]:
|
324
|
+
self.hideToolTip()
|
325
|
+
elif e.type() == QEvent.Enter:
|
326
|
+
self.isEnter = True
|
327
|
+
parent = self.parent() # type: QWidget
|
328
|
+
if self._canShowToolTip():
|
329
|
+
if self._tooltip is None:
|
330
|
+
self._tooltip = self._createToolTip()
|
331
|
+
|
332
|
+
t = parent.toolTipDuration() if parent.toolTipDuration() > 0 else -1
|
333
|
+
self._tooltip.setDuration(t)
|
334
|
+
|
335
|
+
# show the tool tip after delay
|
336
|
+
self.timer.start(self._tooltipDelay)
|
337
|
+
elif e.type() == QEvent.MouseButtonPress:
|
338
|
+
self.hideToolTip()
|
339
|
+
|
340
|
+
return super().eventFilter(obj, e)
|
341
|
+
|
342
|
+
def _createToolTip(self):
|
343
|
+
return ToolTip(self.parent().toolTip(), self.parent().window())
|
344
|
+
|
345
|
+
def hideToolTip(self):
|
346
|
+
""" hide tool tip """
|
347
|
+
self.isEnter = False
|
348
|
+
self.timer.stop()
|
349
|
+
if self._tooltip:
|
350
|
+
self._tooltip.hide()
|
351
|
+
|
352
|
+
def showToolTip(self):
|
353
|
+
""" show tool tip """
|
354
|
+
if not self.isEnter:
|
355
|
+
return
|
356
|
+
|
357
|
+
parent = self.parent() # type: QWidget
|
358
|
+
self._tooltip.setText(parent.toolTip())
|
359
|
+
self._tooltip.adjustPos(parent, self.position)
|
360
|
+
self._tooltip.show()
|
361
|
+
|
362
|
+
def setToolTipDelay(self, delay: int):
|
363
|
+
""" set the delay of tool tip """
|
364
|
+
self._tooltipDelay = delay
|
365
|
+
|
366
|
+
def _canShowToolTip(self) -> bool:
|
367
|
+
parent = self.parent() # type: QWidget
|
368
|
+
return parent.isWidgetType() and parent.toolTip() and parent.isEnabled()
|
369
|
+
|
370
|
+
|
371
|
+
class ItemViewToolTip(ToolTip):
|
372
|
+
""" Item view tool tip """
|
373
|
+
|
374
|
+
def adjustPos(self, view: QAbstractItemView, itemRect: QRect, tooltipType: ItemViewToolTipType):
|
375
|
+
manager = ItemViewToolTipManager.make(tooltipType, itemRect)
|
376
|
+
self.move(manager.position(self, view))
|
377
|
+
|
378
|
+
|
379
|
+
|
380
|
+
class ItemViewToolTipDelegate(ToolTipFilter):
|
381
|
+
""" Item view tool tip """
|
382
|
+
|
383
|
+
def __init__(self, parent: QAbstractItemView, showDelay=300, tooltipType=ItemViewToolTipType.TABLE):
|
384
|
+
super().__init__(parent, showDelay, ToolTipPosition.TOP)
|
385
|
+
self.text = ""
|
386
|
+
self.currentIndex = None
|
387
|
+
self.tooltipDuration = -1
|
388
|
+
self.tooltipType = tooltipType
|
389
|
+
self.viewport = parent.viewport()
|
390
|
+
|
391
|
+
parent.installEventFilter(self)
|
392
|
+
parent.viewport().installEventFilter(self)
|
393
|
+
parent.horizontalScrollBar().valueChanged.connect(self.hideToolTip)
|
394
|
+
parent.verticalScrollBar().valueChanged.connect(self.hideToolTip)
|
395
|
+
|
396
|
+
def eventFilter(self, obj: QObject, e: QEvent) -> bool:
|
397
|
+
if obj is self.parent():
|
398
|
+
if e.type() in [QEvent.Type.Hide, QEvent.Type.Leave]:
|
399
|
+
self.hideToolTip()
|
400
|
+
elif e.type() == QEvent.Type.Enter:
|
401
|
+
self.isEnter = True
|
402
|
+
elif obj is self.viewport:
|
403
|
+
if e.type() == QEvent.Type.MouseButtonPress:
|
404
|
+
self.hideToolTip()
|
405
|
+
|
406
|
+
return QObject.eventFilter(self, obj, e)
|
407
|
+
|
408
|
+
def _createToolTip(self):
|
409
|
+
return ItemViewToolTip(self.text, self.parent().window())
|
410
|
+
|
411
|
+
def showToolTip(self):
|
412
|
+
""" show tool tip """
|
413
|
+
if not self._tooltip:
|
414
|
+
self._tooltip = self._createToolTip()
|
415
|
+
|
416
|
+
view = self.parent() # type: QAbstractItemView
|
417
|
+
self._tooltip.setText(self.text)
|
418
|
+
|
419
|
+
if self.currentIndex:
|
420
|
+
rect = view.visualRect(self.currentIndex)
|
421
|
+
else:
|
422
|
+
rect = QRect()
|
423
|
+
|
424
|
+
self._tooltip.adjustPos(view, rect, self.tooltipType)
|
425
|
+
self._tooltip.show()
|
426
|
+
|
427
|
+
def _canShowToolTip(self) -> bool:
|
428
|
+
return True
|
429
|
+
|
430
|
+
def setText(self, text: str):
|
431
|
+
self.text = text
|
432
|
+
if self._tooltip:
|
433
|
+
self._tooltip.setText(text)
|
434
|
+
|
435
|
+
def setToolTipDuration(self, duration):
|
436
|
+
self.tooltipDuration = duration
|
437
|
+
if self._tooltip:
|
438
|
+
self._tooltip.setDuration(duration)
|
439
|
+
|
440
|
+
def helpEvent(self, event: QHelpEvent, view: QAbstractItemView, option: QStyleOptionViewItem, index: QModelIndex) -> bool:
|
441
|
+
if not event or not view:
|
442
|
+
return False
|
443
|
+
|
444
|
+
if event.type() == QEvent.Type.ToolTip:
|
445
|
+
text = index.data(Qt.ItemDataRole.ToolTipRole)
|
446
|
+
if not text:
|
447
|
+
self.hideToolTip()
|
448
|
+
return False
|
449
|
+
|
450
|
+
self.text = text
|
451
|
+
self.currentIndex = index
|
452
|
+
|
453
|
+
if not self._tooltip:
|
454
|
+
self._tooltip = self._createToolTip()
|
455
|
+
self._tooltip.setDuration(self.tooltipDuration)
|
456
|
+
|
457
|
+
# show the tool tip after delay
|
458
|
+
self.timer.start(self._tooltipDelay)
|
459
|
+
|
460
|
+
return True
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
from PySide6.QtCore import Qt, QSize, QRectF, QModelIndex, QEvent
|
3
|
+
from PySide6.QtGui import QPainter, QColor, QPalette
|
4
|
+
from PySide6.QtWidgets import QTreeWidget, QStyledItemDelegate, QStyle, QTreeView, QApplication, QStyleOptionViewItem
|
5
|
+
|
6
|
+
from ...common.style_sheet import FluentStyleSheet, themeColor, isDarkTheme, setCustomStyleSheet
|
7
|
+
from ...common.font import getFont
|
8
|
+
from ...common.color import autoFallbackThemeColor
|
9
|
+
from .check_box import CheckBoxIcon
|
10
|
+
from .scroll_area import SmoothScrollDelegate
|
11
|
+
|
12
|
+
|
13
|
+
class TreeItemDelegate(QStyledItemDelegate):
|
14
|
+
""" Tree item delegate """
|
15
|
+
|
16
|
+
def __init__(self, parent: QTreeView):
|
17
|
+
super().__init__(parent)
|
18
|
+
self.lightCheckedColor = QColor()
|
19
|
+
self.darkCheckedColor = QColor()
|
20
|
+
|
21
|
+
def setCheckedColor(self, light, dark):
|
22
|
+
""" set the color of indicator in checked status
|
23
|
+
|
24
|
+
Parameters
|
25
|
+
----------
|
26
|
+
light, dark: str | QColor | Qt.GlobalColor
|
27
|
+
color in light/dark theme mode
|
28
|
+
"""
|
29
|
+
self.lightCheckedColor = QColor(light)
|
30
|
+
self.darkCheckedColor = QColor(dark)
|
31
|
+
self.parent().viewport().update()
|
32
|
+
|
33
|
+
def paint(self, painter, option, index):
|
34
|
+
painter.setRenderHints(
|
35
|
+
QPainter.Antialiasing | QPainter.TextAntialiasing)
|
36
|
+
super().paint(painter, option, index)
|
37
|
+
|
38
|
+
if index.data(Qt.CheckStateRole) is not None:
|
39
|
+
self._drawCheckBox(painter, option, index)
|
40
|
+
|
41
|
+
if not (option.state & (QStyle.State_Selected | QStyle.State_MouseOver)):
|
42
|
+
return
|
43
|
+
|
44
|
+
painter.save()
|
45
|
+
painter.setPen(Qt.NoPen)
|
46
|
+
|
47
|
+
# draw background
|
48
|
+
h = option.rect.height() - 4
|
49
|
+
c = 255 if isDarkTheme() else 0
|
50
|
+
painter.setBrush(QColor(c, c, c, 9))
|
51
|
+
painter.drawRoundedRect(
|
52
|
+
4, option.rect.y() + 2, self.parent().width() - 8, h, 4, 4)
|
53
|
+
|
54
|
+
# draw indicator
|
55
|
+
if option.state & QStyle.State_Selected and self.parent().horizontalScrollBar().value() == 0:
|
56
|
+
painter.setBrush(autoFallbackThemeColor(self.lightCheckedColor, self.darkCheckedColor))
|
57
|
+
painter.drawRoundedRect(4, 9+option.rect.y(), 3, h - 13, 1.5, 1.5)
|
58
|
+
|
59
|
+
painter.restore()
|
60
|
+
|
61
|
+
def _drawCheckBox(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
|
62
|
+
painter.save()
|
63
|
+
checkState = Qt.CheckState(index.data(Qt.ItemDataRole.CheckStateRole))
|
64
|
+
|
65
|
+
isDark = isDarkTheme()
|
66
|
+
|
67
|
+
r = 4.5
|
68
|
+
x = option.rect.x() + 23
|
69
|
+
y = option.rect.center().y() - 9
|
70
|
+
rect = QRectF(x, y, 19, 19)
|
71
|
+
|
72
|
+
if checkState == Qt.CheckState.Unchecked:
|
73
|
+
painter.setBrush(QColor(0, 0, 0, 26)
|
74
|
+
if isDark else QColor(0, 0, 0, 6))
|
75
|
+
painter.setPen(QColor(255, 255, 255, 142)
|
76
|
+
if isDark else QColor(0, 0, 0, 122))
|
77
|
+
painter.drawRoundedRect(rect, r, r)
|
78
|
+
else:
|
79
|
+
color = autoFallbackThemeColor(self.lightCheckedColor, self.darkCheckedColor)
|
80
|
+
painter.setPen(color)
|
81
|
+
painter.setBrush(color)
|
82
|
+
painter.drawRoundedRect(rect, r, r)
|
83
|
+
|
84
|
+
if checkState == Qt.CheckState.Checked:
|
85
|
+
CheckBoxIcon.ACCEPT.render(painter, rect)
|
86
|
+
else:
|
87
|
+
CheckBoxIcon.PARTIAL_ACCEPT.render(painter, rect)
|
88
|
+
|
89
|
+
painter.restore()
|
90
|
+
|
91
|
+
|
92
|
+
def initStyleOption(self, option, index):
|
93
|
+
super().initStyleOption(option, index)
|
94
|
+
|
95
|
+
# font
|
96
|
+
option.font = index.data(Qt.FontRole) or getFont(13)
|
97
|
+
|
98
|
+
# text color
|
99
|
+
textColor = Qt.white if isDarkTheme() else Qt.black
|
100
|
+
textBrush = index.data(Qt.ForegroundRole)
|
101
|
+
if textBrush is not None:
|
102
|
+
textColor = textBrush.color()
|
103
|
+
|
104
|
+
option.palette.setColor(QPalette.Text, textColor)
|
105
|
+
option.palette.setColor(QPalette.HighlightedText, textColor)
|
106
|
+
|
107
|
+
|
108
|
+
class TreeViewBase:
|
109
|
+
""" Tree view base class """
|
110
|
+
|
111
|
+
def _initView(self):
|
112
|
+
self.scrollDelagate = SmoothScrollDelegate(self)
|
113
|
+
|
114
|
+
self.header().setHighlightSections(False)
|
115
|
+
self.header().setDefaultAlignment(Qt.AlignCenter)
|
116
|
+
|
117
|
+
self.setItemDelegate(TreeItemDelegate(self))
|
118
|
+
self.setIconSize(QSize(16, 16))
|
119
|
+
self.setMouseTracking(True)
|
120
|
+
|
121
|
+
FluentStyleSheet.TREE_VIEW.apply(self)
|
122
|
+
|
123
|
+
def setCheckedColor(self, light, dark):
|
124
|
+
""" set the color in checked status
|
125
|
+
|
126
|
+
Parameters
|
127
|
+
----------
|
128
|
+
light, dark: str | QColor | Qt.GlobalColor
|
129
|
+
color in light/dark theme mode
|
130
|
+
"""
|
131
|
+
self.itemDelegate().setCheckedColor(light, dark)
|
132
|
+
|
133
|
+
def drawBranches(self, painter, rect, index):
|
134
|
+
rect.moveLeft(15)
|
135
|
+
return QTreeView.drawBranches(self, painter, rect, index)
|
136
|
+
|
137
|
+
def setBorderVisible(self, isVisible: bool):
|
138
|
+
""" set the visibility of border """
|
139
|
+
self.setProperty("isBorderVisible", isVisible)
|
140
|
+
self.setStyle(QApplication.style())
|
141
|
+
|
142
|
+
def setBorderRadius(self, radius: int):
|
143
|
+
""" set the radius of border """
|
144
|
+
qss = f"QTreeView{{border-radius: {radius}px}}"
|
145
|
+
setCustomStyleSheet(self, qss, qss)
|
146
|
+
|
147
|
+
|
148
|
+
class TreeWidget(TreeViewBase, QTreeWidget):
|
149
|
+
""" Tree widget """
|
150
|
+
|
151
|
+
def __init__(self, parent=None):
|
152
|
+
super().__init__(parent=parent)
|
153
|
+
self._initView()
|
154
|
+
|
155
|
+
def viewportEvent(self, event):
|
156
|
+
"""
|
157
|
+
Catch the click event to override the item "expand/collapse" function which is
|
158
|
+
still called in the place it was before moving the branches in the drawBranches method.
|
159
|
+
"""
|
160
|
+
if event.type() != QEvent.Type.MouseButtonPress:
|
161
|
+
return super().viewportEvent(event)
|
162
|
+
|
163
|
+
index = self.indexAt(event.pos())
|
164
|
+
item = self.itemFromIndex(index)
|
165
|
+
|
166
|
+
if item is None:
|
167
|
+
return super().viewportEvent(event)
|
168
|
+
|
169
|
+
level = 0
|
170
|
+
while item.parent() is not None:
|
171
|
+
item = item.parent()
|
172
|
+
level += 1
|
173
|
+
|
174
|
+
indent = level * self.indentation() + 20
|
175
|
+
if event.pos().x() > indent and event.pos().x() < indent + 10:
|
176
|
+
if self.isExpanded(index):
|
177
|
+
self.collapse(index)
|
178
|
+
else:
|
179
|
+
self.expand(index)
|
180
|
+
|
181
|
+
return super().viewportEvent(event)
|
182
|
+
|
183
|
+
|
184
|
+
class TreeView(TreeViewBase, QTreeView):
|
185
|
+
""" Tree view """
|
186
|
+
|
187
|
+
def __init__(self, parent=None):
|
188
|
+
super().__init__(parent=parent)
|
189
|
+
self._initView()
|
190
|
+
|
191
|
+
def viewportEvent(self, event):
|
192
|
+
"""
|
193
|
+
Catch the click event to override the item "expand/collapse" function which is
|
194
|
+
still called in the place it was before moving the branches in the drawBranches method.
|
195
|
+
"""
|
196
|
+
if event.type() != QEvent.Type.MouseButtonPress:
|
197
|
+
return super().viewportEvent(event)
|
198
|
+
|
199
|
+
index = self.indexAt(event.pos())
|
200
|
+
if not index.isValid():
|
201
|
+
return super().viewportEvent(event)
|
202
|
+
|
203
|
+
level = 0
|
204
|
+
currentIndex = index
|
205
|
+
while currentIndex.parent().isValid():
|
206
|
+
currentIndex = currentIndex.parent()
|
207
|
+
level += 1
|
208
|
+
|
209
|
+
indent = level * self.indentation() + 20
|
210
|
+
if event.pos().x() > indent and event.pos().x() < indent + 10:
|
211
|
+
if self.isExpanded(index):
|
212
|
+
self.collapse(index)
|
213
|
+
else:
|
214
|
+
self.expand(index)
|
215
|
+
|
216
|
+
return super().viewportEvent(event)
|