在不违反 class 封装的情况下在两个 windows 之间通信
Communicate between two windows without violating class encapsulation
我创建了一个 pyqt5 小项目。这是 运行:
时应用程序的打印屏幕
当用户从主 window 中单击 QPushButton
时,会出现对话框 window 并且用户会在 QlineEdit
中写入内容。然后在单击对话框 window 的 QPushButton
时,对话框 window 向主 window 发送信号并被删除。该信号包含用户键入的文本。
下面是我的两个class的描述,非常简单:
主窗口class.
DialogWindow class(我想制作自己的 Dialog Class,而不使用现有的 Dialog windows)。
我的主脚本
我有几个问题:
在 windows 之间使用信号进行通信是否正确?我不认为我违反了 class 封装。但是,我不喜欢通过以下方式连接 child class 上的信号:
self.mySignal.connect(parent.updatelabelAnswer)
在这一行中,我使用了属性 parent
- 可以吗?在我看来,这不是使用信号的好方法。
我的第二个问题是:
我可以在 DialogWindow
的 on_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()
我创建了一个 pyqt5 小项目。这是 运行:
时应用程序的打印屏幕当用户从主 window 中单击 QPushButton
时,会出现对话框 window 并且用户会在 QlineEdit
中写入内容。然后在单击对话框 window 的 QPushButton
时,对话框 window 向主 window 发送信号并被删除。该信号包含用户键入的文本。
下面是我的两个class的描述,非常简单:
主窗口class.
DialogWindow class(我想制作自己的 Dialog Class,而不使用现有的 Dialog windows)。
我的主脚本
我有几个问题:
在 windows 之间使用信号进行通信是否正确?我不认为我违反了 class 封装。但是,我不喜欢通过以下方式连接 child class 上的信号:
self.mySignal.connect(parent.updatelabelAnswer)
在这一行中,我使用了属性 parent
- 可以吗?在我看来,这不是使用信号的好方法。
我的第二个问题是:
我可以在 DialogWindow
的 on_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()