Dynamically add and remove QWidgets to QLayout with PyQt5

2021/03/13 categories:PyQt5| tags:PyQt5|Python|

I tried dynamically adding and removing QWidgets to QLayout in PyQt5.

Add QWidget to QLayout

insertWidgetを使用して、QPushButtonがクリックされたときにQVBoxLayoutにQWidgetを追加するというプログラムを作成しました。QVBoxLayoutにはあらかじめQSpacerItemを追加しておいて、QSpacerItemの1個前のインデックスに挿入する形にしました。

For example, if you add one QGroupBox

QVBoxLayout
├QGroupBox
└QSpacerItem

With the above configuration, when the second one is added

QVBoxLayout
├QGroupBox
├QGroupBox
└QSpacerItem

Make the process so that it has the above configuration. The code for the addition is below. Layout.count () gets the number of items in the layout and inserts the QGroupBox one before that number, that is, before the QSpacerItem.

def addGroupBox(self):
    count = self.scrollAreaWidgetLayout.count() - 1
    groupBox = QtWidgets.QGroupBox('GroupBox ' + str(count), self.scrollAreaWidget)
    self.scrollAreaWidgetLayout.insertWidget(count, groupBox)

Remove QWidget from QLayout

Looking at the reference, it seems that the following three functions can be used for the deletion process, so I tried each one. As a result, I was able to delete QWidget normally by using deleteLater.

Delete with widget.deleteLater ()

