在不违反 class 封装的情况下在两个 windows 之间通信

Communicate between two windows without violating class encapsulation

我创建了一个 pyqt5 小项目。这是 运行:

时应用程序的打印屏幕

当用户从主 window 中单击 QPushButton 时,会出现对话框 window 并且用户会在 QlineEdit 中写入内容。然后在单击对话框 window 的 QPushButton 时,对话框 window 向主 window 发送信号并被删除。该信号包含用户键入的文本。

下面是我的两个class的描述,非常简单:

我有几个问题:

在 windows 之间使用信号进行通信是否正确?我不认为我违反了 class 封装。但是,我不喜欢通过以下方式连接 child class 上的信号:

self.mySignal.connect(parent.updatelabelAnswer)

在这一行中,我使用了属性 parent - 可以吗?在我看来,这不是使用信号的好方法。

我的第二个问题是:

我可以在 DialogWindowon_pushButton_clicked 位置调用 self.deleteLater() 吗?似乎没有,因为我已经检查过 python 交互式 shell 并且 object myDialogWindow 仍然可以访问。

好吧,我想我应该 post 一个答案而不是写臃肿的评论 :P

关于删除我会引用Qt documentation:

As with QWidget::close(), done() deletes the dialog if the Qt::WA_DeleteOnClose flag is set. If the dialog is the application's main widget, the application terminates. If the dialog is the last window closed, the QApplication::lastWindowClosed() signal is emitted.

但是,如果您想从打开它的其他小部件处理对话框 window 的关闭(和删除),则应使用插槽和信号。只需将一个按钮或任何来自您的主要小部件及其 clicked() 信号的按钮连接到对话框的 done() 插槽,您就可以开始了。

在这一点上我还想指出,删除对话框可能没有必要。根据对话框的内存占用量(创建和 运行 它使用了多少内存),您可能希望考虑在开始时创建对话框并将其保留在内存中,直到主应用程序关闭。除此之外,您还可以使用 hide()show() 将其显示在屏幕上。对于足够小的事物,这实际上是一个普遍的好做法,因为与简单地隐藏和显示它相比,删除然后创建 window 需要更多时间。


现在关于信号和槽,它们具有非常直接的语义。正如我在评论中 post 和我的其他 answer 编辑的那样,为了将插槽连接到信号,您需要让它们出现在同一范围内。如果不是这种情况,将一个(或两个)传递到情况已解决的地方。在你的情况下,你必须有一个共同的地方。如果两者都是顶级小部件,则必须在 main() 内进行连接。我宁愿将对话框作为扩展添加到您的 MainWindow class(作为 class 成员)和实例化以及那里的连接 - 例如在您的 [=16 的构造函数中=]:

class MainWindow(QMainWindow, Ui_MainWindow):

  def __init__(self, parent=None):
    super(MainWindow, self).__init__(parent)
    self.setupUi(self)
    self.dialog = DialogWindow(self)

    # Connect mainwindow's signals to dialog's slots
    # Connect dialog's signals to mainwindow's slots
    # And even connect dialog's signals to dialog's slots

一般来说,父级应该始终是执行信号连接的一方。让子部件在父部件上建立连接是有问题的,因为它对父部件施加了限制并导致副作用,并且在为子部件转移父部件所有权的情况下完全中断。

在您的示例中,我会考虑两个选项 "correct"。如果对话至少要有一定的持久性,而不是 运行 模态,那么它应该定义父 class 连接到的信号。对话框不应自行删除,这应该是父 class 在收到信号后的责任。

主窗口

def on_pushbutton_clicked(self):
    if not self.dlg:
        self.dlg = DialogWindow(self)
        self.dlg.mySignal.connect(self.on_mySignal)
        self.dlg.show()

def on_mySignal(value):
    self.dlg.mySignal.disconnect()
    self.dlg.close()
    self.dlg.deleteLater()
    self.dlg = None
    self.updateLabelAnswer(value)

您的对话框似乎是一个临时对话框,其存在只是为了收集输入,并且应该是 运行 模态的。在这种情况下,您甚至不必定义任何信号。只需创建 class 并提供 API 即可获取文本框的值。

DialogWindow

class DialogWindow(...)
    ...
    def on_pushbutton_clicked(self):
        self.accept()

    def getValue(self):
        return self.lineEdit.text()

在主窗口中

def on_pushbutton_clicked(self):
    dlg = DialogWindow(self)
    if dlg.exec_():
        value = dlg.getValue()