PyQt5 QMainWindow用setCentralWidget切换界面重复操作崩溃如何解决
崩溃原因
QMainWindow的setCentralWidget()方法存在默认销毁逻辑:当你传入新的中央控件时,Qt会自动释放原本挂载在中央部件位置的旧控件对象。
你代码中screen2、screen3对象仅在main()函数中初始化了一次:第一次切换到screen2后再切换到screen3时,screen2就被Qt自动销毁了,后续再调用setCentralWidget(self._screen2)时访问的是已被回收的无效内存,触发野指针错误导致程序崩溃。
修复方案
推荐使用Qt原生的QStackedWidget堆叠组件实现多页面切换,该组件会保存所有已添加的页面实例,仅通过索引切换显示,不会自动销毁页面,稳定性更高。
修复后完整可运行代码
import sys from PyQt5.QtWidgets import ( QWidget, QMenuBar, QMenu, QApplication, QAction, QMainWindow, QVBoxLayout, QLabel, QStackedWidget ) class mainwindow(QMainWindow): def __init__(self, screen2, screen3): super().__init__() self._screen2 = screen2 self._screen3 = screen3 # 初始化堆叠控件,保存所有页面 self.stacked_widget = QStackedWidget() self.tela() self.acoes() self.sinais() def tela(self): # 初始化screen1 screen1 = QWidget() layout = QVBoxLayout() label = QLabel('screen 1') layout.addWidget(label) screen1.setLayout(layout) # 所有页面添加到堆叠控件 self.stacked_widget.addWidget(screen1) # index 0 self.stacked_widget.addWidget(self._screen2) # index 1 self.stacked_widget.addWidget(self._screen3) # index 2 # 堆叠控件设为中央部件,永远不会被替换 self.setCentralWidget(self.stacked_widget) def acoes(self): self.action_screen1 = QAction('screen 1') self.action_screen2 = QAction('screen 2') self.action_screen3 = QAction('screen 3') menu_opt = QMenuBar(self) self.setMenuBar(menu_opt) menu = QMenu("avaliable screens", self) menu_opt.addMenu(menu) screen_menu = menu.addMenu("screens") screen_menu.addAction(self.action_screen1) screen_menu.addAction(self.action_screen2) screen_menu.addAction(self.action_screen3) def sinais(self): # 切换仅修改堆叠控件的显示索引 self.action_screen1.triggered.connect(lambda: self.stacked_widget.setCurrentIndex(0)) self.action_screen2.triggered.connect(lambda: self.stacked_widget.setCurrentIndex(1)) self.action_screen3.triggered.connect(lambda: self.stacked_widget.setCurrentIndex(2)) class widget1(QWidget): def __init__(self): super().__init__() self.screen() def screen(self): layout = QVBoxLayout() label = QLabel('screen 2') layout.addWidget(label) self.setLayout(layout) class widget2(QWidget): def __init__(self): super().__init__() self.screen() def screen(self): layout = QVBoxLayout() label = QLabel('screen 3') layout.addWidget(label) self.setLayout(layout) def main(): applic = QApplication(sys.argv) screen2 = widget1() screen3 = widget2() app = mainwindow(screen2, screen3) app.show() sys.exit(applic.exec_()) if __name__ == '__main__': main()
其他可选修复方式
如果你不想修改现有结构,也可以在每次调用setCentralWidget()前,手动将旧的中央控件的父对象设为None,避免Qt自动销毁:
def tela2(self): # 先把旧控件取出来,设为无父对象,避免被销毁 old_widget = self.centralWidget() if old_widget: old_widget.setParent(None) self.setCentralWidget(self._screen2) def tela3(self): old_widget = self.centralWidget() if old_widget: old_widget.setParent(None) self.setCentralWidget(self._screen3)
该方式不需要新增依赖组件,但需要注意自行管理所有页面的生命周期,避免内存泄漏。
内容的提问来源于stack exchange,提问作者zeca_abc
相关产品推荐
相关产品推荐

