Reputation: 1759
class window25(QtWidgets.QMainWindow):
def __init__(self):
try:
super(window25,self).__init__()
self.lineedit=QtWidgets.QLineEdit()
self.checkbox=QtWidgets.QCheckBox()
self.optBox=QtWidgets.QRadioButton()
self.btn=QtWidgets.QPushButton("press me")
self.guichange()
self.btn.clicked.connect(self.guichange)
self.show()
except Exception as E:
print(E)
def guichange(self):
try:
wid=QtWidgets.QWidget()
# self.setCentralWidget()
myLayout=QtWidgets.QVBoxLayout()
myLayout.addWidget(self.btn)
myLayout.addWidget(random.choice([self.lineedit,self.checkbox,self.optBox]))
wid.setLayout(myLayout)
self.setCentralWidget(wid)
except Exception as E:
print(E)
app=QtWidgets.QApplication([])
ex=window25()
sys.exit(app.exec_())
I was trying to do the experiment.Although I know about QStackedWidget to change windows but I tried something else that is pressing a button randomly changes the mainWindow Central widget.After a couple of success(i.e. pressing button 3,4 times work correctly") but after that I got an error
wrapped c/c++ object of type QCheckBox has been deleted
wrapped c/c++ object of type QLineEdit has been deleted
i am unable to understand which statement caused this error and why or where I am wrong
Upvotes: 3
Views: 6539
Reputation: 244202
This is caused by the implementation and philosophy of Qt, reviewing the documentation observed:
[...]
QObjects organize themselves in object trees. When you create a QObject with another object as parent, the object will automatically add itself to the parent's children() list. The parent takes ownership of the object; i.e., it will automatically delete its children in its destructor. You can look for an object by name and optionally type using findChild() or findChildren().
[...]
As he says if the parent destroys the children too, and since all widgets have QObject
as their predecessor, they also comply with those rules.
But why are some widget destroyed?
Each time you use setCentralWidget()
the object that was previously the central widget is deleted.
What are the children of the CentralWidget()?
when the widgets are attached to a layout, these widgets are set as the parent to the widget that uses that layout.
wid = QtWidgets.QWidget()
myLayout.addWidget(tmp)
wid.setLayout(myLayout)
self.setCentralWidget(wid)
Why is not QPushButton eliminated?
What happens with QPushButton
and sometimes with the other widgets is that before using setCentralWidget()
, that is deleting the parent, it is changed from the parent:
myLayout=QtWidgets.QVBoxLayout() # some parent
myLayout.addWidget(self.btn) # a new parent is established
[...]
self.setCentralWidget(wid) # elimination of the old parent
Finally I will explain a random example using the following code:
class window25(QtWidgets.QMainWindow):
counter = 0
def __init__(self):
try:
super(window25,self).__init__()
self.lineedit=QtWidgets.QLineEdit()
self.lineedit.setObjectName("QLineEdit")
self.checkbox=QtWidgets.QCheckBox()
self.checkbox.setObjectName("QCheckBox")
self.optBox=QtWidgets.QRadioButton()
self.optBox.setObjectName("QRadioButton")
self.btn=QtWidgets.QPushButton("press me")
self.guichange()
self.btn.clicked.connect(self.guichange)
self.show()
except Exception as E:
print(E)
def guichange(self):
try:
print("\ncall guichange")
wid = QtWidgets.QWidget()
wid.setObjectName("wid-{}".format(self.counter))
myLayout=QtWidgets.QVBoxLayout()
myLayout.addWidget(self.btn)
tmp = random.choice([self.lineedit,self.checkbox,self.optBox])
myLayout.addWidget(tmp)
wid.setLayout(myLayout)
wid.destroyed.connect(lambda obj: print("delete: ",obj.objectName()))
tmp.destroyed.connect(lambda obj: print("delete: ",obj.objectName()))
self.setCentralWidget(wid)
print("add: {} parent: {}".format(tmp.objectName(),tmp.parent().objectName()))
self.counter += 1
except Exception as E:
print(E)
Explanation:
call guichange
QLineEdit # tmp is QLineEdit
add: QLineEdit parent: wid-0 # set to wid-0 as the parent of QLineEdit
call guichange
QLineEdit # tmp is QLineEdit
add: QLineEdit parent: wid-1 # set to wid-1 as new parent of QLineEdit
delete: wid-0 # delete old parent
call guichange
QRadioButton # tmp is QRadioButton
add: QRadioButton parent: wid-2 # set to wid-2 as the parent of QRadioButton
delete: wid-1 # delete old parent
delete: QLineEdit # message printed by the lambda function
delete: QLineEdit # indicating that QLineEdit has been deleted
call guichange
wrapped C/C++ object of type QLineEdit has been deleted # you want to use QLineEdit
# but it had been removed
# previously causing
# that error message
If you want to exchange widgets the appropriate option is QStackedWidget
as I show below:
class window25(QtWidgets.QMainWindow):
def __init__(self):
super(window25, self).__init__()
self.lineedit = QtWidgets.QLineEdit()
self.checkbox = QtWidgets.QCheckBox()
self.optBox = QtWidgets.QRadioButton()
self.btn = QtWidgets.QPushButton("press me")
wid = QtWidgets.QWidget()
myLayout=QtWidgets.QVBoxLayout()
self.stacked = QtWidgets.QStackedWidget()
self.stacked.addWidget(self.lineedit)
self.stacked.addWidget(self.checkbox)
self.stacked.addWidget(self.optBox)
wid.setLayout(myLayout)
myLayout.addWidget(self.btn)
myLayout.addWidget(self.stacked)
self.setCentralWidget(wid)
self.guichange()
self.btn.clicked.connect(self.guichange)
def guichange(self):
widget = random.choice([self.lineedit,self.checkbox,self.optBox])
self.stacked.setCurrentWidget(widget)
if __name__ == '__main__':
app=QtWidgets.QApplication(sys.argv)
ex=window25()
ex.show()
sys.exit(app.exec_())
Upvotes: 6