首页 > 解决方案 > 隐藏小部件时如何阻止 PyQt5 删除小部件?

问题描述

我正在使用 Python3.9 和 PyQt5,我有一个带有很多小部件的 GUI。我有一个选择屏幕,当用户单击其中一个选项时,我会隐藏所有选择小部件并显示实际应用程序的所有小部件。这很好用。

但是,我还有一个重置按钮,它应该隐藏应用程序的所有小部件并显示选择的小部件,但是当我单击重置按钮时,我得到RuntimeError: wrapped C/C++ object of type QPushButton has been deleted.

显然,我的按钮对象被删除了,可能是由于一些垃圾收集,所以当我再次尝试显示它们时,我得到了这个错误。应用程序小部件被正确隐藏,但我只有一个空白的 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_())

标签: python-3.xpyqt5

解决方案


解释:

许多替换 QObjects 的方法的策略是,如果要替换的对象以容器为父对象,则它将被删除,例如:

def replace_foo(self, new_foo_object):
    if self.foo_object.parent() is self:
        self.foo_object.deleteLater()
    self.foo_object = new_foo_object

这就是setCentralWidget().

解决方案

一个可能的解决方案是在替换小部件之前将当前centralWidget的父级设置为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)

但是,避免使用大量隐藏或显示小部件的代码的更好解决方案是使用 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()

推荐阅读