在PyQt5中删除小部件时如何去除黑色space

How to remove black space when deleting widgets in PyQt5

我有一个使用 PyQt5 生成 GUI 的代码,使用户能够基于条目 (QLineEdit) 创建多个按钮 (QPushButton),并在按下“X”按钮时删除这些按钮 (deleteLater() ).

我的问题是,当通过按下关联的 X 按钮删除其中一些按钮时,这会在按钮最初所在的位置留下一个小空 space,因此我想知道如何删除这些 spaces?

空的图像 spaces

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLineEdit, QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QGroupBox, QScrollArea, QLabel
from PyQt5.QtCore import Qt
import sys


class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.setWindowTitle("My Program")
        self.setGeometry(100, 100, 1500, 1500)
        self.initUI()

    def initUI(self):
        widgets = MainWidgets()
        self.setCentralWidget(widgets)


class MainWidgets(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.grid = QGridLayout()
        self.grid.setColumnStretch(0, 1)
        self.grid.setColumnStretch(1, 1)
        self.grid.setColumnStretch(2, 1)
        self.grid.setColumnStretch(3, 1)
        self.grid.setColumnStretch(4, 1)

        self.groupBox = QGroupBox("Labels")
        self.groupBox.setStyleSheet('''
            QGroupBox::title {
                subcontrol-position: top center;
            }
        ''')

        right_column_layout = QVBoxLayout(self.groupBox)
        scrollArea = QScrollArea()
        scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        scrollArea.setWidgetResizable(True)
        right_column_layout.addWidget(scrollArea)
        scrollArea.setWidget(RightColWidgets())

        self.grid.addWidget(self.groupBox, 0, 5, 1, 5)
        self.setLayout(self.grid)


class RightColWidgets(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.layout = QVBoxLayout(self)

        self.labelEntry = QLineEdit(self)

        self.addLabelButton = QPushButton(self)
        self.addLabelButton.setText("Add Label")
        self.addLabelButton.clicked.connect(self.addNewLabel)

        self.emptyspace = QLabel(self)

        self.layout.addWidget(self.labelEntry, stretch=0)
        self.layout.addWidget(self.addLabelButton, stretch=0)
        self.layout.addWidget(self.emptyspace, stretch=1)

    def addNewLabel(self):
        labelname = self.labelEntry.text()
        newLabelItems = Labels(self, labelname)

        self.layout.insertWidget(2, newLabelItems)


class Labels(QWidget):
    def __init__(self, parent, labelname, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.mylabelname = labelname
        self.initUI()

    def initUI(self):
        self.labelButton = QPushButton(self)
        self.labelButton.setText(str(self.mylabelname))
        self.labelButton.setStyleSheet("""
            QPushButton {border: 1px solid back; background: rgba(103, 186, 181, 0.5); padding-top: 10px; padding-bottom: 10px}
        """)
        self.labelButton.clicked.connect(self.printbutton)
        self.buttonErase = QPushButton(self)
        self.buttonErase.setText("X")
        self.buttonErase.setStyleSheet("""
            QPushButton {border: 1px solid back; padding-right: 5 px; padding-left: 5 px; padding-top: 10px; padding-bottom: 10px}
        """)
        self.buttonErase.clicked.connect(self.erasebutton)

        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.labelButton, stretch=1)
        layout.addWidget(self.buttonErase, stretch=0)

    def printbutton(self):
        print('clicked:', self.labelButton.text())

    def erasebutton(self):
        self.labelButton.deleteLater()
        self.buttonErase.deleteLater()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    # app.setStyle('Fusion')
    window = MyWindow()
    window.showMaximized()
    sys.exit(app.exec_())

删除子项不会删除容器,因此您看到的是空的 Labels 小部件,其布局间距为 contentsMargins()

一个简单的解决方案可能是直接将按钮与它自己的 deleteLeter() 连接起来,这会自动删除它的子项:

        self.buttonErase.clicked.connect(self.deleteLater)

一个更好的解决方案是将信号连接到并让它在中做所有必要的事情cleaner 方式,因为您可能需要跟踪现有的小部件(例如,将它们从当前存在的标签列表中删除):

class RightColWidgets(QWidget):
    # ...

    def addNewLabel(self):
        labelname = self.labelEntry.text()
        newLabelItems = Labels(self, labelname)

        self.layout.insertWidget(2, newLabelItems)
        newLabelItems.buttonErase.clicked.connect(
            lambda: self.deleteLabel(newLabelItems))

    def deleteLabel(self, widget):
        self.layout.removeWidget(widget)
        widget.deleteLater()

显然,在这种情况下,您不再需要在 Label class 的 initUi 中连接 clicked 信号。

请注意,layout() 是任何 QWidget 的现有(和 动态)属性,因此您不应覆盖它。