简体   繁体   中英

Overriding QPushButton Class Event Method with PySide

I am wanting to make a custom button class that allows for clicking and drag and drop . I also want the ability to control the aesthetics of it (button color, mouse over color, clicked color, drag over color, etc.).

Note: these examples are shorter, generic versions of what I need to do. I know it doesn't contain all necessary things to work and some names of variables and functions are just examples. My real example will need as many as 15 instances of this class instead of the 4 shown.

In the example of clicking the button, as soon as I override the mousePressEvent and the mouseReleaseEvent the my_button.clicked.connect(my_func) statement no longer works. And being that I want to use this custom class for all kinds of button functions I can't just put the button functions in the overridden method, which is what my current solution is and I hate it (see below). I tried using the super(DropZone_Category, self)mouseReleaseEvent(event) statement after my style sheet changes (as suggested in other questions) but this did absolutely nothing for me. All I really wanted to do with these overrides was control the style sheet so if there is a better way to do this, please advise.

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()

In the example of the drag and drop functionality, I need an easier, cleaner way of setting up the drop function for each button, as it is different for each instance needed. Something similar to the my_button.clicked.connect(my_func) if possible. If not possible I'm open to other suggestions. I am currently handling it like the mouseReleaseEvent method above but in the dropEvent method, which I again...hate.

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)

As said above, the main reason for this mess is that that I really want to learn how to control the aesthetics of a UI. That coupled with the large number of very specific instances that will be used from these classes and the added drag and drop functionality has turned this into a headache for me. My current conclusion (with others advice) is to create a sub class of this base class for each special instance I need (ie 15 subclasses of DropZone_Categories). I would really hate to do this because it feels like it violates the entire reason for making classes. If this is the only solution I will happily do it but I just wanted to see before making even more of a mess. Thanks and SORRY for for the long post.

I see first of all that you are using the Qt StyleSheet incorrectly, the QSS handle pseudo-states like hover and pressed , so it is not necessary to change the QSS at every moment besides that task is expensive.

The solution in that case is to use the pseudo-states:

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
}

According to what you mention, which was the most relevant information regarding your post, you want each button to process the files differently, so the best option is to create a signal that sends the files to the slot that will be responsible for processing it. In the following example, 4 buttons have been created where each one can be connected to a slot that the files obtain, then it is your responsibility the part of the logic that processes those files.

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_())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM