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