PySide6子类方法中调用父类方法失败问题求助
Qt子类调用父类方法异常问题
问题描述
在子类Widget中调用父类MainWindow的do_something方法时,仅在__init__方法中调用成功,按钮点击触发的clickedInitialize方法中调用失败,报错信息如下:
AttributeError: 'PySide6.QtWidgets.QStackedWidget' object has no attribute 'do_something'
尝试代码
class MainWindow(QMainWindow): ws = websocket.WebSocket() threadpool = QThreadPool() pippo = "pippo" def __init__(self): super().__init__() tabs = QTabWidget() tabs.setTabPosition(QTabWidget.North) tabs.setMovable(False) tabs.setDocumentMode(True) self.voices = Widget("Voices", self) tabs.addTab(self.voices, "Voices") self.setCentralWidget(tabs) def do_something(self): print ('doing something!') class Widget(QWidget): def __init__(self, name, parent=None): super().__init__(parent) self.ui = Ui_Widget() self.ui.setupUi(self) self.name=name self.lan = str(config_ini("language")) self.username = str(config_ini("user")) print(self.parent().pippo) # 此处调用成功 self.parent().do_something() # 此处调用成功 self.ui.InitializeButton.clicked.connect(self.clickedInitialize) # 点击Initialize按钮触发 def clickedInitialize(self): self.parent().do_something() # 此处调用失败
错误栈信息
Traceback (most recent call last): File "C:\Users\IENOGIUS\Documents\Cefla\VoiceCommands\VoiceChecker\voicechecker.py", line 294, in clickedInitialize self.parent().do_something() AttributeError: 'PySide6.QtWidgets.QStackedWidget' object has no attribute 'do_something'
问题原因
Qt的父对象层级发生了变化:
- 在
Widget的__init__阶段,传入的父对象是MainWindow,此时self.parent()返回的是MainWindow实例,所以调用方法成功。 - 当把
Widget添加到QTabWidget的tab后,QTabWidget内部会自动用QStackedWidget管理所有tab页面,此时Widget的父对象被替换成了这个QStackedWidget,而非原来的MainWindow,因此调用self.parent()拿到的是没有do_something方法的QStackedWidget实例,触发报错。
解决方案
方案1:保存原始父对象
在Widget初始化时,将传入的MainWindow实例保存为自己的成员变量,后续直接通过该变量调用方法:
class Widget(QWidget): def __init__(self, name, parent=None): super().__init__(parent) self.main_window = parent # 保存原始的MainWindow实例 self.ui = Ui_Widget() self.ui.setupUi(self) self.name=name self.lan = str(config_ini("language")) self.username = str(config_ini("user")) print(self.main_window.pippo) self.main_window.do_something() self.ui.InitializeButton.clicked.connect(self.clickedInitialize) def clickedInitialize(self): self.main_window.do_something() # 直接调用保存的MainWindow实例方法
方案2:获取顶层窗口对象
利用Qt的QWidget.window()方法,该方法会返回当前控件所属的顶层窗口(即MainWindow),不受中间父对象变化影响:
def clickedInitialize(self): self.window().do_something()
方案3:使用信号槽机制(推荐,符合Qt设计理念)
通过信号解耦子类与父类的依赖,避免直接调用父类方法:
class Widget(QWidget): initialize_triggered = Signal() # 定义自定义信号 def __init__(self, name, parent=None): super().__init__(parent) self.ui = Ui_Widget() self.ui.setupUi(self) self.name=name self.lan = str(config_ini("language")) self.username = str(config_ini("user")) print(self.parent().pippo) self.parent().do_something() self.ui.InitializeButton.clicked.connect(self.clickedInitialize) def clickedInitialize(self): self.initialize_triggered.emit() # 点击按钮时发射信号 # 在MainWindow中连接信号与槽 class MainWindow(QMainWindow): # ... 其他代码 ... def __init__(self): super().__init__() tabs = QTabWidget() # ... 其他代码 ... self.voices = Widget("Voices", self) self.voices.initialize_triggered.connect(self.do_something) # 绑定信号到目标方法 tabs.addTab(self.voices, "Voices") self.setCentralWidget(tabs)
内容的提问来源于stack exchange,提问作者Hawke
相关产品推荐
相关产品推荐

