3

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.

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
clouvis
  • 567
  • 1
  • 6
  • 18
  • Check my answer on this topic [here](http://stackoverflow.com/a/35744748/1559401). As for `deleteLater()` even though slots can be invoked manually without emitting a single it is generally not necessary and don't know why you do that here. :P Also please avoid posting **screenshots** of your code when you can simply copy-paste it here. Makes it easier for people to work with it and solve your problem faster. – rbaleksandar Mar 14 '16 at 15:57
  • thank for your answer. It seems to me that I cannot use a third script to connect the two widgets, because the instance of the dialog window will only be created while clicking on the push button of the main window, therefore I have to create this instance in the slot on_pushButton_clicked. For the delete question, how can I do to delete the instance of the dialog window ? I have pasted the code because I had it from another computer :p . But you are right, there must be a better solution – clouvis Mar 14 '16 at 16:08

2 Answers2

2

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()
Brendan Abel
  • 35,343
  • 14
  • 88
  • 118
  • Thank you Brendan Abel for you explication! I now know how to make modal input dialog and non modal persistent peripherical windows. – clouvis Mar 16 '16 at 14:55
1

Okay so I guess I should post an answer instead of writing bloated comments :P

About the deletion I will quote the 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.

However if you want to handle the closing (and deletion) of the dialog window from your other widget that opens it, slots and signals should be used. Simply connect a button or whatever from your main widget and its clicked() signal to the done() slot of your dialog and you are good to go.

At this point I would also like to point out that deleting a dialog may not be necessary. Based on the memory footprint of the dialog (how much memory is used to create and run it) you may wish to consider creating the dialog at the beginning and leaving it in your memory until the main application is closed. In addition to that you can use hide() and show() to display it on the screen. This is actually a generally good practice for things that are small enough since the deletion and then creation of a window takes more time compared to simply hiding and showing it.


Now about the signals and slots these have pretty straight-forward semantics. As I've posted in the comments and my other answer in order to connect a slot to a signal you need to have them present in the same scope. If that's not the case pass one (or both) to a place where the situation is fixed. In your case you have to have a common place for both. If both are top-level widgets you have to do the connections inside your main(). I would rather add the dialog as an extension to your MainWindow class (as a class member) and to the instantiation plus the connections there - for example in the constructor of your 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
Community
  • 1
  • 1
rbaleksandar
  • 8,713
  • 7
  • 76
  • 161
  • Thank you very much rbaleksandar for your answer ! I have written the self.dialog= DialogWindow(self) in the init of the MainWindow as you said, and right below it I have also written : self.dialog.getMySignal().connect(self.updateLabelAnswer). Is it the good way to access to the mySignal attribute of self.dialog ? Because I don't want to violate the encapsulation by writing directly self.dialog.mySignal. However my solution reminds ugly to me. Concerning the deletion of the dialog window, I followed your advice and don't take care about it. I just write show() and hide() as you said. – clouvis Mar 15 '16 at 15:15
  • You can connect the dialog signals from within your main window since it's visible there. You can even connect a dialog's signal to a dialog's slot inside your main window. There is nothing wrong in accessing the dialog class members from within your main window class. This is **not** C++. The data encapsulation model in Python is much more flexible. Everything you define with `self.XXX` (without double underscores `__`) is public. See [here](http://stackoverflow.com/a/1641236/1559401) for more info on private data in Python classes. – rbaleksandar Mar 15 '16 at 18:57
  • Thank you rbaleksandar for your answer ! I understand well how to call my dialog window now. – clouvis Mar 16 '16 at 14:54