我正在为使用PySide2的应用程序构建一个图形界面。我的主窗口是一个QMainWindow,每当在主窗口上执行特定的操作时,我都试图打开一个弹出窗口,即QDialog。
弹出窗口开得很好。但是,打开后,主窗口就不再响应了。我认为问题在于我的应用程序正在用弹出窗口覆盖主窗口。每当我试图更改主窗口的stackedWidget索引时,错误消息是:
AttributeError:“Ui_popupHideSuccess”对象没有属性“stackedWidget”
我用来打开主窗口的代码如下:
if __name__ == '__main__': app = QApplication(sys.argv) myWindow = MainWindow() myWindow.show() sys.exit(app.exec_())
我用来打开弹出窗口的代码如下:
def showPopupSuccessHide(self): self.window = QDialog() self.ui = Ui_popupHideSuccess() self.ui.setupUi(self.window) self.window.show()
窗口本身的代码在其他文件上(因为我正在使用QtDesigner来开发它们)。我认为没有必要解决这个问题,但如果需要,我可以提供。我在这里做错什么了?我需要打开弹出窗口,然后仍然与主窗口交互。
我不知道怎么才能真正解决这个问题。我相信我在打开弹出窗口所用的代码中有错误,但我不知道如何调整它才能正常工作。
上云精选
2核2G云服务器 每月9.33元起,个人开发者专属3年机 低至2.3折
TL;DR
不要覆盖 self.ui 。
self.ui
解释
uic 组合是如何工作的
uic
正确使用pyuic生成的文件的常见方法之一是使用组合(相对于多重继承):
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog from ui_mainWindow import Ui_MainWindow class MyWindow(QMainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.myLineEdit.setText('some text')
这很好,而且很有意义:概念是创建pyuic类的实例(有时称为"form class"),然后使用该实例“设置”实际窗口, self.ui 对象包含对所有小部件的引用。
请注意,使ui持久(使用实例属性)实际上并不是一个严格的要求,但要能够直接访问小部件通常是必需的,这对于创建信号连接或读取属性通常很重要。
但是,如果这不是必需的,它还是会起作用的:小部件自动“修复”到主窗口(或它们的直接父窗口),垃圾收集不是问题,因为Qt将在内部保留它自己的引用(就Qt而言,“窗口具有所有权”)。
从技术上讲,这是完全正确的:
class MyWindow(QMainWindow): def __init__(self): super().__init__() Ui_MainWindow().setupUi(self)
然后,我们仍然可以使用 findChild 及其对象名称(在Designer中设置的对象名称)访问这些小部件:
findChild
self.findChild(QLineEdit, 'myLineEdit').setText('some text')
显然,这是不太实际的。
创建“子”窗口
当需要创建子窗口(通常是对话框)时,通常建议使用实例属性来避免垃圾收集:
def createWindow(self): self.window = QDialog() self.window.show()
如果该对话框也有一个设计器文件,我们需要做一些类似于在开始时所做的事情。不幸的是,一个非常常见的错误是使用相同的名称创建ui实例:
def createWindow(self): self.window = QDialog() self.ui = Ui_Dialog() self.ui.setupUi(self.window) self.ui.anotherLineEdit.setText('another text') self.window.show()
这在理论上是很好的:所有的工作都如预期的那样。但是有一个很大的问题:上面的内容覆盖了 self.ui ,这意味着我们失去了对主窗口小部件的 所有 引用。
假设您希望根据主窗口中写入的文本在对话框中设置行编辑的文本;以下内容可能会崩溃:
def createWindow(self): self.window = QDialog() self.ui = Ui_Dialog() self.ui.setupUi(self.window) self.ui.anotherLineEdit.setText(self.ui.myLineEdit.text()) self.window.show()
这清楚地显示了一个重要的方面:在分配可能已经存在的属性之前,必须始终进行思考。
在上面的代码中,这实际上已经完成了两次:我们不仅重写了之前创建的 self.ui ,而且还重写了 window() ,这是所有Qt小部件的现有函数(它返回调用它的小部件的顶级祖先窗口)。
window()
根据经验,一定要花时间决定如何命名对象,使用智能名称,并考虑到大多数常见的名称可能已经被采用:请记住在您使用的小部件类型的文档中检查“所有成员的列表,包括继承的成员”链接,直到您有足够的经验来记住它们。
解决方案
显而易见的解决方案是为对话框的ui使用不同的名称:
def createWindow(self): self.dialog = QDialog() self.dialog_ui = Ui_Dialog() self.dialog_ui.setupUi(self.dialog) self.dialog_ui.anotherLineEdit.setText(self.ui.myLineEdit.text()) self.dialog.show()
更好的解决方案是为对话框创建一个子类,而不是:
class MyDialog(QDialog): def __init__(self, parent=None) super().__init__(parent) self.ui = Ui_Dialog() self.ui.setupUi(self) class MyWindow(QMainWindow): # ... def createWindow(self): self.dialog = MyDialog() self.dialog.ui.anotherLineEdit.setText(self.ui.myLineEdit.text()) self.dialog.show()
还请记住,另一个常见的方法(在我的经验中,更简单、更直观)是使用多重继承而不是组合:
class MyDialog(QDialog, Ui_Dialog): def __init__(self, parent=None) super().__init__(parent) self.setupUi(self) class MyWindow(QMainWindow, Ui_MainWindow): def __init__(self): super().__init__() self.setupUi(self)