用 PySide 覆盖 QPushButton Class 事件方法

Overriding QPushButton Class Event Method with PySide

我想制作一个允许点击拖放的自定义按钮class。我还希望能够控制它的美观(按钮颜色、鼠标悬停颜色、点击颜色、拖动颜色等)。

注意:这些示例是我需要做的较短的通用版本。我知道它不包含工作所需的所有内容,一些变量和函数的名称只是示例。我的真实示例将需要多达 15 个此 class 的实例,而不是显示的 4 个。

clicking 按钮的示例中,一旦我覆盖 mousePressEventmouseReleaseEvent my_button.clicked.connect(my_func) 语句不再有效。因为我想将此自定义 class 用于所有类型的按钮功能,所以我不能只将按钮功能放在覆盖的方法中,这是我当前的解决方案,我讨厌它(见下文)。我尝试在我的样式 sheet 更改后使用 super(DropZone_Category, self)mouseReleaseEvent(event) 语句(如其他问题中所建议的那样),但这对我来说绝对没有任何作用。我真正想用这些覆盖来做的就是控制样式 sheet 所以如果有更好的方法来做到这一点,请指教。

from PySide import QtGui, QtCore


class DropZone_Category(QtGui.QPushButton):
    def __init__(self, parent=None, *args, **kwargs):
        super(DropZone_Category, self).__init__(parent, *args, **kwargs)

        # Instance Attrs
        self.parent = parent

        # Style Variables
        self.dropZone_font = QtGui.QFont()
        self.dropZone_font.setPointSize(16)
        self.dropZone_font.setFamily("Calibri")
        self.default_button_stylesheet = "background-color: rgb(100,100,100);" \
                                         "color: lightgrey;" \
                                         "border-radius: 3px;"

        self.drag_button_stylesheet = "background-color: rgb(166,215,176);" \
                                          "color: rgb(0,97,19);" \
                                          "border-radius: 3px;"

        self.hover_button_stylesheet =  "background-color: rgb(150,150,150);" \
                                         "color: lightgrey;" \
                                         "border-radius: 3px;"

        self.clicked_button_stylesheet = "background-color: rgb(80,80,80);" \
                                         "color: lightgrey;" \
                                         "border-radius: 3px;"

        # Overrides
        self.resize(QtCore.QSize(120, 120))
        self.setFont(self.dropZone_font)
        self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        self.setMinimumSize(90, 90)
        self.setAcceptDrops(True)
        self.setStyleSheet(self.default_button_stylesheet)

    def enterEvent(self, *args, **kwargs):
        self.setStyleSheet(self.hover_button_stylesheet)

    def leaveEvent(self, *args, **kwargs):
        self.setStyleSheet(self.default_button_stylesheet)

    def mousePressEvent(self, *args, **kwargs):
        self.setStyleSheet(self.clicked_button_stylesheet)
        # super(DropZone_Category, self).mousePressEvent(event)

    def mouseReleaseEvent(self, event,*args, **kwargs):
        button_used = self.text()
        self.setStyleSheet(self.default_button_stylesheet)
        # super(DropZone_Category, self).mouseReleaseEvent(event)

        if button_used == "btn1":
            btn1_clicked_func()
        elif button_used == "btn2":
            btn2_clicked_func()
        elif button_used == "btn3":
            btn3_clicked_func()
        elif button_used == "btn4":
            btn4_clicked_func()

拖放 功能的示例中,我需要一种更简单、更清晰的方法来为每个按钮设置拖放功能,因为它对于每个需要的实例都是不同的.如果可能,类似于 my_button.clicked.connect(my_func) 的内容。如果不可能,我愿意接受其他建议。我目前正在像上面的 mouseReleaseEvent 方法一样处理它,但是在 dropEvent 方法中,我再次...讨厌。

def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            self.setStyleSheet(self.drag_button_stylesheet)

            event.acceptProposedAction()
        else:
            super(DropZone_Category, self).dragEnterEvent(event)

def dragLeaveEvent(self, event):
    self.setStyleSheet(self.default_button_stylesheet)

def dragMoveEvent(self, event):
    super(DropZone_Category, self).dragMoveEvent(event)

