包含按钮、标签等的复合小部件。如何构建以及如何使用它

Composite Widget containig buttons, labels, etc. How to build such and how to use it

我正在寻找最合适的解决方案来创建复合小部件,我可以在应用程序的几个地方重复使用(图形控制界面有几个子菜单,其中一些复杂的小部件可以重复使用)。

主要是我试图创建我将在多个地方使用的自定义 QWidget、QLabel 和 QFrame 对象。我准备了这样的例子:

class FansLabelsRight(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        print('is it working?')
        self.setGeometry(275,65,110,140)
        self.setStyleSheet("background: rgba(255,0,0,50)")
        #self.layout = QLayout(self)
        self.pane1 = QLabel(self)
        self.pane2 = QLabel(self)
        self.pane3 = QLabel(self)
        pixmap1 = QPixmap('rtC.png')
        pixmap2 = QPixmap('rtB.png')
        pixmap3 = QPixmap('rtA.png')
        self.pane1.setPixmap(pixmap1)
        self.pane2.setPixmap(pixmap2)
        self.pane3.setPixmap(pixmap3)
        #self.layout.addWidget(self.pane1)
        #self.layout.addWidget(self.pane2)
        #self.layout.addWidget(self.pane3)
        self.pane1.setGeometry(30,10,pixmap1.width(), pixmap1.height())
        self.pane2.setGeometry(35,30,pixmap2.width(), pixmap2.height())
        self.pane3.setGeometry(40,30,pixmap3.width(), pixmap3.height())
        self.setLayout(self.layout)

        self.show()

没用。除了我测试过的其他一些没有积极结果的方法之外,我还期望我基于 Q tDesigner 构建的方法——创建自定义对象,它实际上添加到我的 QMainWindow class 其他小部件,但这不是我所期望的,也不是我读到的应该是可以的。换句话说,我想到了带有子部件的容器。真的可以吗?

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        self.label = QLabel(Form)
        self.label.setGeometry(QtCore.QRect(60, 10, 61, 81))
        self.label.setPixmap(QPixmap("rtA.png"))
        self.label.setObjectName("label")
        self.label_2 = QLabel(Form)
        self.label_2.setGeometry(QtCore.QRect(70, 30, 51, 111))
        self.label_2.setPixmap(QPixmap("rtB.png"))
        self.label_2.setObjectName("label_2")
        self.label_3 = QLabel(Form)
        self.label_3.setGeometry(QtCore.QRect(60, 90, 47, 61))
        self.label_3.setPixmap(QPixmap("rtC.png"))
        self.label_3.setObjectName("label_3")
        self.pushButton = QPushButton(Form)
        self.pushButton.setGeometry(QtCore.QRect(0, 0, 75, 61))
        self.pushButton.setFlat(True)
        self.pushButton.setObjectName("pushButton")
        self.pushButton_2 = QPushButton(Form)
        self.pushButton_2.setGeometry(QtCore.QRect(0, 100, 75, 61))
        self.pushButton_2.setFlat(True)
        self.pushButton_2.setObjectName("pushButton_2")

        QtCore.QMetaObject.connectSlotsByName(Form)

你能告诉我如何实现吗?如何创建自己的小部件,该小部件将从多个对象构建并将它们之间的整个逻辑保存在其 class 中? (按照逻辑,我理解按下一个按钮将显示另一个标签,而另一个按钮将显示命令,等等...)

我遇到的一个问题是布局,它不允许我将这些标签放在我想要的地方。

这是它应该是什么样子的示例。

使用 Qt 提供的基本小部件可以做您想做的事,但需要牢记一些缺点和注意事项。

我想您要显示的三个图像是右侧的 "level" 形状,如果是这种情况,您不能使用布局,因为它们的几何形状重叠:对于此类小部件看,您必须 "embed" 子窗口小部件而不将它们分配给布局。

然后,如果你想让那些半圆形箭头像按钮一样做出反应,显然你不能使用基本的 QPushButtons,因为它们是矩形的。一个解决方案是子类化,覆盖 paintEvent(使用您的图像提供按钮的自定义绘图),然后使用图像为小部件创建一个遮罩,这样点击事件只会在其边界内发生。

这是一个基本示例:

class MyButton(QtWidgets.QPushButton):
    def __init__(self, image, parent):
        super().__init__(parent)
        self.image = QtGui.QPixmap(image)
        self.setFixedSize(self.image.size())
        self.setMask(self.image.createHeuristicMask())

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        qp.drawPixmap(QtCore.QPoint(), self.image)


class MyWidget(QtWidgets.QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setFixedSize(110, 140)
        self.upButton = MyButton('uparrow.png', self)
        self.downButton = MyButton('downarrow.png', self)
        self.downButton.move(0, self.height() - self.downButton.height())

        self.rtcA = QtWidgets.QLabel(self)
        self.rtcA.setPixmap(QtGui.QPixmap('rtcA.png'))

        self.rtcB = QtWidgets.QLabel(self)
        self.rtcB.setPixmap(QtGui.QPixmap('rtcB.png'))

        self.rtcC = QtWidgets.QLabel(self)
        self.rtcC.setPixmap(QtGui.QPixmap('rtcC.png'))

        for label in self.rtcA, self.rtcB, self.rtcC:
            label.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)

        self.upButton.clicked.connect(self.up)
        self.downButton.clicked.connect(self.down)

        p = self.palette()
        p.setColor(p.Window, QtGui.QColor(57, 57, 57))
        self.setPalette(p)

    def up(self):
        print('going up')

    def down(self):
        print('going down')

这是它的样子:

如您所见,我没有使用任何布局:您只需将该小部件添加为 parent 参数即可将子级添加到 "container" 小部件;这使这些子项出现在容器内部,并且它们可以在父项边界内自由移动。

一些小笔记。
对于 3 个指标,我使用的图像已经 "translate" 实际图像内容(意味着它们位于小部件的左上角)。这是为了确保它们正确对齐,并避免通过不断的反复试验来获得小部件上的位置;例如,第三张图片实际上是这样的:

为了使这项工作按预期进行,我必须制作这些小部件 "transparent for mouse events":因为它们是在按钮 之后添加的,所以它们将位于按钮之上,并且按钮不会收到任何鼠标事件,因为标签将在路上。另一种可能性是在标签 之后添加按钮 ,或者使用 raise_()lower():

    self.upButton.raise_()
    self.downButton.raise_()
    # or, alternatively
    for label in self.rtcA, self.rtcB, self.rtcC:
        label.lower()

作为另一种可能性,您可以为每个标签创建 "mask images"(在每个部分的边界周围使用纯色非 alpha 背景)并使用 setMask(QtGui.QPixmap('rtcA_mask.png').createHeuristicMask()) 与我所做的类似按钮。这种方法的好处是,如果您需要它们,可以从这些标签正确接收鼠标事件。