PyQt5でQLayoutへのQWidgetの動的追加と動的削除
2021/03/13 categories:PyQt5| tags:PyQt5|Python|
PyQt5でQLayoutへのQWidgetの動的追加と動的削除を試してみました。
QLayoutにQWidgetを追加する
insertWidgetを使用して、QPushButtonがクリックされたときにQVBoxLayoutにQWidgetを追加するというプログラムを作成しました。QVBoxLayoutにはあらかじめQSpacerItemを追加しておいて、QSpacerItemの1個前のインデックスに挿入する形にしました。
例えば、QGroupBoxを一つ追加した場合
QVBoxLayout
├QGroupBox
└QSpacerItem
という構成となり、2個目を追加した場合
QVBoxLayout
├QGroupBox
├QGroupBox
└QSpacerItem
という構成になるような処理にします。追加に関するコードは以下の通りです。Layout.count()でレイアウト内のアイテムの数を取得して、その数の1個前、つまりQSpacerItemの前にQGroupBoxを挿入しています。
def addGroupBox(self):
count = self.scrollAreaWidgetLayout.count() - 1
groupBox = QtWidgets.QGroupBox('GroupBox ' + str(count), self.scrollAreaWidget)
self.scrollAreaWidgetLayout.insertWidget(count, groupBox)
QLayoutからQWidgetを削除する
リファレンスを見たところ、削除の処理には下記3つの関数が使用できそうなので、それぞれ試してみました。結果的にはdeleteLaterを使用すれば正常にQWidgetの削除を行うことができました。
widget.deleteLater()で削除
deleteLaterは、オブジェクトを削除するようにスケジュールする関数で、コントロールがイベントループに戻るとオブジェクトが削除されるという処理のようです。下記のコードのように、レイアウト内のアイテムが1個より多い場合に最後のアイテムから1個前のアイテムをdeleteLaterで削除するという処理にしました。これでQWidgetを動的に削除されることを確認できました。
def deleteLaterGroupBox(self):
count = self.scrollAreaWidgetLayout.count()
if count == 1:
return
item = self.scrollAreaWidgetLayout.itemAt(count - 2)
widget = item.widget()
widget.deleteLater()
removeItem(self, QLayoutItem)で削除
リファレンスを見るとremoveItemもQWidgetの削除に使用できるのではないかとお思います。しかし下記のコードを実行してみたところ、QWidgetが削除されているような動作はしていますが、ウィンドウには削除したQWidgetとその子ウィジェットが描画されたままになっており、子ウィジェットも操作可能な状態になってしまいました。
def removeItemGroupBox(self):
count = self.scrollAreaWidgetLayout.count()
if count == 1:
return
item = self.scrollAreaWidgetLayout.itemAt(count - 2)
self.scrollAreaWidgetLayout.removeItem(item)
removeWidget(self, QWidget w)で削除
removeWidgetも使えそうなので、removeItemと同じように下記のコードを実行してみましたが、removeItemと同じ動作をして、描画も子ウィジェットも残ったままとなってしまいました。リファレンスには、removeItemを呼出しした後、ウィジェットに適切なジオメトリを与えるか、ウィジェットをレイアウトに戻すが、必要に応じて明示的に非表示にすると描かれているので、QWidgetを動的に削除するような用途を想定していないのかもしれません。
def removeWidgetGroupBox(self):
count = self.scrollAreaWidgetLayout.count()
if count == 1:
return
item = self.scrollAreaWidgetLayout.itemAt(count - 2)
widget = item.widget()
self.scrollAreaWidgetLayout.removeWidget(widget)
ソースコード
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()