【问题标题】:Communicate between two windows without violating class encapsulation在不违反类封装的情况下在两个窗口之间进行通信
【发布时间】:2016-06-29 18:24:18
【问题描述】:

我创建了一个小 pyqt5 项目。这是应用程序运行时的打印屏幕:

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

以下是我的两个类的描述,非常简单:

  • MainWindow 类。

  • DialogWindow 类(我想制作自己的 Dialog 类,而不使用预先存在的 Dialog 窗口)。

  • 我的主脚本

我有几个问题:

它是使用信号在窗口之间进行通信的正确方法吗?我不认为我违反了类封装。但是我不喜欢通过写来连接子类上的信号:

self.mySignal.connect(parent.updatelabelAnswer)

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

我的第二个问题是:

我可以在DialogWindowon_pushButton_clicked 插槽中调用self.deleteLater() 吗?似乎不是,因为我已经检查了 python 交互式 shell 并且对象 myDialogWindow 仍然可以访问。

【问题讨论】:

  • 查看我对这个主题的回答here。至于deleteLater(),即使可以手动调用插槽而不发出单个插槽,通常也没有必要,也不知道为什么要在这里这样做。 :P 当您可以简单地在此处复制粘贴时,请避免发布您的代码的屏幕截图。让人们更轻松地使用它并更快地解决您的问题。
  • 感谢您的回答。在我看来,我不能使用第三个脚本来连接两个小部件,因为对话框窗口的实例只会在单击主窗口的按钮时创建,因此我必须在插槽 on_pushButton_clicked 中创建这个实例.对于删除问题,我该如何删除对话窗口的实例?我粘贴了代码,因为我是从另一台计算机上获得的:p。但你是对的,一定有更好的解决方案

标签: python dialog pyqt encapsulation qt-signals


【解决方案1】:

好吧,所以我想我应该发布一个答案,而不是写臃肿的 cmets:P

关于删除我会引用Qt documentation

与 QWidget::close() 一样,done() 会删除对话框,如果 Qt::WA_DeleteOnClose 标志已设置。如果对话框是应用程序的 主小部件,应用程序终止。如果对话框是最后一个 窗口关闭时,会发出 QApplication::lastWindowClosed() 信号。

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

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


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

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

【讨论】:

  • 非常感谢 rbaleksandar 的回答!正如您所说,我已经在 MainWindow 的 init 中编写了 self.dialog= DialogWindow(self),并且在它的正下方我还编写了:self.dialog.getMySignal().connect(self.updateLabelAnswer)。这是访问 self.dialog 的 mySignal 属性的好方法吗?因为我不想直接写self.dialog.mySignal来违反封装。然而,我的解决方案让我觉得很难看。关于对话框窗口的删除,我听从了你的建议,没有在意。我只是按照你说的写了 show() 和 hide()。
  • 您可以在主窗口中连接对话信号,因为它在那里可见。您甚至可以将对话框的信号连接到主窗口内的对话框插槽。从主窗口类中访问对话框类成员没有任何问题。这不是 C++。 Python 中的数据封装模型更加灵活。您使用self.XXX(不带双下划线__)定义的所有内容都是公开的。有关 Python 类中私有数据的更多信息,请参阅here
  • 感谢 rbaleksandar 的回答!我现在很清楚如何调用我的对话窗口了。
【解决方案2】:

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

在您的示例中,我认为有两个选项是“正确的”。如果对话框至少是持久的,而不是模态运行的,那么它应该定义一个父类连接到的信号。对话框不应该自行删除,这应该是收到信号后父类的责任。

主窗口

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)

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

对话框窗口

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()

【讨论】:

  • 感谢 Brendan Abel 的解释!我现在知道如何制作模态输入对话框和非模态持久外围窗口。
猜你喜欢
  • 1970-01-01
  • 2017-02-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多