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,167 @@
1
+ # coding:utf-8
2
+ from PySide6.QtCore import Qt, Signal, QObject, QEvent
3
+ from PySide6.QtGui import QColor
4
+ from PySide6.QtWidgets import QLabel, QFrame, QVBoxLayout, QHBoxLayout, QPushButton
5
+ from fframelesswindow import FramelessDialog
6
+
7
+ from ...common.auto_wrap import TextWrap
8
+ from ...common.style_sheet import FluentStyleSheet
9
+ from ..widgets.button import PrimaryPushButton
10
+ from ..widgets.label import BodyLabel
11
+
12
+ from .mask_dialog_base import MaskDialogBase
13
+
14
+
15
+ class Ui_MessageBox:
16
+ """ Ui of message box """
17
+
18
+ yesSignal = Signal()
19
+ cancelSignal = Signal()
20
+
21
+ def __init__(self, *args, **kwargs):
22
+ pass
23
+
24
+ def _setUpUi(self, title, content, parent):
25
+ self.content = content
26
+ self.titleLabel = QLabel(title, parent)
27
+ self.contentLabel = BodyLabel(content, parent)
28
+
29
+ self.buttonGroup = QFrame(parent)
30
+ self.yesButton = PrimaryPushButton(self.tr('OK'), self.buttonGroup)
31
+ self.cancelButton = QPushButton(self.tr('Cancel'), self.buttonGroup)
32
+
33
+ self.vBoxLayout = QVBoxLayout(parent)
34
+ self.textLayout = QVBoxLayout()
35
+ self.buttonLayout = QHBoxLayout(self.buttonGroup)
36
+
37
+ self.__initWidget()
38
+
39
+ def __initWidget(self):
40
+ self.__setQss()
41
+ self.__initLayout()
42
+
43
+ # fixes https://github.com/NumBNN/Python-FastUI-Widgets/issues/19
44
+ self.yesButton.setAttribute(Qt.WA_LayoutUsesWidgetRect)
45
+ self.cancelButton.setAttribute(Qt.WA_LayoutUsesWidgetRect)
46
+
47
+ self.yesButton.setFocus()
48
+ self.buttonGroup.setFixedHeight(81)
49
+
50
+ self.contentLabel.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
51
+ self._adjustText()
52
+
53
+ self.yesButton.clicked.connect(self.__onYesButtonClicked)
54
+ self.cancelButton.clicked.connect(self.__onCancelButtonClicked)
55
+
56
+ def _adjustText(self):
57
+ if self.isWindow():
58
+ if self.parent():
59
+ w = max(self.titleLabel.width(), self.parent().width())
60
+ chars = max(min(w / 9, 140), 30)
61
+ else:
62
+ chars = 100
63
+ else:
64
+ w = max(self.titleLabel.width(), self.window().width())
65
+ chars = max(min(w / 9, 100), 30)
66
+
67
+ self.contentLabel.setText(TextWrap.wrap(self.content, chars, False)[0])
68
+
69
+ def __initLayout(self):
70
+ self.vBoxLayout.setSpacing(0)
71
+ self.vBoxLayout.setContentsMargins(0, 0, 0, 0)
72
+ self.vBoxLayout.addLayout(self.textLayout, 1)
73
+ self.vBoxLayout.addWidget(self.buttonGroup, 0, Qt.AlignBottom)
74
+ self.vBoxLayout.setSizeConstraint(QVBoxLayout.SetMinimumSize)
75
+
76
+ self.textLayout.setSpacing(12)
77
+ self.textLayout.setContentsMargins(24, 24, 24, 24)
78
+ self.textLayout.addWidget(self.titleLabel, 0, Qt.AlignTop)
79
+ self.textLayout.addWidget(self.contentLabel, 0, Qt.AlignTop)
80
+
81
+ self.buttonLayout.setSpacing(12)
82
+ self.buttonLayout.setContentsMargins(24, 24, 24, 24)
83
+ self.buttonLayout.addWidget(self.yesButton, 1, Qt.AlignVCenter)
84
+ self.buttonLayout.addWidget(self.cancelButton, 1, Qt.AlignVCenter)
85
+
86
+ def __onCancelButtonClicked(self):
87
+ self.reject()
88
+ self.cancelSignal.emit()
89
+
90
+ def __onYesButtonClicked(self):
91
+ self.accept()
92
+ self.yesSignal.emit()
93
+
94
+ def __setQss(self):
95
+ self.titleLabel.setObjectName("titleLabel")
96
+ self.contentLabel.setObjectName("contentLabel")
97
+ self.buttonGroup.setObjectName('buttonGroup')
98
+ self.cancelButton.setObjectName('cancelButton')
99
+
100
+ FluentStyleSheet.DIALOG.apply(self)
101
+ FluentStyleSheet.DIALOG.apply(self.contentLabel)
102
+
103
+ self.yesButton.adjustSize()
104
+ self.cancelButton.adjustSize()
105
+
106
+ def setContentCopyable(self, isCopyable: bool):
107
+ """ set whether the content is copyable """
108
+ if isCopyable:
109
+ self.contentLabel.setTextInteractionFlags(
110
+ Qt.TextInteractionFlag.TextSelectableByMouse)
111
+ else:
112
+ self.contentLabel.setTextInteractionFlags(
113
+ Qt.TextInteractionFlag.NoTextInteraction)
114
+
115
+
116
+ class Dialog(FramelessDialog, Ui_MessageBox):
117
+ """ Dialog box """
118
+
119
+ yesSignal = Signal()
120
+ cancelSignal = Signal()
121
+
122
+ def __init__(self, title: str, content: str, parent=None):
123
+ super().__init__(parent=parent)
124
+ self._setUpUi(title, content, self)
125
+
126
+ self.windowTitleLabel = QLabel(title, self)
127
+
128
+ self.setResizeEnabled(False)
129
+ self.resize(240, 192)
130
+ self.titleBar.hide()
131
+
132
+ self.vBoxLayout.insertWidget(0, self.windowTitleLabel, 0, Qt.AlignTop)
133
+ self.windowTitleLabel.setObjectName('windowTitleLabel')
134
+ FluentStyleSheet.DIALOG.apply(self)
135
+ self.setFixedSize(self.size())
136
+
137
+ def setTitleBarVisible(self, isVisible: bool):
138
+ self.windowTitleLabel.setVisible(isVisible)
139
+
140
+
141
+ class MessageBox(MaskDialogBase, Ui_MessageBox):
142
+ """ Message box """
143
+
144
+ yesSignal = Signal()
145
+ cancelSignal = Signal()
146
+
147
+ def __init__(self, title: str, content: str, parent=None):
148
+ super().__init__(parent=parent)
149
+ self._setUpUi(title, content, self.widget)
150
+
151
+ self.setShadowEffect(60, (0, 10), QColor(0, 0, 0, 50))
152
+ self.setMaskColor(QColor(0, 0, 0, 76))
153
+ self._hBoxLayout.removeWidget(self.widget)
154
+ self._hBoxLayout.addWidget(self.widget, 1, Qt.AlignCenter)
155
+
156
+ self.buttonGroup.setMinimumWidth(280)
157
+ self.widget.setFixedSize(
158
+ max(self.contentLabel.width(), self.titleLabel.width()) + 48,
159
+ self.contentLabel.y() + self.contentLabel.height() + 105
160
+ )
161
+
162
+ def eventFilter(self, obj, e: QEvent):
163
+ if obj is self.window():
164
+ if e.type() == QEvent.Resize:
165
+ self._adjustText()
166
+
167
+ return super().eventFilter(obj, e)
@@ -0,0 +1,307 @@
1
+ # coding:utf-8
2
+ import os
3
+
4
+ from PySide6.QtCore import Qt, Signal
5
+ from PySide6.QtGui import (QBrush, QColor, QFont, QFontMetrics, QMouseEvent,
6
+ QPainter, QPen, QPixmap)
7
+ from PySide6.QtWidgets import (QApplication, QFileDialog, QHBoxLayout, QLabel,
8
+ QVBoxLayout, QWidget, QPushButton)
9
+
10
+ from ...common.config import isDarkTheme
11
+ from ...common.icon import getIconColor
12
+ from ...common.style_sheet import FluentStyleSheet
13
+ from .dialog import Dialog
14
+ from .mask_dialog_base import MaskDialogBase
15
+ from ..widgets.scroll_area import SingleDirectionScrollArea
16
+
17
+
18
+ class FolderListDialog(MaskDialogBase):
19
+ """ Folder list dialog box """
20
+
21
+ folderChanged = Signal(list)
22
+
23
+ def __init__(self, folderPaths: list, title: str, content: str, parent):
24
+ super().__init__(parent=parent)
25
+ self.title = title
26
+ self.content = content
27
+ self.__originalPaths = folderPaths
28
+ self.folderPaths = folderPaths.copy()
29
+
30
+ self.vBoxLayout = QVBoxLayout(self.widget)
31
+ self.titleLabel = QLabel(title, self.widget)
32
+ self.contentLabel = QLabel(content, self.widget)
33
+ self.scrollArea = SingleDirectionScrollArea(self.widget)
34
+ self.scrollWidget = QWidget(self.scrollArea)
35
+ self.completeButton = QPushButton(self.tr('Done'), self.widget)
36
+ self.addFolderCard = AddFolderCard(self.scrollWidget)
37
+ self.folderCards = [FolderCard(i, self.scrollWidget)
38
+ for i in folderPaths]
39
+ self.__initWidget()
40
+
41
+ def __initWidget(self):
42
+ """ initialize widgets """
43
+ self.__setQss()
44
+
45
+ w = max(self.titleLabel.width()+48, self.contentLabel.width()+48, 352)
46
+ self.widget.setFixedWidth(w)
47
+ self.scrollArea.resize(294, 72)
48
+ self.scrollWidget.resize(292, 72)
49
+ self.scrollArea.setFixedWidth(294)
50
+ self.scrollWidget.setFixedWidth(292)
51
+ self.scrollArea.setMaximumHeight(400)
52
+ self.scrollArea.setViewportMargins(0, 0, 0, 0)
53
+ self.scrollArea.setWidgetResizable(True)
54
+ self.scrollArea.setWidget(self.scrollWidget)
55
+ self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
56
+ self.scrollArea.hScrollBar.setForceHidden(True)
57
+ self.__initLayout()
58
+
59
+ # connect signal to slot
60
+ self.addFolderCard.clicked.connect(self.__showFileDialog)
61
+ self.completeButton.clicked.connect(self.__onButtonClicked)
62
+ for card in self.folderCards:
63
+ card.clicked.connect(self.__showDeleteFolderCardDialog)
64
+
65
+ def __initLayout(self):
66
+ """ initialize layout """
67
+ self.vBoxLayout.setContentsMargins(24, 24, 24, 24)
68
+ self.vBoxLayout.setSizeConstraint(QVBoxLayout.SetFixedSize)
69
+ self.vBoxLayout.setAlignment(Qt.AlignTop)
70
+ self.vBoxLayout.setSpacing(0)
71
+
72
+ # labels
73
+ layout_1 = QVBoxLayout()
74
+ layout_1.setContentsMargins(0, 0, 0, 0)
75
+ layout_1.setSpacing(6)
76
+ layout_1.addWidget(self.titleLabel, 0, Qt.AlignTop)
77
+ layout_1.addWidget(self.contentLabel, 0, Qt.AlignTop)
78
+ self.vBoxLayout.addLayout(layout_1, 0)
79
+ self.vBoxLayout.addSpacing(12)
80
+
81
+ # cards
82
+ layout_2 = QHBoxLayout()
83
+ layout_2.setAlignment(Qt.AlignCenter)
84
+ layout_2.setContentsMargins(4, 0, 4, 0)
85
+ layout_2.addWidget(self.scrollArea, 0, Qt.AlignCenter)
86
+ self.vBoxLayout.addLayout(layout_2, 1)
87
+ self.vBoxLayout.addSpacing(24)
88
+
89
+ self.scrollLayout = QVBoxLayout(self.scrollWidget)
90
+ self.scrollLayout.setAlignment(Qt.AlignTop)
91
+ self.scrollLayout.setContentsMargins(0, 0, 0, 0)
92
+ self.scrollLayout.setSpacing(8)
93
+ self.scrollLayout.addWidget(self.addFolderCard, 0, Qt.AlignTop)
94
+ for card in self.folderCards:
95
+ self.scrollLayout.addWidget(card, 0, Qt.AlignTop)
96
+
97
+ # buttons
98
+ layout_3 = QHBoxLayout()
99
+ layout_3.setContentsMargins(0, 0, 0, 0)
100
+ layout_3.addStretch(1)
101
+ layout_3.addWidget(self.completeButton)
102
+ self.vBoxLayout.addLayout(layout_3, 0)
103
+
104
+ self.__adjustWidgetSize()
105
+
106
+ def __showFileDialog(self):
107
+ """ show file dialog to select folder """
108
+ path = QFileDialog.getExistingDirectory(
109
+ self, self.tr("Choose folder"), "./")
110
+
111
+ if not path or path in self.folderPaths:
112
+ return
113
+
114
+ # create folder card
115
+ card = FolderCard(path, self.scrollWidget)
116
+ self.scrollLayout.addWidget(card, 0, Qt.AlignTop)
117
+ card.clicked.connect(self.__showDeleteFolderCardDialog)
118
+ card.show()
119
+
120
+ self.folderPaths.append(path)
121
+ self.folderCards.append(card)
122
+
123
+ self.__adjustWidgetSize()
124
+
125
+ def __showDeleteFolderCardDialog(self):
126
+ """ show delete folder card dialog """
127
+ sender = self.sender()
128
+ title = self.tr('Are you sure you want to delete the folder?')
129
+ content = self.tr("If you delete the ") + f'"{sender.folderName}"' + \
130
+ self.tr(" folder and remove it from the list, the folder will no "
131
+ "longer appear in the list, but will not be deleted.")
132
+ dialog = Dialog(title, content, self.window())
133
+ dialog.yesSignal.connect(lambda: self.__deleteFolderCard(sender))
134
+ dialog.exec_()
135
+
136
+ def __deleteFolderCard(self, folderCard):
137
+ """ delete selected folder card """
138
+ self.scrollLayout.removeWidget(folderCard)
139
+ index = self.folderCards.index(folderCard)
140
+ self.folderCards.pop(index)
141
+ self.folderPaths.pop(index)
142
+ folderCard.deleteLater()
143
+
144
+ # adjust height
145
+ self.__adjustWidgetSize()
146
+
147
+ def __setQss(self):
148
+ """ set style sheet """
149
+ self.titleLabel.setObjectName('titleLabel')
150
+ self.contentLabel.setObjectName('contentLabel')
151
+ self.completeButton.setObjectName('completeButton')
152
+ self.scrollWidget.setObjectName('scrollWidget')
153
+
154
+ FluentStyleSheet.FOLDER_LIST_DIALOG.apply(self)
155
+ self.setStyle(QApplication.style())
156
+
157
+ self.titleLabel.adjustSize()
158
+ self.contentLabel.adjustSize()
159
+ self.completeButton.adjustSize()
160
+
161
+ def __onButtonClicked(self):
162
+ """ done button clicked slot """
163
+ if sorted(self.__originalPaths) != sorted(self.folderPaths):
164
+ self.setEnabled(False)
165
+ QApplication.processEvents()
166
+ self.folderChanged.emit(self.folderPaths)
167
+
168
+ self.close()
169
+
170
+ def __adjustWidgetSize(self):
171
+ N = len(self.folderCards)
172
+ h = 72*(N+1) + 8*N
173
+ self.scrollArea.setFixedHeight(min(h, 400))
174
+
175
+
176
+ class ClickableWindow(QWidget):
177
+ """ Clickable window """
178
+
179
+ clicked = Signal()
180
+
181
+ def __init__(self, parent=None):
182
+ super().__init__(parent)
183
+ self.setAttribute(Qt.WA_TranslucentBackground)
184
+ self.setWindowFlags(Qt.FramelessWindowHint)
185
+ self.setFixedSize(292, 72)
186
+ self._isPressed = None
187
+ self._isEnter = False
188
+
189
+ def enterEvent(self, e):
190
+ self._isEnter = True
191
+ self.update()
192
+
193
+ def leaveEvent(self, e):
194
+ self._isEnter = False
195
+ self.update()
196
+
197
+ def mouseReleaseEvent(self, e):
198
+ self._isPressed = False
199
+ self.update()
200
+ if e.button() == Qt.LeftButton:
201
+ self.clicked.emit()
202
+
203
+ def mousePressEvent(self, e: QMouseEvent):
204
+ self._isPressed = True
205
+ self.update()
206
+
207
+ def paintEvent(self, e):
208
+ """ paint window """
209
+ painter = QPainter(self)
210
+ painter.setRenderHints(QPainter.Antialiasing)
211
+
212
+ isDark = isDarkTheme()
213
+ bg = 51 if isDark else 204
214
+ brush = QBrush(QColor(bg, bg, bg))
215
+ painter.setPen(Qt.NoPen)
216
+
217
+ if not self._isEnter:
218
+ painter.setBrush(brush)
219
+ painter.drawRoundedRect(self.rect(), 4, 4)
220
+ else:
221
+ painter.setPen(QPen(QColor(bg, bg, bg), 2))
222
+ painter.drawRect(1, 1, self.width() - 2, self.height() - 2)
223
+ painter.setPen(Qt.NoPen)
224
+ if not self._isPressed:
225
+ bg = 24 if isDark else 230
226
+ brush.setColor(QColor(bg, bg, bg))
227
+ painter.setBrush(brush)
228
+ painter.drawRect(2, 2, self.width() - 4, self.height() - 4)
229
+ else:
230
+ bg = 102 if isDark else 230
231
+ brush.setColor(QColor(153, 153, 153))
232
+ painter.setBrush(brush)
233
+ painter.drawRoundedRect(
234
+ 5, 1, self.width() - 10, self.height() - 2, 2, 2)
235
+
236
+
237
+ class FolderCard(ClickableWindow):
238
+ """ Folder card """
239
+
240
+ def __init__(self, folderPath: str, parent=None):
241
+ super().__init__(parent)
242
+ self.folderPath = folderPath
243
+ self.folderName = os.path.basename(folderPath)
244
+ c = getIconColor()
245
+ self.__closeIcon = QPixmap(f":/fastuiwidgets/images/folder_list_dialog/Close_{c}.png").scaled(
246
+ 12, 12, Qt.KeepAspectRatio, Qt.SmoothTransformation)
247
+
248
+ def paintEvent(self, e):
249
+ """ paint card """
250
+ super().paintEvent(e)
251
+ painter = QPainter(self)
252
+ painter.setRenderHints(
253
+ QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform | QPainter.Antialiasing)
254
+
255
+ # paint text and icon
256
+ color = Qt.white if isDarkTheme() else Qt.black
257
+ painter.setPen(color)
258
+ if self._isPressed:
259
+ self.__drawText(painter, 12, 12, 12, 10)
260
+ painter.drawPixmap(self.width() - 26, 18, self.__closeIcon)
261
+ else:
262
+ self.__drawText(painter, 10, 13, 10, 11)
263
+ painter.drawPixmap(self.width() - 24, 20, self.__closeIcon)
264
+
265
+ def __drawText(self, painter, x1, fontSize1, x2, fontSize2):
266
+ """ draw text """
267
+ # paint folder name
268
+ font = QFont("Microsoft YaHei")
269
+ font.setBold(True)
270
+ font.setPixelSize(fontSize1)
271
+ painter.setFont(font)
272
+ name = QFontMetrics(font).elidedText(
273
+ self.folderName, Qt.ElideRight, self.width()-48)
274
+ painter.drawText(x1, 30, name)
275
+
276
+ # paint folder path
277
+ font = QFont("Microsoft YaHei")
278
+ font.setPixelSize(fontSize2)
279
+ painter.setFont(font)
280
+ path = QFontMetrics(font).elidedText(
281
+ self.folderPath, Qt.ElideRight, self.width()-24)
282
+ painter.drawText(x2, 37, self.width() - 16, 18, Qt.AlignLeft, path)
283
+
284
+
285
+ class AddFolderCard(ClickableWindow):
286
+ """ Add folder card """
287
+
288
+ def __init__(self, parent=None):
289
+ super().__init__(parent)
290
+ c = getIconColor()
291
+ self.__iconPix = QPixmap(f":/fastuiwidgets/images/folder_list_dialog/Add_{c}.png").scaled(
292
+ 22, 22, Qt.KeepAspectRatio, Qt.SmoothTransformation)
293
+
294
+ def paintEvent(self, e):
295
+ """ paint card """
296
+ super().paintEvent(e)
297
+ painter = QPainter(self)
298
+ w = self.width()
299
+ h = self.height()
300
+ pw = self.__iconPix.width()
301
+ ph = self.__iconPix.height()
302
+ if not self._isPressed:
303
+ painter.drawPixmap(
304
+ int(w/2 - pw/2), int(h/2 - ph/2), self.__iconPix)
305
+ else:
306
+ painter.drawPixmap(
307
+ int(w/2 - (pw - 4)/2), int(h/2 - (ph - 4)/2), pw - 4, ph - 4, self.__iconPix)
@@ -0,0 +1,120 @@
1
+ # coding:utf-8
2
+ from PySide6.QtCore import QEasingCurve, QPropertyAnimation, Qt, QEvent, QPoint
3
+ from PySide6.QtGui import QColor, QResizeEvent
4
+ from PySide6.QtWidgets import (QDialog, QGraphicsDropShadowEffect,
5
+ QGraphicsOpacityEffect, QHBoxLayout, QWidget, QFrame)
6
+
7
+ from ...common.config import isDarkTheme
8
+
9
+
10
+ class MaskDialogBase(QDialog):
11
+ """ Dialog box base class with a mask """
12
+
13
+ def __init__(self, parent=None):
14
+ super().__init__(parent=parent)
15
+ self._isClosableOnMaskClicked = False
16
+ self._isDraggable = False
17
+ self._dragPos = QPoint()
18
+ self._hBoxLayout = QHBoxLayout(self)
19
+ self.windowMask = QWidget(self)
20
+
21
+ # dialog box in the center of mask, all widgets take it as parent
22
+ self.widget = QFrame(self, objectName='centerWidget')
23
+ self.setWindowFlags(Qt.FramelessWindowHint)
24
+ self.setAttribute(Qt.WA_TranslucentBackground)
25
+ self.setGeometry(0, 0, parent.width(), parent.height())
26
+
27
+ c = 0 if isDarkTheme() else 255
28
+ self.windowMask.resize(self.size())
29
+ self.windowMask.setStyleSheet(f'background:rgba({c}, {c}, {c}, 0.6)')
30
+ self._hBoxLayout.addWidget(self.widget)
31
+ self.setShadowEffect()
32
+
33
+ self.window().installEventFilter(self)
34
+ self.windowMask.installEventFilter(self)
35
+ self.widget.installEventFilter(self)
36
+
37
+ def setShadowEffect(self, blurRadius=60, offset=(0, 10), color=QColor(0, 0, 0, 100)):
38
+ """ add shadow to dialog """
39
+ shadowEffect = QGraphicsDropShadowEffect(self.widget)
40
+ shadowEffect.setBlurRadius(blurRadius)
41
+ shadowEffect.setOffset(*offset)
42
+ shadowEffect.setColor(color)
43
+ self.widget.setGraphicsEffect(None)
44
+ self.widget.setGraphicsEffect(shadowEffect)
45
+
46
+ def setMaskColor(self, color: QColor):
47
+ """ set the color of mask """
48
+ self.windowMask.setStyleSheet(f"""
49
+ background: rgba({color.red()}, {color.green()}, {color.blue()}, {color.alpha()})
50
+ """)
51
+
52
+ def showEvent(self, e):
53
+ """ fade in """
54
+ opacityEffect = QGraphicsOpacityEffect(self)
55
+ self.setGraphicsEffect(opacityEffect)
56
+ opacityAni = QPropertyAnimation(opacityEffect, b'opacity', self)
57
+ opacityAni.setStartValue(0)
58
+ opacityAni.setEndValue(1)
59
+ opacityAni.setDuration(200)
60
+ opacityAni.setEasingCurve(QEasingCurve.InSine)
61
+ opacityAni.finished.connect(lambda: self.setGraphicsEffect(None))
62
+ opacityAni.start()
63
+ super().showEvent(e)
64
+
65
+ def done(self, code):
66
+ """ fade out """
67
+ self.widget.setGraphicsEffect(None)
68
+ opacityEffect = QGraphicsOpacityEffect(self)
69
+ self.setGraphicsEffect(opacityEffect)
70
+ opacityAni = QPropertyAnimation(opacityEffect, b'opacity', self)
71
+ opacityAni.setStartValue(1)
72
+ opacityAni.setEndValue(0)
73
+ opacityAni.setDuration(100)
74
+ opacityAni.finished.connect(lambda: self._onDone(code))
75
+ opacityAni.start()
76
+
77
+ def _onDone(self, code):
78
+ self.setGraphicsEffect(None)
79
+ QDialog.done(self, code)
80
+
81
+ def isClosableOnMaskClicked(self):
82
+ return self._isClosableOnMaskClicked
83
+
84
+ def setClosableOnMaskClicked(self, isClosable: bool):
85
+ self._isClosableOnMaskClicked = isClosable
86
+
87
+ def setDraggable(self, draggable: bool):
88
+ self._isDraggable = draggable
89
+
90
+ def isDraggable(self) -> bool:
91
+ return self._isDraggable
92
+
93
+ def resizeEvent(self, e):
94
+ self.windowMask.resize(self.size())
95
+
96
+ def eventFilter(self, obj, e: QEvent):
97
+ if obj is self.window():
98
+ if e.type() == QEvent.Resize:
99
+ re = QResizeEvent(e)
100
+ self.resize(re.size())
101
+ elif obj is self.windowMask:
102
+ if e.type() == QEvent.MouseButtonRelease and e.button() == Qt.LeftButton \
103
+ and self.isClosableOnMaskClicked():
104
+ self.reject()
105
+ elif obj is self.widget and self.isDraggable():
106
+ if e.type() == QEvent.MouseButtonPress and e.button() == Qt.LeftButton:
107
+ if not self.widget.childrenRegion().contains(e.pos()):
108
+ self._dragPos = e.pos()
109
+ return True
110
+ elif e.type() == QEvent.MouseMove and not self._dragPos.isNull():
111
+ pos = self.widget.pos() + e.pos() - self._dragPos
112
+ pos.setX(max(0, min(pos.x(), self.width() - self.widget.width())))
113
+ pos.setY(max(0, min(pos.y(), self.height() - self.widget.height())))
114
+
115
+ self.widget.move(pos)
116
+ return True
117
+ elif e.type() == QEvent.MouseButtonRelease:
118
+ self._dragPos = QPoint()
119
+
120
+ return super().eventFilter(obj, e)
@@ -0,0 +1,92 @@
1
+ # coding:utf-8
2
+ from PySide6.QtCore import Qt, Signal
3
+ from PySide6.QtGui import QColor
4
+ from PySide6.QtWidgets import QFrame, QVBoxLayout, QHBoxLayout, QPushButton
5
+
6
+ from ...common.style_sheet import FluentStyleSheet
7
+ from ..widgets.button import PrimaryPushButton
8
+
9
+ from .mask_dialog_base import MaskDialogBase
10
+
11
+
12
+ class MessageBoxBase(MaskDialogBase):
13
+ """ Message box base """
14
+
15
+ def __init__(self, parent=None):
16
+ super().__init__(parent=parent)
17
+ self.buttonGroup = QFrame(self.widget)
18
+ self.yesButton = PrimaryPushButton(self.tr('OK'), self.buttonGroup)
19
+ self.cancelButton = QPushButton(self.tr('Cancel'), self.buttonGroup)
20
+
21
+ self.vBoxLayout = QVBoxLayout(self.widget)
22
+ self.viewLayout = QVBoxLayout()
23
+ self.buttonLayout = QHBoxLayout(self.buttonGroup)
24
+
25
+ self.__initWidget()
26
+
27
+ def __initWidget(self):
28
+ self.__setQss()
29
+ self.__initLayout()
30
+
31
+ self.setShadowEffect(60, (0, 10), QColor(0, 0, 0, 50))
32
+ self.setMaskColor(QColor(0, 0, 0, 76))
33
+
34
+ # fixes https://github.com/NumBNN/Python-FastUI-Widgets/issues/19
35
+ self.yesButton.setAttribute(Qt.WA_LayoutUsesWidgetRect)
36
+ self.cancelButton.setAttribute(Qt.WA_LayoutUsesWidgetRect)
37
+
38
+ self.yesButton.setAttribute(Qt.WA_MacShowFocusRect, False)
39
+
40
+ self.yesButton.setFocus()
41
+ self.buttonGroup.setFixedHeight(81)
42
+
43
+ self.yesButton.clicked.connect(self.__onYesButtonClicked)
44
+ self.cancelButton.clicked.connect(self.__onCancelButtonClicked)
45
+
46
+ def __initLayout(self):
47
+ self._hBoxLayout.removeWidget(self.widget)
48
+ self._hBoxLayout.addWidget(self.widget, 1, Qt.AlignCenter)
49
+
50
+ self.vBoxLayout.setSpacing(0)
51
+ self.vBoxLayout.setContentsMargins(0, 0, 0, 0)
52
+ self.vBoxLayout.addLayout(self.viewLayout, 1)
53
+ self.vBoxLayout.addWidget(self.buttonGroup, 0, Qt.AlignBottom)
54
+
55
+ self.viewLayout.setSpacing(12)
56
+ self.viewLayout.setContentsMargins(24, 24, 24, 24)
57
+
58
+ self.buttonLayout.setSpacing(12)
59
+ self.buttonLayout.setContentsMargins(24, 24, 24, 24)
60
+ self.buttonLayout.addWidget(self.yesButton, 1, Qt.AlignVCenter)
61
+ self.buttonLayout.addWidget(self.cancelButton, 1, Qt.AlignVCenter)
62
+
63
+ def validate(self) -> bool:
64
+ """ validate the data of form before closing dialog
65
+
66
+ Returns
67
+ -------
68
+ isValid: bool
69
+ whether the data of form is legal
70
+ """
71
+ return True
72
+
73
+ def __onCancelButtonClicked(self):
74
+ self.reject()
75
+
76
+ def __onYesButtonClicked(self):
77
+ if self.validate():
78
+ self.accept()
79
+
80
+ def __setQss(self):
81
+ self.buttonGroup.setObjectName('buttonGroup')
82
+ self.cancelButton.setObjectName('cancelButton')
83
+ FluentStyleSheet.DIALOG.apply(self)
84
+
85
+ def hideYesButton(self):
86
+ self.yesButton.hide()
87
+ self.buttonLayout.insertStretch(0, 1)
88
+
89
+ def hideCancelButton(self):
90
+ self.cancelButton.hide()
91
+ self.buttonLayout.insertStretch(0, 1)
92
+