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,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
|
+
|