首页 文章

在PyQT5中创建自定义小部件

提问于
浏览
3

我想知道如何在pyqt中创建自定义小部件 . 我已经看到了C的许多不同的例子,以及几个pyqt的非描述性例子,但没有真正解释如何实现它并实现它 . 特别是没有一个例子基本上不只是修改了qt-designer输出,而且我从头开始编写我的代码,所以这不是很有用 .

到目前为止,我能找到的最好的例子是basically just someone modifying qt-designer code并没有真正解释其中的任何内容 .

有人可以告诉我一个如何创建自定义小部件的示例吗?

Edit: 我正在尝试使用嵌入的 QStackedWidget 创建一个小部件,并在底部按钮来循环页面 .

我还计划为每个页面创建一个单独的小部件,但考虑到我实际上无法完成第一步,我认为当我到达它时我会越过那个桥 .

2 回答

  • 3

    下面将展示如何用2个按钮实现 QStackedWidget ,基本思路是布局设计,为此我们分析一个 QVBoxLayout 必须放置 QStackedWidget 和另一个布局,这第二个布局将是 QHBoxLayout 到有按钮 . 然后我们连接处理页面之间转换的信号 . 同样在这个例子中,我创建了3种类型的小部件,这些小部件将放置在每个页面上 .

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

    这里有一些很好的建议,例子和方法 .

    我认为您可以通过三种方式划分自定义窗口小部件或任何自定义“事物” .

    • Behavior:使用所需行为覆盖其默认方法时 .

    • Layout:您在布局中添加的所有qt对象,项目或窗口小部件都将遵循其位置规则及其策略 .

    • StyleSheet:如果是Widget对象,你设置Widget的样式,让我们设置它的"CSS",只是为了简洁 . 这里有一些referencesexamples .


    Note: 如果是非Widget对象,您将无法设置StyleSheet,因此您必须覆盖一些绘制方法,创建自己的Painters等等 .


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

    小贴士:

    您还可以在窗口小部件中使用蒙版,以“疯狂”方式更改其格式 . 例如,如果您需要一个空心环状小部件,您可以拥有具有此格式和一些透明度的图像,从中创建一个QPixMap并将其作为掩码应用于您的小部件 . 不是一件微不足道的工作,而是一种很酷的工作 .

    由于我向您展示了没有框架的没有"TopBar"的示例,您还可以查看this其他问题,其中我将展示如何创建自己的顶部栏,移动并调整概念 .

相关问题