Reputation: 21
I'm using Python3.9 and PyQt5 and I've got a GUI with lots of widgets. I've got a selection screen of sorts, and when the user clicks on one of the options, I hide all the selection widgets and show all of the widgets for the actual app. That works fine.
However, I also have a reset button, which is supposed to hide all the widgets for the app and show the widgets for the selection, but when I click the reset button, I get RuntimeError: wrapped C/C++ object of type QPushButton has been deleted
.
Clearly, my button objects are getting deleted, probably by some garbage collection, so when I try to show them again, I get this error. The app widgets are hidden properly, but then I just have a blank GUI.
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMainWindow, QApplication, QHBoxLayout, QWidget
class GUI(QMainWindow):
def __init__(self):
super().__init__()
self.selection_button_1 = QtWidgets.QPushButton('Selection Button 1', self)
self.selection_button_1.clicked.connect(self.select)
self.selection_button_2 = QtWidgets.QPushButton('Selection Button 2', self)
self.selection_button_2.clicked.connect(self.select)
# When the user clicks either selection button, some stuff
# is configured and self.show_app() is called
self.app_button_1 = QtWidgets.QPushButton('App Button 1', self)
self.app_button_2 = QtWidgets.QPushButton('App Button 2', self)
self.reset_button = QtWidgets.QPushButton('Reset', self)
self.reset_button.clicked.connect(self.reset)
self.selection_hbox = QHBoxLayout()
self.app_hbox = QHBoxLayout()
self.selection_central_widget = QWidget()
self.app_central_widget = QWidget()
self.arrange_widgets()
self.show_selection()
def arrange_widgets(self) -> None:
self.selection_hbox.addWidget(self.selection_button_1)
self.selection_hbox.addWidget(self.selection_button_2)
self.selection_central_widget.setLayout(self.selection_hbox)
self.app_hbox.addWidget(self.app_button_1)
self.app_hbox.addWidget(self.app_button_2)
self.app_hbox.addWidget(self.reset_button)
self.app_central_widget.setLayout(self.app_hbox)
def show_selection(self) -> None:
self.app_button_1.hide()
self.app_button_2.hide()
self.selection_button_1.show()
self.selection_button_2.show()
self.setCentralWidget(self.selection_central_widget)
def show_app(self) -> None:
self.selection_button_1.hide()
self.selection_button_2.hide()
self.app_button_1.show()
self.app_button_2.show()
self.setCentralWidget(self.app_central_widget)
def select(self) -> None:
# Configure some other stuff
self.show_app()
def reset(self) -> None:
# Reset some other stuff
self.show_selection()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = GUI()
window.show()
sys.exit(app.exec_())
Upvotes: 0
Views: 393
Reputation: 244282
Many methods that replace QObjects have the policy that if the object to be replaced has the container as parent then it will be deleted, something like:
def replace_foo(self, new_foo_object):
if self.foo_object.parent() is self:
self.foo_object.deleteLater()
self.foo_object = new_foo_object
And that's what happens with setCentralWidget()
.
A possible solution is that before replacing the widget is to set the parent of the current centralWidget to be None:
def show_selection(self) -> None:
self.app_button_1.hide()
self.app_button_2.hide()
self.selection_button_1.show()
self.selection_button_2.show()
self.app_central_widget.setParent(None)
self.setCentralWidget(self.selection_central_widget)
def show_app(self) -> None:
self.selection_button_1.hide()
self.selection_button_2.hide()
self.app_button_1.show()
self.app_button_2.show()
self.selection_central_widget.setParent(None)
self.setCentralWidget(self.app_central_widget)
But a better solution that avoids having a lot of code that hides or shows widgets is to use a QStackedWidget:
class GUI(QMainWindow):
def __init__(self):
super().__init__()
self.stacked_widget = QStackedWidget()
self.setCentralWidget(self.stacked_widget)
self.widget_selection = self.create_selection()
self.widget_app = self.create_app()
def create_selection(self):
self.selection_button_1 = QtWidgets.QPushButton("Selection Button 1")
self.selection_button_1.clicked.connect(self.select)
self.selection_button_2 = QtWidgets.QPushButton("Selection Button 2")
self.selection_button_2.clicked.connect(self.select)
container = QWidget()
lay = QHBoxLayout(container)
lay.addWidget(self.selection_button_1)
lay.addWidget(self.selection_button_2)
self.stacked_widget.addWidget(container)
return container
def create_app(self):
self.app_button_1 = QtWidgets.QPushButton("App Button 1")
self.app_button_2 = QtWidgets.QPushButton("App Button 2")
self.reset_button = QtWidgets.QPushButton("Reset")
self.reset_button.clicked.connect(self.reset)
container = QWidget()
lay = QHBoxLayout(container)
lay.addWidget(self.app_button_1)
lay.addWidget(self.app_button_2)
lay.addWidget(self.reset_button)
self.stacked_widget.addWidget(container)
return container
def show_selection(self) -> None:
self.stacked_widget.setCurrentWidget(self.widget_selection)
def show_app(self) -> None:
self.stacked_widget.setCurrentWidget(self.widget_app)
def select(self) -> None:
# Configure some other stuff
self.show_app()
def reset(self) -> None:
# Reset some other stuff
self.show_selection()
Upvotes: 1