简体   繁体   English

在PyQT5中创建自定义小部件

[英]Creating a custom widget in PyQT5

I would like to know how one can create a custom widget in pyqt. 我想知道如何在pyqt中创建自定义小部件。 I've seen many different examples for C++, and a couple non descript examples for pyqt, but nothing that really explains how to do it and implement it. 我已经看到了很多不同的C ++示例,以及pyqt的几个非描述性示例,但没有真正解释如何实现它并实现它。 There is especially no examples that basically aren't just modified qt-designer output, and I'm writing my code from scratch so that's not very helpful. 特别是没有例子基本上不只是修改了qt-designer输出,而且我从头开始编写我的代码,所以这不是很有用。

So far, the best example I could find was basically just someone modifying qt-designer code and not really explaining what any of it was doing. 到目前为止,我能找到的最好的例子基本上只是修改qt-designer代码的人,而不是真正解释其中的任何代码

Could someone please show me an example of how to create a custom widget? 有人可以告诉我一个如何创建自定义小部件的示例吗?

Edit: I'm attempting to create a widget with an embedded QStackedWidget , and buttons on the bottom to cycle the pages. 编辑:我正在尝试创建一个带有嵌入式QStackedWidget的小部件,以及底部的按钮来循环页面。

I also planned on having a seperate widget for each page, but considering I can't actually accomplish step one, I figured I would cross that bridge when I get to it. 我还计划为每个页面创建一个单独的小部件,但考虑到我实际上无法完成第一步,我认为当我到达它时我会越过那个桥。

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. 下面展示了如何使用2个按钮实现QStackedWidget ,基本思路是布局设计,为此我们分析必须放置QVBoxLayout来放置QStackedWidget和另一个布局,第二个布局将是QHBoxLayout到有按钮。 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. 同样在这个例子中,我创建了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_())

在此输入图像描述

在此输入图像描述

在此输入图像描述

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. 布局 :您在布局中添加的所有qt对象,项目或窗口小部件都将遵循其位置规则及其策略。
  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. StyleSheet:如果你在Widget对象中设置了Widget的样式,那就设置它的“CSS”,只是为了简洁。 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. 注意:如果是非Widget对象,您将无法设置StyleSheet,因此您必须覆盖一些绘制方法,创建自己的Painters等等。


Here are some random examples with some comments along approaching the 3 topics I mentioned above: 以下是一些随机的例子,其中有一些评论我接近上面提到的3个主题:

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. 例如,如果您需要一个空心环状小部件,您可以使用这种格式和一些透明度的图像,从中创建一个QPixMap并将其作为掩码应用于您的小部件。 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. 因为我发现你没有“顶栏”无边框的例子,你也可以在看这个的其他问题,我展示了如何创建自己的顶栏,左右移动和调整大小的概念。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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