Как убрать черное пространство при удалении виджетов в PyQt5

У меня есть код для создания графического интерфейса с PyQt5, который позволяет пользователю создавать несколько кнопок (QPushButton) на основе записи (QLineEdit) и удалять эти кнопки при нажатии кнопки X (deleteLater()).

Моя проблема в том, что при удалении некоторых из этих кнопок нажатием соответствующей кнопки X остается небольшое пустое пространство там, где изначально были кнопки, и поэтому мне интересно, как удалить эти пробелы?

Изображение пустых мест

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

person Marc    schedule 10.02.2021    source источник


Ответы (1)


Удаление дочерних элементов не удаляет контейнер, поэтому вы видите пустой виджет Labels с интервалом его макета contentsMargins().

Простым решением может быть прямое подключение кнопки к ее собственному deleteLeter(), что автоматически удалит ее дочерние элементы:

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

Лучшим решением было бы подключить сигнал к родительскому и позволить ему делать все необходимое более чистым способом, так как вам может понадобиться сохранить отслеживать существующие виджеты (например, чтобы удалить их из списка существующих ярлыков):

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

Очевидно, что в этом случае больше не нужно подключать сигнал clicked в initUi класса Label.

Обратите внимание, что layout() – это существующий (и динамический) любого QWidget, поэтому его не следует перезаписывать.

person musicamante    schedule 10.02.2021
comment
Большое спасибо! - person Marc; 10.02.2021