简体   繁体   English

从向导/对话框中接受/完成的信号中检索值的Qt规范方法

[英]Qt canonical way of retrieving values from Wizard / Dialog on accepted / finished signal

I'm using PyQt, but I guess the same questions also applies to Qt C++. 我正在使用PyQt,但我想同样的问题也适用于Qt C ++。

Assume that I have a main window with a button that opens a wizard that collects data and that data needs to be used in the main window after the wizard has closed. 假设我有一个带按钮的主窗口,该按钮会打开一个向导来收集数据,并且在向导关闭后需要在主窗口中使用数据。 standard procedure. 标准程序。

So there are multiple ways to do this. 因此,有多种方法可以做到这一点。 either I can pass a reference to the main window to the Wizard and it does all the work using the main window reference, but I'd say that breaks modularity. 要么我可以将对主窗口的引用传递给向导,然后它使用主窗口引用完成所有工作,但是我要说这破坏了模块化。 I can also wire up a callback to the wizard accepted rejected or finished signal, but in that callback, I don't have a reference to the wizard itself, so I cannot get to the data in the wizards fields. 我还可以将回调连接到向导accepted rejectedfinished信号,但是在该回调中,我没有对向导本身的引用,因此无法获取向导字段中的数据。 Unless I store a reference to the wizard as instance variable in order to access it again from the callback. 除非我将对向导的引用存储为实例变量,以便从回调中再次访问它。

Another option is (even though I haven't fully understood it yet) to get a reference to the emitter of the signal (ie the wizard) in the callback using https://doc.qt.io/qt-5/qobject.html#sender . 另一个选择是(即使我尚未完全理解)使用https://doc.qt.io/qt-5/qobject在回调中获取对信号发射器(即向导)的引用。 html#sender But that seems not recommended. 但这似乎不建议。

So whats the canonical way? 那么规范的方式是什么?

Premise: this is a bit of an opinion based question, as there is not one and only "good" way to do that. 前提:这是一个基于观点的问题,因为没有一种且只有一种 “好的”方式可以做到这一点。 I just wanted to comment (opinion based answer/questions are discouraged here in SO), but the limited formatting isn't very helpful. 我只是想发表评论(SO中不建议基于意见的答案/问题),但是有限的格式不是很有帮助。

"Passing a reference" doesn't necessarily break modularity. “通过引用”并不一定会破坏模块化。
Instead, that's exactly what QDialog usually are initialized: the parent is the "calling" window, which is also how a QDialog can be "modal" to the parent or the whole application (meaning that no interaction outside the dialog is allowed as long as it is active). 取而代之的是QDialog通常被初始化的内容:父级是“调用”窗口,这也是QDialog如何对父级或整个应用程序进行“模态化”(这意味着只要在对话框外不允许交互,它是活动的)。

AFAIK, I don't know if this is actually considered canonical , but the following is the most commonly suggested approach. AFAIK,我不知道这是否真的被认为是规范的 ,但是以下是最常用的建议方法。
The idea is that you have a child object (a QDialog, usually) which might or might not be initialized everytime you need it, that's up to you; 这个想法是,您有一个子对象(通常是QDialog),它可能会或可能不会在每次需要时初始化,这取决于您; the important part is that you need a reference to it at least for the time required to update its result, which can even happen within the scope of a single method/slot. 重要的一点是,至少在更新其结果所需的时间内,您需要对其进行引用,这甚至可能在单个方法/槽的范围内发生。

from PyQt5 import QtWidgets

class MyWizard(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        layout = QtWidgets.QVBoxLayout()
        self.setLayout(layout)
        self.checkBox = QtWidgets.QCheckBox('check')
        layout.addWidget(self.checkBox)
        self.input = QtWidgets.QLineEdit()
        layout.addWidget(self.input)
        buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel)
        layout.addWidget(buttonBox)
        buttonBox.accepted.connect(self.accept)
        buttonBox.rejected.connect(self.reject)

    def setData(self, **data):
        self.checkBox.setChecked(data.get('check', False))
        self.input.setText(data.get('text', ''))

    def getData(self):
        return {'check': self.checkBox.isChecked(), 'text': self.input.text()}

    def exec_(self, **data):
        self.setData(**data)
        return super().exec_()


class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        centralWidget = QtWidgets.QWidget()
        self.setCentralWidget(centralWidget)
        layout = QtWidgets.QHBoxLayout()
        centralWidget.setLayout(layout)
        self.showWizBtn = QtWidgets.QPushButton('Show wizard')
        layout.addWidget(self.showWizBtn)
        self.showWizBtn.clicked.connect(self.getDataFromWizard)
        self.data = {}

    def getDataFromWizard(self):
        wiz = MyWizard(self)
        if wiz.exec_(**self.data):
            self.data.update(wiz.getData())


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

Another possibility is to create a persistent child dialog (but keep in mind that if the data can be changed by the parent, you'll have to find a way to update it, at least when executed); 另一种可能性是创建一个持久的子对话框(但请记住,如果父级可以更改数据,则至少在执行时,必须找到一种更新方法); the concept here is that you can exec the dialog whenever you need, and you have the accepted signal connected to a slot that can get the data from the dialog. 这里的概念是,您可以在需要时执行对话框,并且将accepted信号连接到可以从对话框获取数据的插槽。 This is not a common use (nor very suggested IMHO) and should be used only for very specific scenarios. 这不是常见用途(也不建议使用IMHO),并且仅应用于非常特殊的情况。

As you already found out, using sender is not suggested: signals are asynchronous, and while in normal conditions the sender is reliable it's better to avoid using it unless absolutely necessary. 正如您已经发现的,不建议使用sender :信号是异步的,并且在正常情况下发送器是可靠的,除非绝对必要,否则最好避免使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM