Search code examples
pythondialogpyqtencapsulationqt-signals

Communicate between two windows without violating class encapsulation


I have created a little pyqt5 project. Here is a printscreen of the application while running:

application while running

When the user clicks on the QPushButton from the main window, the dialog window appears and the user writes something in the QlineEdit. Then while clicking on the QPushButton of the dialog window, the dialog window sends a signal to the main window and is deleted. The signal contains the text typed by the user.

Here are the descriptions of my two classes which are very simple:

  • The MainWindow class.

  • The DialogWindow class (I want to make my own Dialog Class without using the pre existing Dialog windows).

  • My main script

enter image description here

I have several questions:

Is it the right way of using signals in order to communicate between windows? I do not think that I violate the class encapsulation. However I do not like to connect the signal on the child class by writing:

self.mySignal.connect(parent.updatelabelAnswer)

In this line I use the attribute parent - is it okay? It seems to me that it is not a good way to use signals.

My second question is:

Am I right to call self.deleteLater() in the on_pushButton_clicked slot of DialogWindow? It seems not, as I have checked with the python interactive shell and the object myDialogWindow is still accessible.


Solution

  • Generally, the parent should always be the one performing the signal connecting. Having the child widget make connections on the parent is problematic because it places limitations on the parent and causes side effects, and completely breaks in cases where parent ownership is transfrerred for the child widget.

    In your example, there are two options I would consider "correct". If the dialog was meant to be at least somewhat persistent, and not meant to be run modally, then it should define a signal that the parent class connects to. The dialog should not delete itself, that should be the responsibility of the parent class after the signal is received.

    MainWindow

    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)
    

    Your dialog seems to be a temporary dialog that exists just to gather input and should probably be run modally. In that case, you don't even have to define any signals. Just create the class and provide an API to get the value of the text box.

    DialogWindow

    class DialogWindow(...)
        ...
        def on_pushbutton_clicked(self):
            self.accept()
    
        def getValue(self):
            return self.lineEdit.text()
    

    In MainWindow

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