folder_list_dialog.py 10.6 KB
Newer Older
1 2 3
# coding:utf-8
import os

之一Yo's avatar
之一Yo 已提交
4
from PyQt5.QtCore import Qt, pyqtSignal
5 6
from PyQt5.QtGui import (QBrush, QColor, QFont, QFontMetrics, QMouseEvent,
                         QPainter, QPen, QPixmap)
之一Yo's avatar
之一Yo 已提交
7 8
from PyQt5.QtWidgets import (QApplication, QFileDialog, QHBoxLayout, QLabel,
                             QVBoxLayout, QWidget, QPushButton)
9

之一Yo's avatar
之一Yo 已提交
10
from ...common.config import qconfig
之一Yo's avatar
之一Yo 已提交
11 12 13 14 15
from ...common.icon import getIconColor
from ...common.style_sheet import setStyleSheet
from .dialog import Dialog
from .mask_dialog_base import MaskDialogBase
from ..widgets.scroll_area import ScrollArea
16 17


之一Yo's avatar
之一Yo 已提交
18 19
class FolderListDialog(MaskDialogBase):
    """ Folder list dialog box """
20 21 22 23 24 25 26

    folderChanged = pyqtSignal(list)

    def __init__(self, folderPaths: list, title: str, content: str, parent):
        super().__init__(parent=parent)
        self.title = title
        self.content = content
之一Yo's avatar
之一Yo 已提交
27
        self.__originalPaths = folderPaths
28
        self.folderPaths = folderPaths.copy()
之一Yo's avatar
之一Yo 已提交
29 30

        self.vBoxLayout = QVBoxLayout(self.widget)
31 32
        self.titleLabel = QLabel(title, self.widget)
        self.contentLabel = QLabel(content, self.widget)
之一Yo's avatar
之一Yo 已提交
33 34
        self.scrollArea = ScrollArea(self.widget)
        self.scrollWidget = QWidget(self.scrollArea)
之一Yo's avatar
之一Yo 已提交
35
        self.completeButton = QPushButton(self.tr('Done'), self.widget)
之一Yo's avatar
之一Yo 已提交
36 37
        self.addFolderCard = AddFolderCard(self.scrollWidget)
        self.folderCards = [FolderCard(i, self.scrollWidget)
38 39 40 41
                            for i in folderPaths]
        self.__initWidget()

    def __initWidget(self):
之一Yo's avatar
之一Yo 已提交
42
        """ initialize widgets """
43
        self.__setQss()
之一Yo's avatar
之一Yo 已提交
44 45 46 47 48 49 50 51 52 53 54

        w = max(self.titleLabel.width()+60, self.contentLabel.width()+60, 440)
        self.widget.setFixedWidth(w)
        self.scrollArea.resize(368, 90)
        self.scrollWidget.resize(365, 90)
        self.scrollArea.setFixedWidth(368)
        self.scrollWidget.setFixedWidth(365)
        self.scrollArea.setMaximumHeight(500)
        self.scrollArea.setViewportMargins(0, 0, 0, 0)
        self.scrollArea.setWidget(self.scrollWidget)
        self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
55
        self.__initLayout()
之一Yo's avatar
之一Yo 已提交
56 57 58

        # connect signal to slot
        self.addFolderCard.clicked.connect(self.__showFileDialog)
59 60
        self.completeButton.clicked.connect(self.__onButtonClicked)
        for card in self.folderCards:
之一Yo's avatar
之一Yo 已提交
61
            card.clicked.connect(self.__showDeleteFolderCardDialog)
62 63

    def __initLayout(self):
之一Yo's avatar
之一Yo 已提交
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
        """ initialize layout """
        self.vBoxLayout.setContentsMargins(30, 30, 30, 30)
        self.vBoxLayout.setSizeConstraint(QVBoxLayout.SetFixedSize)
        self.vBoxLayout.setAlignment(Qt.AlignTop)
        self.vBoxLayout.setSpacing(0)

        # labels
        layout_1 = QVBoxLayout()
        layout_1.setContentsMargins(0, 0, 0, 0)
        layout_1.setSpacing(7)
        layout_1.addWidget(self.titleLabel, 0, Qt.AlignTop)
        layout_1.addWidget(self.contentLabel, 0, Qt.AlignTop)
        self.vBoxLayout.addLayout(layout_1, 0)
        self.vBoxLayout.addSpacing(15)

        # cards
        layout_2 = QHBoxLayout()
        layout_2.setAlignment(Qt.AlignCenter)
        layout_2.setContentsMargins(5, 0, 5, 0)
        layout_2.addWidget(self.scrollArea, 0, Qt.AlignCenter)
        self.vBoxLayout.addLayout(layout_2, 1)
        self.vBoxLayout.addSpacing(30)

        self.scrollLayout = QVBoxLayout(self.scrollWidget)
        self.scrollLayout.setAlignment(Qt.AlignTop)
        self.scrollLayout.setContentsMargins(0, 0, 0, 0)
        self.scrollLayout.setSpacing(10)
        self.scrollLayout.addWidget(self.addFolderCard, 0, Qt.AlignTop)
        for card in self.folderCards:
            self.scrollLayout.addWidget(card, 0, Qt.AlignTop)

        # buttons
        layout_3 = QHBoxLayout()
        layout_3.setContentsMargins(0, 0, 0, 0)
        layout_3.addStretch(1)
        layout_3.addWidget(self.completeButton)
        self.vBoxLayout.addLayout(layout_3, 0)

        self.__adjustWidgetSize()

    def __showFileDialog(self):
        """ show file dialog to select folder """
        path = QFileDialog.getExistingDirectory(
            self, self.tr("Choose folder"), "./")

        if not path or path in self.folderPaths:
            return

        # create folder card
        card = FolderCard(path, self.scrollWidget)
        self.scrollLayout.addWidget(card, 0, Qt.AlignTop)
        card.clicked.connect(self.__showDeleteFolderCardDialog)
        card.show()

        self.folderPaths.append(path)
        self.folderCards.append(card)

        self.__adjustWidgetSize()

    def __showDeleteFolderCardDialog(self):
        """ show delete folder card dialog """
