Python中PyQt5类间函数调用异常:按钮dropEvent事件调用主类函数时setText失效
嘿,我一眼就看出问题出在哪了——你在dropEvent里错误地创建了一个全新的MainWindow实例,而不是操作当前屏幕上显示的那个主窗口对象!
问题根源
看这行代码:
MainWindow().lbl_setText()
这里的MainWindow()会生成一个完全新的、从未显示过的主窗口实例。你调用它的lbl_setText()时,print("setText")能正常输出是因为它是全局控制台输出,但self.lbl_1.setText("setText")修改的是这个隐藏窗口里的标签,和你正在看的那个窗口完全没关系,所以自然看不到变化。
解决方案
这里给你三种可行的解决方式,从简单到符合Qt设计理念的都有:
方法1:通过父控件直接获取当前主窗口
因为你的Button是在MainWindow内部创建的,它的父对象就是当前显示的MainWindow实例,直接用self.parent()就能拿到它:
修改Button类的dropEvent方法:
def dropEvent(self, event): self.setText(event.mimeData().text()) # 获取当前显示的主窗口实例 main_window = self.parent() main_window.lbl_setText()
方法2:显式传递主窗口引用
如果觉得通过父对象获取不够直观,也可以在创建Button时直接把主窗口实例传进去保存:
先修改Button类:
class Button(QPushButton): def __init__(self, title, parent, main_window): super(Button, self).__init__(title, parent) self.setAcceptDrops(True) self.main_window = main_window # 保存主窗口引用 # dragEnterEvent方法不变 def dropEvent(self, event): self.setText(event.mimeData().text()) self.main_window.lbl_setText()
然后在MainWindow的setup方法里创建按钮时传入自身:
self.btn_quit = Button('Quit', self, self)
方法3:用Qt信号槽机制(最推荐)
这种方式更符合Qt的设计思想,组件之间解耦,不需要直接持有主窗口引用:
给Button类添加一个自定义信号,在drop事件触发时发出信号,主窗口监听这个信号并执行更新标签的操作:
class Button(QPushButton): # 定义一个自定义信号,用来通知主窗口更新标签 update_label_signal = pyqtSignal() def __init__(self, title, parent): super(Button, self).__init__(title, parent) self.setAcceptDrops(True) # dragEnterEvent方法不变 def dropEvent(self, event): self.setText(event.mimeData().text()) self.update_label_signal.emit() # 发出信号
然后在MainWindow的setup方法里连接信号和槽:
self.btn_quit = Button('Quit', self) # 把按钮的信号连接到主窗口的lbl_setText方法 self.btn_quit.update_label_signal.connect(self.lbl_setText) self.btn_quit.clicked.connect(QApplication.instance().quit) # 其他代码不变
修改后的完整代码(以方法1为例)
from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5.QtCore import * class Button(QPushButton): def __init__(self, title, parent): super(Button, self).__init__(title, parent) self.setAcceptDrops(True) def dragEnterEvent(self, event): if event.mimeData().hasFormat('text/plain'): event.accept() else: event.ignore() def dropEvent(self, event): self.setText(event.mimeData().text()) main_window = self.parent() main_window.lbl_setText() class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setGeometry(100, 100, 200, 150) self.setWindowTitle("Example") self.setup() def setup(self): self.btn_quit = Button('Quit', self) self.btn_quit.clicked.connect(QApplication.instance().quit) self.btn_quit.resize(self.btn_quit.sizeHint()) self.btn_quit.move(90, 100) self.btn_quit.setAcceptDrops(True) self.le_drag = QLineEdit('', self) self.le_drag.setDragEnabled(True) self.le_drag.move(50, 50) self.lbl_1= QLabel('Hi', self) self.lbl_1.move(50, 10) def closeEvent(self, event: QCloseEvent): reply = QMessageBox.question(self, 'Message', "r u sure!", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore() def lbl_setText(self): print("setText") self.lbl_1.setText("setText") def main(): app = QApplication([]) app.setStyle("Fusion") mainWin = MainWindow() mainWin.show() app.exec() if __name__ == '__main__': main()
现在你再拖放文本到按钮上,主窗口的标签就会正确变成"setText"啦!
内容的提问来源于stack exchange,提问作者Gürkan KARADAĞ
相关产品推荐
相关产品推荐

