我正在为使用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来开发它们)。我认为没有必要解决这个问题,但如果需要,我可以提供。我在这里做错什么了?我需要打开弹出窗口,然后仍然与主窗口交互。
我不知道怎么才能真正解决这个问题。我相信我在打开弹出窗口所用的代码中有错误,但我不知道如何调整它才能正常工作。
发布于 2022-11-10 15:55:09
TL;DR
不要覆盖self.ui
。
解释
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中设置的对象名称)访问这些小部件:
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小部件的现有函数(它返回调用它的小部件的顶级祖先窗口)。
根据经验,一定要花时间决定如何命名对象,使用智能名称,并考虑到大多数常见的名称可能已经被采用:请记住在您使用的小部件类型的文档中检查“所有成员的列表,包括继承的成员”链接,直到您有足够的经验来记住它们。
解决方案
显而易见的解决方案是为对话框的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)
self.myLineEdit.setText('some text')
def createWindow(self):
self.dialog = MyDialog()
self.dialog.anotherLineEdit.setText(self.myLineEdit.text())
self.dialog.show()
这种方法的唯一问题是,它可能会无意中覆盖“主”小部件的函数名称:例如,如果您在Designer中创建了一个子小部件并将其重命名为“窗口”。如前所述,如果您总是仔细考虑分配给对象的名称,这可能永远不会发生(将小部件命名为“窗口”没有多大意义)。
https://stackoverflow.com/questions/74379650
复制相似问题