125
        sender = self.sender()
之一Yo's avatar
之一Yo 已提交
126 127 128 129
        title = self.tr('Are you sure you want to delete the folder?')
        content = self.tr("If you delete the ") + f'"{sender.folderName}"' + \
            self.tr(" folder and remove it from the list, the folder will no "
                    "longer appear in the list, but will not be deleted.")
130
        dialog = Dialog(title, content, self.window())
之一Yo's avatar
之一Yo 已提交
131
        dialog.yesSignal.connect(lambda: self.__deleteFolderCard(sender))
132 133
        dialog.exec_()

之一Yo's avatar
之一Yo 已提交
134 135 136
    def __deleteFolderCard(self, folderCard):
        """ delete selected folder card """
        self.scrollLayout.removeWidget(folderCard)
137 138 139 140
        index = self.folderCards.index(folderCard)
        self.folderCards.pop(index)
        self.folderPaths.pop(index)
        folderCard.deleteLater()
之一Yo's avatar
之一Yo 已提交
141 142 143

        # adjust height
        self.__adjustWidgetSize()
144 145

    def __setQss(self):
之一Yo's avatar
之一Yo 已提交
146
        """ set style sheet """
147 148 149
        self.titleLabel.setObjectName('titleLabel')
        self.contentLabel.setObjectName('contentLabel')
        self.completeButton.setObjectName('completeButton')
之一Yo's avatar
之一Yo 已提交
150 151
        self.scrollWidget.setObjectName('scrollWidget')

之一Yo's avatar
之一Yo 已提交
152
        setStyleSheet(self, 'folder_list_dialog')
之一Yo's avatar
之一Yo 已提交
153
        self.setStyle(QApplication.style())
之一Yo's avatar
之一Yo 已提交
154

之一Yo's avatar
之一Yo 已提交
155 156 157 158
        self.titleLabel.adjustSize()
        self.contentLabel.adjustSize()
        self.completeButton.adjustSize()

159
    def __onButtonClicked(self):
之一Yo's avatar
之一Yo 已提交
160 161 162 163
        """ done button clicked slot """
        if sorted(self.__originalPaths) != sorted(self.folderPaths):
            self.setEnabled(False)
            QApplication.processEvents()
164
            self.folderChanged.emit(self.folderPaths)
之一Yo's avatar
之一Yo 已提交
165

166 167
        self.close()

之一Yo's avatar
之一Yo 已提交
168 169 170 171 172 173
    def __adjustWidgetSize(self):
        N = len(self.folderCards)
        h = 90*(N+1) + 10*N
        self.scrollArea.setFixedHeight(min(h, 500))
        self.scrollWidget.setFixedHeight(h)

174 175

class ClickableWindow(QWidget):
之一Yo's avatar
之一Yo 已提交
176
    """ Clickable window """
177 178 179 180 181 182 183

    clicked = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setWindowFlags(Qt.FramelessWindowHint)
之一Yo's avatar
之一Yo 已提交
184
        self.setFixedSize(365, 90)
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
        self._isPressed = None
        self._isEnter = False

    def enterEvent(self, e):
        self._isEnter = True
        self.update()

    def leaveEvent(self, e):
        self._isEnter = False
        self.update()

    def mouseReleaseEvent(self, e):
        self._isPressed = False
        self.update()
        if e.button() == Qt.LeftButton:
            self.clicked.emit()

    def mousePressEvent(self, e: QMouseEvent):
        self._isPressed = True
        self.update()

    def paintEvent(self, e):
之一Yo's avatar
之一Yo 已提交
207
        """ paint window """
208 209
        painter = QPainter(self)
        painter.setRenderHints(QPainter.Antialiasing)
之一Yo's avatar
之一Yo 已提交
210

之一Yo's avatar
之一Yo 已提交
211
        isDark = qconfig.theme == 'dark'
之一Yo's avatar
之一Yo 已提交
212 213
        bg = 51 if isDark else 204
        brush = QBrush(QColor(bg, bg, bg))
214
        painter.setPen(Qt.NoPen)
之一Yo's avatar
之一Yo 已提交
215

216 217 218 219
        if not self._isEnter:
            painter.setBrush(brush)
            painter.drawRoundedRect(self.rect(), 5, 5)
        else:
之一Yo's avatar
之一Yo 已提交
220
            painter.setPen(QPen(QColor(bg, bg, bg), 2))
221 222 223
            painter.drawRect(1, 1, self.width() - 2, self.height() - 2)
            painter.setPen(Qt.NoPen)
            if not self._isPressed:
之一Yo's avatar
之一Yo 已提交
224 225
                bg = 24 if isDark else 230
                brush.setColor(QColor(bg, bg, bg))
226 227 228
                painter.setBrush(brush)
                painter.drawRect(2, 2, self.width() - 4, self.height() - 4)
            else:
之一Yo's avatar
之一Yo 已提交
229
                bg = 102 if isDark else 230
230 231 232 233 234 235 236
                brush.setColor(QColor(153, 153, 153))
                painter.setBrush(brush)
                painter.drawRoundedRect(
                    6, 1, self.width() - 12, self.height() - 2, 3, 3)


class FolderCard(ClickableWindow):
之一Yo's avatar
之一Yo 已提交
237
    """ Folder card """
238 239 240 241 242

    def __init__(self, folderPath: str, parent=None):
        super().__init__(parent)
        self.folderPath = folderPath
        self.folderName = os.path.basename(folderPath)
之一Yo's avatar
之一Yo 已提交
243 244
        c = getIconColor()
        self.__closeIcon = QPixmap(f":/qfluentwidgets/images/folder_list_dialog/Close_{c}.png")
245 246

    def paintEvent(self, e):
之一Yo's avatar
之一Yo 已提交
247
        """ paint card """
248 249 250
        super().paintEvent(e)
        painter = QPainter(self)
        painter.setRenderHints(
之一Yo's avatar
之一Yo 已提交
251
            QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform | QPainter.Antialiasing)
之一Yo's avatar
之一Yo 已提交
252 253

        # paint text and icon
之一Yo's avatar
之一Yo 已提交
254
        color = Qt.white if qconfig.theme == 'dark' else Qt.black
之一Yo's avatar
之一Yo 已提交
255
        painter.setPen(color)
256 257
        if self._isPressed:
            self.__drawText(painter, 15, 10, 15, 9)
之一Yo's avatar
之一Yo 已提交
258
            painter.drawPixmap(self.width() - 33, 23, self.__closeIcon)
259 260
        else:
            self.__drawText(painter, 12, 11, 12, 10)
之一Yo's avatar
之一Yo 已提交
261
            painter.drawPixmap(self.width() - 30, 25, self.__closeIcon)
262 263

    def __drawText(self, painter, x1, fontSize1, x2, fontSize2):
之一Yo's avatar
之一Yo 已提交
264 265
        """ draw text """
        # paint folder name
266 267 268 269 270
        font = QFont("Microsoft YaHei", fontSize1, 75)
        painter.setFont(font)
        name = QFontMetrics(font).elidedText(
            self.folderName, Qt.ElideRight, self.width()-60)
        painter.drawText(x1, 37, name)
之一Yo's avatar
之一Yo 已提交
271 272

        # paint folder path
273 274 275 276 277 278 279 280
        font = QFont("Microsoft YaHei", fontSize2)
        painter.setFont(font)
        path = QFontMetrics(font).elidedText(
            self.folderPath, Qt.ElideRight, self.width()-30)
        painter.drawText(x2, 46, self.width() - 20, 23, Qt.AlignLeft, path)


class AddFolderCard(ClickableWindow):
之一Yo's avatar
之一Yo 已提交
281
    """ Add folder card """
282 283 284

    def __init__(self, parent=None):
        super().__init__(parent)
之一Yo's avatar
之一Yo 已提交
285 286
        c = getIconColor()
        self.__iconPix = QPixmap(f":/qfluentwidgets/images/folder_list_dialog/Add_{c}.png")
287 288

    def paintEvent(self, e):
之一Yo's avatar
之一Yo 已提交
289
        """ paint card """
290 291
        super().paintEvent(e)
        painter = QPainter(self)
之一Yo's avatar
之一Yo 已提交
292 293 294 295
        w = self.width()
        h = self.height()
        pw = self.__iconPix.width()
        ph = self.__iconPix.height()
296 297
        if not self._isPressed:
            painter.drawPixmap(
之一Yo's avatar
之一Yo 已提交
298
                int(w/2 - pw/2), int(h/2 - ph/2), self.__iconPix)
299 300
        else:
            painter.drawPixmap(
之一Yo's avatar
之一Yo 已提交
301
                int(w/2 - (pw - 4)/2), int(h/2 - (ph - 4)/2), pw - 4, ph - 4, self.__iconPix)