python-3.x - 隐藏小部件时如何阻止 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_())
解决方案
解释:
许多替换 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()
推荐阅读
- javascript - 有没有办法让 electron-js 桌面应用分发包体积更小?
- python - 有没有办法先输出一个句子然后在 tkinter 中退出
- php - 编译(几乎)所有代理标头以检索真实用户 ip?
- html - 弯曲内侧的横向距离
- javascript - 如何在javascript中制作2个小数位
- java - Neo4j“执行架构修改后尝试执行写入查询”错误
- react-native - React Native 中的 Sleep Graph UI
- ffmpeg - 使用ffmpeg将pngs合并为mp4时,透明背景意外变绿
- settings - 如何在暗模式下更改偏好颜色?
- python - 如何打印包含列名的数据框的每一行?