如何添加自定义小部件(带布局)重叠另一个自定义小部件(带布局)pyqt

How to add custom widget (with layout) overlapping another custom widget (with layout) pyqt

我需要添加两个与 2 个 QPushButton 重叠的标签。如果用户点击标签右侧,则点击右侧按钮,如果点击标签左侧,则点击左侧按钮。

我希望有这样的东西:

目标是保存 space 数据以供显示。

为此,我创建了一个 class,它继承自 QWidget,带有一个 HBoxlayout 和 2 个按钮。

class DoublePushButton(QWidget):
    clicked = Signal(int)

    def __init__(self, **kargs) -> None:
        super().__init__(**kargs)

        self.data_to_display = 0

        self.right_button = QPushButton()
        self.right_button.pressed.connect(self.rightF)

        self.left_button = QPushButton()
        self.left_button.pressed.connect(self.leftF)

        self.button_layout = QHBoxLayout()
        self.button_layout.setContentsMargins(0,0,0,0)
        self.button_layout.setSpacing(0)

        buttons = [self.left_button,self.right_button]
        for i in buttons:
            self.setButtonParam(i)
            self.button_layout.addWidget(i)

        self.setLayout(self.button_layout)

    def setButtonParam(self, button):
        button.setFlat(True)
        button.setMinimumSize(30,120)
        button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

    def rightF(self):
        self.clicked.emit(1)

    def leftF(self):
        self.clicked.emit(-1)

然后我在 VBoxLaout 中创建了 2 个标签:

class TitleData(QWidget):
    def __init__(self,title="Title", data="Data", **kwargs):
        super().__init__(**kwargs)
        self.title = title
        self.data = data

        self.title_label = QLabel(self.title)
        self.data_label = QLabel(self.data)
        self.title_label.setAttribute(Qt.WA_NoSystemBackground)
        self.title_label.setAttribute(Qt.WA_TransparentForMouseEvents)
        self.data_label.setAttribute(Qt.WA_NoSystemBackground)
        self.data_label.setAttribute(Qt.WA_TransparentForMouseEvents)

        self.l = QVBoxLayout(self.parent())
        self.l.addWidget(self.title_label)
        self.l.addWidget(self.data_label)

        self.setLayout(self.l)

当我尝试像这样放置 title_data 重叠的 DoublePushButton 时:

class DataDisplay(QWidget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.sub_widget = DoublePushButton()
        self.sub_widget.clicked.connect(self.update_data_choice)

        self.xxx = TitleData(parent=self.sub_widget)

        self.l = QVBoxLayout()
        self.l.addWidget(self.sub_widget)
        self.l.setContentsMargins(0,0,0,0)
        self.l.setSpacing(0)
        self.setLayout(self.l)

    def update_data_choice(self, value):
        print(value)

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    window = QMainWindow()
    window.setCentralWidget(DataDisplay())
    window.show()
    sys.exit(app.exec_())

我收到这个错误: QLayout:正在尝试将 QLayout“”添加到已经有布局的 DoublePushButton“”。

并且标签保持不可见。

当我尝试在 DoublePushButton class 的 init 末尾使用此代码(代替 self.setLayout( self.button_layout)):

self.f = QFrame(self.parent())
self.f.setLayout(self.button_layout)

出现标签,但没有按钮...

那么,您知道让我的 TitleData class 与 DoublePushButton 重叠的方法吗?

谢谢你的时间,对不起我的英语。

主要问题是一个小部件只能有一个布局管理器,如果已经存在一个布局,则无法设置另一个布局。它也应该 而不是 由子部件负责设置父部件的布局。

另一个重要的方面是盒子布局不允许重叠小部件,因此唯一的解决方案是使用网格布局并根据重叠小部件的放置方式指定跨度。

考虑到以上情况,有两种可能性:在包含按钮的小部件上使用网格布局并在它们之上添加标签,或者反过来。在下面的示例中,我正在做后者。请注意,在这种情况下,带有标签的小部件 不是 子项,而是父项。

class TitleData(QWidget):
    def __init__(self,title="Title", data="Data", buttons=None, **kwargs):
        super().__init__(**kwargs)
        self.title = title
        self.data = data

        self.title_label = QLabel(self.title)
        self.data_label = QLabel(self.data)
        for label in self.title_label, self.data_label:
            label.setAttribute(Qt.WA_NoSystemBackground)
            label.setAttribute(Qt.WA_TransparentForMouseEvents)
            label.setAlignment(Qt.AlignHCenter)

        self.l = QGridLayout(self)
        self.l.addWidget(self.title_label, 0, 0, alignment=Qt.AlignTop)
        self.l.addWidget(self.data_label, 1, 0)
        self.l.setRowStretch(1, 1)
        if buttons:
            self.l.addWidget(buttons, 0, 0, 2, 1)
            buttons.lower()


class DataDisplay(QWidget):
    def __init__(self, **kwargs):
        # ...
        self.xxx = TitleData(buttons=self.sub_widget)

        self.l = QVBoxLayout()
        self.l.addWidget(self.xxx)
        # ...

请注意,按钮小部件已降低,以确保它位于标签后面。

请注意,从用户体验的角度来看,这种方法不是很好,至少对于当前的实现而言:按钮可以保持焦点矩形,即使它们是平面的,这可能会使标签难以阅读(想象有垂直线的字母或可能变成不同字母 带有 垂直线的字母,例如“o”变成“d”或“b”)。为此创建两个 classes 也是不必要的,因为你可以用一个简单的 class 做同样的事情,你最终可以使用样式表来提高可读性:

class TitleDataButton(QWidget):
    clicked = pyqtSignal(int)
    def __init__(self, title='Title', data='Data', buttons=True, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title = title
        self.data = data

        self.title_label = QLabel(self.title)
        self.data_label = QLabel(self.data)
        for label in self.title_label, self.data_label:
            label.setAttribute(Qt.WA_NoSystemBackground)
            label.setAttribute(Qt.WA_TransparentForMouseEvents)
            label.setAlignment(Qt.AlignHCenter)

        layout = QGridLayout(self)
        layout.addWidget(self.title_label, 0, 0, 1, 2, alignment=Qt.AlignTop)
        layout.addWidget(self.data_label, 1, 0, 1, 2)
        layout.setRowStretch(1, 1)

        if buttons:
            self.right_button = QPushButton()
            self.right_button.pressed.connect(self.rightF)
            self.left_button = QPushButton()
            self.left_button.pressed.connect(self.leftF)

            for c, button in enumerate((self.left_button, self.right_button)):
                layout.addWidget(button, 0, c, 2, 1)
                button.setMinimumSize(30,120)
                button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
                button.lower()
                button.setFocusPolicy(Qt.NoFocus)

                button.setStyleSheet('''
                    QPushButton {
                        border: 1px solid transparent;
                        border-radius: 2px;
                    }
                    QPushButton:hover {
                        border-color: darkGray;
                        background: lightGray;
                    }
                    QPushButton:pressed {
                        background: darkGray;
                    }
                ''')

    def rightF(self):
        self.clicked.emit(1)

    def leftF(self):
        self.clicked.emit(-1)

如上,按钮被降低,以便它们位于标签下方。