【问题标题】:PySide2 main window not working after pop-upPySide2主窗口在弹出后不起作用
【发布时间】:2022-11-10 23:55:09
【问题描述】:

我正在使用 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 来开发它们)。我认为解决此问题没有必要,但如果需要,我可以提供。我在这里做错了什么?我需要打开弹出窗口并在之后仍与主窗口进行交互。

我不知道如何实际解决这个问题。我相信我的错误出现在我用来打开弹出窗口的代码中,但我不确定如何调整它以使其正常工作。

【问题讨论】:

  • 您可能在MainWindow 类中创建self.ui,然后在showPopupSuccessHide 中覆盖。在该函数中,将所有出现的self.ui 更改为其他内容,例如self.popupUi,或使其成为新窗口的属性,例如self.window.ui。注意: 1.window()是所有Qt小部件的现有函数,你不能覆盖它(使用更好的变量名); 2. 如果你为弹出窗口正确地创建了一个子类(就像你为MainWindow 所做的那样),它将在它自己的__init__ 中创建它的ui,这一切都不会发生。
  • 谢谢@musicamante,这解决了我的问题。您能否将其发布为答案,以便我接受?

标签: python pyqt5 qt5 pyside2


【解决方案1】:

TL;博士

不要覆盖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 类(有时称为“表单类”)的实例,然后使用该实例“设置”实际窗口,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()

如果该对话框也有一个 Designer 文件,我们需要做一些类似于开始时所做的事情。不幸的是,一个非常常见的错误是使用相同的名称创建 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 中创建了一个子小部件并将其重命名为“窗口”。如上所述,如果您总是仔细考虑分配给对象的名称,这可能永远不会发生(将小部件命名为“窗口”没有多大意义)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多