[deleteLater] (https://doc.qt.io/qt-5/qobject.html#deleteLater) is a function that schedules objects to be deleted when the control returns to the event loop. It seems to be a process. As shown in the code below, if there are more than one item in the layout, the previous item from the last item will be deleted with deleteLater. You have now confirmed that the QWidget is dynamically deleted.

def deleteLaterGroupBox(self):
    count = self.scrollAreaWidgetLayout.count()
    if count == 1:
        return
    item = self.scrollAreaWidgetLayout.itemAt(count - 2)
    widget = item.widget()
    widget.deleteLater()

Remove with removeItem(self, QLayoutItem)

Looking at the reference, I think that [removeItem] (https://doc.qt.io/qt-5/qlayout.html#removeItem) can also be used to remove QWidget. However, when I execute the code below, it seems that the QWidget is deleted, but the deleted QWidget and its child widgets are still drawn in the window, and the child widgets can also be operated. It has become a state.

def removeItemGroupBox(self):
    count = self.scrollAreaWidgetLayout.count()
    if count == 1:
        return
    item = self.scrollAreaWidgetLayout.itemAt(count - 2)
    self.scrollAreaWidgetLayout.removeItem(item)

Remove with removeWidget(self, QWidget w)

[removeWidget] (https://doc.qt.io/qt-5/qlayout.html#removeWidget) seems to be usable, so I tried to execute the following code like removeItem, but it works the same as removeItem. After that, both the drawing and the child widgets remained. The reference says to give the widget the proper geometry after calling removeItem, or return the widget to the layout, but explicitly hide it if needed, so remove the QWidget dynamically. It may not be intended for such uses.

def removeWidgetGroupBox(self):
    count = self.scrollAreaWidgetLayout.count()
    if count == 1:
        return
    item = self.scrollAreaWidgetLayout.itemAt(count - 2)
    widget = item.widget()
    self.scrollAreaWidgetLayout.removeWidget(widget)

Source code

import sys
from PyQt5 import QtWidgets, QtCore

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.centralwidget = QtWidgets.QWidget(self)
        self.centralwidgetLayout = QtWidgets.QVBoxLayout(self.centralwidget)

        self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
        self.scrollArea.setWidgetResizable(True)
        self.scrollAreaWidget = QtWidgets.QWidget()
        self.scrollAreaWidget.setGeometry(QtCore.QRect(0, 0, 780, 539))
        self.scrollAreaWidgetLayout = QtWidgets.QVBoxLayout(self.scrollAreaWidget)
        self.scrollAreaWidgetLayout.addItem(QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding))
        self.scrollArea.setWidget(self.scrollAreaWidget)

        self.buttonWidget = QtWidgets.QWidget(self.centralwidget)
        self.buttonAddGroupBox = QtWidgets.QPushButton('Add GroupBox', self.buttonWidget)
        self.buttonDeleteLaterGroupBox = QtWidgets.QPushButton('DeleteLater GroupBox', self.buttonWidget)
        self.buttonRemoveItemGroupBox = QtWidgets.QPushButton('RemoveItem GroupBox', self.buttonWidget)
        self.buttonRemoveWidgetGroupBox = QtWidgets.QPushButton('RemoveWidget GroupBox', self.buttonWidget)
        self.buttonLayout = QtWidgets.QGridLayout(self.buttonWidget)
        self.buttonLayout.addWidget(self.buttonAddGroupBox,          0, 0, 1, 1)
        self.buttonLayout.addWidget(self.buttonDeleteLaterGroupBox,  0, 1, 1, 1)
        self.buttonLayout.addWidget(self.buttonRemoveItemGroupBox,   1, 0, 1, 1)
        self.buttonLayout.addWidget(self.buttonRemoveWidgetGroupBox, 1, 1, 1, 1)
        
        self.centralwidgetLayout.addWidget(self.buttonWidget)
        self.centralwidgetLayout.addWidget(self.scrollArea)
        self.setCentralWidget(self.centralwidget)

        self.buttonAddGroupBox.clicked.connect(self.addGroupBox)
        self.buttonDeleteLaterGroupBox.clicked.connect(self.deleteLaterGroupBox)
        self.buttonRemoveItemGroupBox.clicked.connect(self.removeItemGroupBox)
        self.buttonRemoveWidgetGroupBox.clicked.connect(self.removeWidgetGroupBox)

    def addGroupBox(self):
        count = self.scrollAreaWidgetLayout.count() - 1
        groupBox = QtWidgets.QGroupBox('GroupBox ' + str(count), self.scrollAreaWidget)
        self.scrollAreaWidgetLayout.insertWidget(count, groupBox)

        comboBox = QtWidgets.QComboBox(groupBox)
        comboBox.addItems(['val1', 'val2', 'val3'])

        gridLayout = QtWidgets.QGridLayout(groupBox)
        gridLayout.addWidget(QtWidgets.QLabel('Label ' + str(count), groupBox),       0, 0, 1, 1)
        gridLayout.addWidget(QtWidgets.QLineEdit('LineEdit ' + str(count), groupBox), 0, 1, 1, 1)
        gridLayout.addWidget(comboBox,                                                1, 0, 1, 1)
        gridLayout.addWidget(QtWidgets.QSlider(QtCore.Qt.Horizontal, groupBox),       1, 1, 1, 1)

    def deleteLaterGroupBox(self):
        count = self.scrollAreaWidgetLayout.count()
        if count == 1:
            return
        item = self.scrollAreaWidgetLayout.itemAt(count - 2)
        widget = item.widget()
        widget.deleteLater()

    def removeItemGroupBox(self):
        count = self.scrollAreaWidgetLayout.count()
        if count == 1:
            return
        item = self.scrollAreaWidgetLayout.itemAt(count - 2)
        self.scrollAreaWidgetLayout.removeItem(item)

    def removeWidgetGroupBox(self):
        count = self.scrollAreaWidgetLayout.count()
        if count == 1:
            return
        item = self.scrollAreaWidgetLayout.itemAt(count - 2)
        widget = item.widget()
        self.scrollAreaWidgetLayout.removeWidget(widget)

def main():
    app = QtWidgets.QApplication(sys.argv)
    mainwindow = MainWindow()
    mainwindow.show()
    app.exec()

if __name__ == '__main__':
    main()

Share post

Related Posts

コメント