python-3.xpyqt5qpushbuttonqgroupbox

How to remove all widgets within a QGroupBox in PyQt5?


In my program there is a QGroupBox displayed that has many QPushButton's within it. During the execution of the program, the user can click a button outside of the QGroupBox and all of the buttons within it will be removed or hidden. Problem is that I can't seem to find a way to do this to the buttons directly or by clearing the QGroupBox.

I have already tried deleteLater on the buttons but that didn't work. I then tried clearing the layout of the QGroupBox but that didn't work either. Here is some code I just wrote up that has a the same problem:

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import random

class UI_Dialog(object):
    def addButtons(self,looping):

        # Code to remove the previous QPushButton's goes here.

        placement = -100
        for i in range(looping):
            currentName = 'btn' + str(i)
            placement = placement + 110
            self.btnB = QtWidgets.QPushButton(self.groupBox)         
            self.btnB.setGeometry(QtCore.QRect(10+placement, 30+placement, 100, 100))
            self.btnB.show()
            self.btnB.setObjectName(currentName)
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(1300, 800)
        self.btnA = QtWidgets.QPushButton(Dialog)
        self.btnA.setGeometry(QtCore.QRect(10, 80, 101, 131))
        self.btnA.setObjectName("btn1")
        self.btnA.clicked.connect(self.pushed)
        self.formLayout = QtWidgets.QFormLayout()
        self.groupBox = QtWidgets.QGroupBox("Results")
        self.groupBox.setLayout(self.formLayout)
        self.resultScrollArea = QtWidgets.QScrollArea(Dialog)
        self.resultScrollArea.setWidget(self.groupBox)
        self.resultScrollArea.setGeometry(QtCore.QRect(20, 220, 1011, 531))
        self.resultScrollArea.setWidgetResizable(True)
        self.resultScrollArea.setObjectName("resultScrollArea")     
        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def pushed(self):
        unkownLength = random.randint(1,20)
        self.addButtons(unkownLength)
    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Example Program"))
        self.btnA.setText(_translate("Dialog", "Push Button"))

app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = UI_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())

There are things here that probably don't make sense like the window size, the multiple function calls or it being a Dialog window instead of a QMainWindow. However, in the context of the actual program they do make sense so just ignore that, I know it's inefficient. Also the whole point of the unkownLength variable is to emulate that in the actual program, the number of buttons generated will be determined by user input. The buttons must also not be there at the start so that is why they're created with a button click. When the button is clicked again it should remove or hide all the buttons it created before. Any ideas?


Solution

  • Taking advantage that the buttons are children of the QGroupBox we can get the buttons using findChildren() to use deleteLater():

    from PyQt5 import QtCore, QtGui, QtWidgets
    import sys
    import random
    
    
    class UI_Dialog(object):
        def setupUi(self, Dialog):
            Dialog.setObjectName("Dialog")
            Dialog.resize(1300, 800)
            self.btnA = QtWidgets.QPushButton(Dialog)
            self.btnA.setGeometry(QtCore.QRect(10, 80, 101, 131))
            self.btnA.setObjectName("btn1")
    
            self.formLayout = QtWidgets.QFormLayout()
            self.groupBox = QtWidgets.QGroupBox("Results")
            self.groupBox.setLayout(self.formLayout)
            self.resultScrollArea = QtWidgets.QScrollArea(Dialog)
            self.resultScrollArea.setWidget(self.groupBox)
            self.resultScrollArea.setGeometry(QtCore.QRect(20, 220, 1011, 531))
            self.resultScrollArea.setWidgetResizable(True)
            self.resultScrollArea.setObjectName("resultScrollArea")
            self.retranslateUi(Dialog)
            QtCore.QMetaObject.connectSlotsByName(Dialog)
    
        def retranslateUi(self, Dialog):
            _translate = QtCore.QCoreApplication.translate
            Dialog.setWindowTitle(_translate("Dialog", "Example Program"))
            self.btnA.setText(_translate("Dialog", "Push Button"))
    
    
    class Dialog(QtWidgets.QDialog, UI_Dialog):
        def __init__(self, parent=None):
            super(Dialog, self).__init__(parent)
            self.setupUi(self)
            self.btnA.clicked.connect(self.pushed)
    
        @QtCore.pyqtSlot()
        def pushed(self):
            unkownLength = random.randint(1, 20)
            self.addButtons(unkownLength)
    
        def addButtons(self, looping):
            for button in self.groupBox.findChildren(QtWidgets.QPushButton):
                button.deleteLater()
            placement = -100
            pos = QtCore.QPoint(20, 40)
            for i in range(looping):
                currentName = "btn" + str(i)
                self.btnB = QtWidgets.QPushButton(
                    self.groupBox, objectName=currentName
                )
                self.btnB.setGeometry(QtCore.QRect(pos, QtCore.QSize(100, 100)))
                pos += QtCore.QPoint(110, 110)
                self.btnB.show()
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        w = Dialog()
        w.show()
        sys.exit(app.exec_())