Creating a custom widget in PyQT5

后端 未结 2 1585
谎友^
谎友^ 2020-12-15 13:58

I would like to know how one can create a custom widget in pyqt. I\'ve seen many different examples for C++, and a couple non descript examples for pyqt, but nothing that re

相关标签:
2条回答
  • 2020-12-15 14:10

    In the following it is shown how to implement a QStackedWidget with 2 buttons, the basic idea is to layout the design, for this we analyze that a QVBoxLayout must be placed to place the QStackedWidget and another layout, this second layout will be a QHBoxLayout to have the buttons. Then we connect the signals that handle the transition between pages. Also in this example I have created 3 types of widgets that will be placed on each page.

    from PyQt5.QtWidgets import *
    
    
    class Widget1(QWidget):
        def __init__(self, parent=None):
            QWidget.__init__(self, parent=parent)
            lay = QVBoxLayout(self)
            for i in range(4):
                lay.addWidget(QPushButton("{}".format(i)))
    
    class Widget2(QWidget):
        def __init__(self, parent=None):
            QWidget.__init__(self, parent=parent)
            lay = QVBoxLayout(self)
            for i in range(4):
                lay.addWidget(QLineEdit("{}".format(i)))
    
    class Widget3(QWidget):
        def __init__(self, parent=None):
            QWidget.__init__(self, parent=parent)
            lay = QVBoxLayout(self)
            for i in range(4):
                lay.addWidget(QRadioButton("{}".format(i)))
    
    class stackedExample(QWidget):
        def __init__(self, parent=None):
            QWidget.__init__(self, parent=parent)
            lay = QVBoxLayout(self)
            self.Stack = QStackedWidget()
            self.Stack.addWidget(Widget1())
            self.Stack.addWidget(Widget2())
            self.Stack.addWidget(Widget3())
    
            btnNext = QPushButton("Next")
            btnNext.clicked.connect(self.onNext)
            btnPrevious = QPushButton("Previous")
            btnPrevious.clicked.connect(self.onPrevious)
            btnLayout = QHBoxLayout()
            btnLayout.addWidget(btnPrevious)
            btnLayout.addWidget(btnNext)
    
            lay.addWidget(self.Stack)
            lay.addLayout(btnLayout)
    
        def onNext(self):
            self.Stack.setCurrentIndex((self.Stack.currentIndex()+1) % 3)
    
        def onPrevious(self):
            self.Stack.setCurrentIndex((self.Stack.currentIndex()-1) % 3)
    
    
    if __name__ == '__main__':
        import sys
        app = QApplication(sys.argv)
        w = stackedExample()
        w.show()
        sys.exit(app.exec_())
    

    0 讨论(0)
  • 2020-12-15 14:22

    Here are some nice advises, examples and approaches.

    I think you can divide a custom Widget or any Custom "thing" you want in three ways.

    1. Behavior: When you override its default methods with the behavior you want.
    2. Layout: All the qt objects, be Items, or Widgets you add inside the layout will follow it's position rules and its policies.
    3. StyleSheet: In case of Widget objects where you set the style of the Widget let's say setting its "CSS", just to be concise. Here are some references and examples.

    Note: In case of non Widget objects you will not be able to set a StyleSheet so you will have to override some paint methods, create your own Painters and so on.


    Here are some random examples with some comments along approaching the 3 topics I mentioned above:

    import random
    import sys
    
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import QApplication
    from PyQt5.QtWidgets import QDialog
    from PyQt5.QtWidgets import QHBoxLayout
    from PyQt5.QtWidgets import QPushButton
    from PyQt5.QtWidgets import QVBoxLayout
    from PyQt5.QtWidgets import QWidget
    
    
    
    class MovableWidget(QWidget):
    
        def __init__(self):
            super(MovableWidget, self).__init__()
    
            #remove the frame
            self.setWindowFlags(Qt.CustomizeWindowHint)
            self.pressing = False
    
        # overriding the three next methods is a way to customize your Widgets
        # not just in terms of appearance but also behavioral.
    
        def mousePressEvent(self, QMouseEvent):
            #the pos of the widget when you first pressed it.
            self.start = QMouseEvent.pos()
            #to make sure you are holding mouse button down
            self.pressing = True
    
        def mouseMoveEvent(self, QMouseEvent):
    
            # You can Verify if it's also the left button and some other things
            # you need.
            if self.pressing : #and QMouseEvent.type() == Qt.LeftButton
                self.end = QMouseEvent.pos()
                self.delta = self.mapToGlobal(self.end-self.start)
                self.move(self.delta)
                self.end = self.start
    
        def mouseReleaseEvent(self, QMouseEvent):
            self.pressing = False
    
    # inherits from QDialog and from MovableWidget so we can have its properties.
    class CustomDialog(QDialog, MovableWidget):
    
        def __init__(self):
            super(CustomDialog, self).__init__()
    
            #Make the Dialog transparent
            self.setAttribute(Qt.WA_TranslucentBackground)
    
            # the widget will dispose itself according to the layout rules he's
            # inserted into.
            self.inner_widget = QWidget()
            self.inner_widget.setFixedSize(300,300)
            self.inner_layout = QHBoxLayout()
            self.inner_widget.setLayout(self.inner_layout)
    
            self.btn_change_color = QPushButton("Roll Color")
    
            self.btn_change_color.setStyleSheet("""
                background-color: green;
            """)
    
            # will connect to a function to be executed when the button is clicked.
            self.btn_change_color.clicked.connect(self.change_color)
            self.inner_layout.addWidget(self.btn_change_color)
    
            # Choose among many layouts according to your needs, QVBoxLayout,
            # QHBoxLayout, QStackedLayout, ... you can set its orientation
            # you can set its policies, spacing, margins. That's one of the main
            # concepts you have to learn to customize your Widget in the way
            # you want.
            self.layout = QVBoxLayout()
    
            # stylesheet have basically CSS syntax can call it QSS.
            # it can be used only on objects that come from Widgets
            # Also one of the main things to learn about customizing Widgets.
    
            # Note: The stylesheet you set in the "father" will be applied to its
            # children. Unless you tell it to be applied only to it and/or specify
            # each children's style.
    
            # The point I used inside the StyleSheet before the QDialog
            # e.g .QDialog and .QWidget says it'll be applied only to that
            # instance.
    
            self.setStyleSheet("""
                .QDialog{
                    border-radius: 10px;
                }
            """)
            self.inner_widget.setStyleSheet("""
                .QWidget{
                    background-color: red;
                }
            """)
    
    
            self.layout.addWidget(self.inner_widget)
            self.setLayout(self.layout)
    
        def change_color(self):
            red = random.choice(range(0,256))
            green = random.choice(range(0,256))
            blue = random.choice(range(0,256))
            self.inner_widget.setStyleSheet(
            """
                background-color: rgb({},{},{});
            """.format(red,green,blue)
            )
    
    # since MovableWidget inherits from QWidget it also have QWidget properties.
    class ABitMoreCustomizedWidget(MovableWidget):
    
        def __init__(self):
            super(ABitMoreCustomizedWidget, self).__init__()
    
            self.layout = QHBoxLayout()
            self.setLayout(self.layout)
    
            self.custom_button1 = CustomButton("Button 1")
            self.custom_button1.clicked.connect(self.btn_1_pressed)
            self.custom_button2 = CustomButton("Button 2")
            self.custom_button2.clicked.connect(self.btn_2_pressed)
    
            self.layout.addWidget(self.custom_button1)
            self.layout.addWidget(self.custom_button2)
    
        def btn_1_pressed(self):
            self.custom_button1.hide()
            self.custom_button2.show()
    
        def btn_2_pressed(self):
            self.custom_button2.hide()
            self.custom_button1.show()
    
    class CustomButton(QPushButton):
    
        # it could receive args and keys** so all the QPushButton initializer
        # would work for here too.
        def __init__(self, txt):
            super(CustomButton, self).__init__()
            self.setText(txt)
            self.setStyleSheet("""
                QPushButton{
                    background-color: black;
                    border-radius: 5px;
                    color: white;
                }
                QPushButton::pressed{
                    background-color: blue;
                }
                QPushButton::released{
                    background-color: gray;
                }
            """)
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        custom_dialog = CustomDialog()
        custom_widget = ABitMoreCustomizedWidget()
        custom_dialog.show()
        custom_widget.show()
        sys.exit(app.exec_())
    

    Tips:

    You are also able to make use of masks in your widget changing it's format in "crazy" ways. For example if you need a hollow ringed widget you can have a image with this format and some transparency, create a QPixMap from that and apply it as a mask to your widget. Not a trivial work but kind of cool.

    Since I showed you examples with no "TopBar" with no Frame you can also have a look in this other question where I show how to create your own top bar, move around and resize concepts.

    0 讨论(0)
提交回复
热议问题