def dropEvent(self, event):
    zone_used = self.text()
    paths_list = [url.toLocalFile() for url in event.mimeData().urls()]
    num_files = len(paths_list)

    if not utils.isImages(paths_list): # check if drop objects are images
        print "Drop Rejected: Not all files dropped were images"
    elif not event.mimeData().hasUrls(): # check if drop objects are files
        print "Drop Rejected: Not all items dropped were files"
    else:
        if button_used == "btn1":
            btn1_dropped_func(paths_list )
        elif button_used == "btn2":
            btn2_dropped_func(paths_list )
        elif button_used == "btn3":
            btn3_dropped_func(paths_list )
        elif button_used == "btn4":
            btn4_dropped_func(paths_list )


        sleep(.5)
        self.setStyleSheet(self.default_button_stylesheet)

上面说了,之所以乱成这样,主要是因为我很想学习如何驾驭一个UI的审美。再加上将从这些 classes 中使用的大量非常具体的实例,以及添加的拖放功能,这让我很头疼。我目前的结论(与其他人的建议)是为我需要的每个特殊实例(即 DropZone_Categories 的 15 个子class )创建一个此基础 class 的子 class。我真的很讨厌这样做,因为感觉它违反了制作 classes 的全部原因。如果这是唯一的解决方案,我会很乐意这样做,但我只是想在弄得一团糟之前先看看。感谢和抱歉 post。

首先我看到你错误地使用了 Qt StyleSheet,QSS 处理像 hoverpressed 这样的伪状态,所以没有必要每时每刻都改变 QSS此外,这项任务很昂贵。

这种情况下的解决方案是使用伪状态:

DropZone_Category{
    background-color: rgb(100,100,100);
    color: lightgrey;
    border-radius: 3px;
}

DropZone_Category:hover{
    background-color: rgb(150,150,150);
    color: lightgrey;
    border-radius: 3px;
}

DropZone_Category:pressed{
    background-color: rgb(80,80,80);
    color: lightgrey;
    border-radius: 3px
}

根据您提到的内容,这是与您的 post 最相关的信息,您希望每个按钮以不同方式处理文件,因此最好的选择是创建一个信号,将文件发送到将负责处理它的插槽。在下面的示例中,创建了 4 个按钮,每个按钮都可以连接到文件获取的插槽,那么处理这些文件的逻辑部分由您负责。

import sys

from PySide import QtGui, QtCore


class DropZone_Category(QtGui.QPushButton):
    pathsChanged = QtCore.Signal(list)

    def __init__(self, *args, **kwargs):
        super(DropZone_Category, self).__init__(*args, **kwargs)
        font = self.font()
        font.setPointSize(16)
        font.setFamily("Calibri")
        self.setFont(font)
        self.resize(QtCore.QSize(120, 120))
        self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        self.setMinimumSize(90, 90)
        self.setAcceptDrops(True)

        QSS = """
            DropZone_Category{
                background-color: rgb(100,100,100);
                color: lightgrey;
                border-radius: 3px;
            }

            DropZone_Category:hover{
                background-color: rgb(150,150,150);
                color: lightgrey;
                border-radius: 3px;
            }

            DropZone_Category:pressed{
                background-color: rgb(80,80,80);
                color: lightgrey;
                border-radius: 3px
            }
        """
        self.setStyleSheet(QSS)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.acceptProposedAction()

    def dropEvent(self, event):
        paths_list = [url.toLocalFile() for url in event.mimeData().urls()]
        if paths_list:
            self.pathsChanged.emit(paths_list)

class Widget(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        lay = QtGui.QVBoxLayout(self)

        info = [("category 1", self.fun1), ("category 2", self.fun2), ("category 3", self.fun3), ("category 4", self.fun4)]

        for text, fun in info:
            button = DropZone_Category(text)
            button.pathsChanged.connect(fun)
            lay.addWidget(button)

    def fun1(self, files):
        print("fun1", files)

    def fun2(self, files):
        print("fun2", files)

    def fun3(self, files):
        print("fun3", files)

    def fun4(self, files):
        print("fun4", files)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())