Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PyQt: give parent when creating a widget?

Let's assume I want to create a dialog box, a child of my main program:

from PyQt4 import QtGui, QtCore

class WizardJournal(QtGui.QDialog):

    def __init__(self, parent):

        super(WizardJournal, self).__init__(parent)

        self.parent = parent

        self.initUI()


    def initUI(self):

        self.parent.wizard = QtGui.QWidget()

        self.ok_button = QtGui.QPushButton("OK", self)

        self.vbox_global = QtGui.QVBoxLayout(self)

        self.vbox_global.addWidget(self.ok_button)

        self.paret.wizard.setLayout(self.vbox_global)
        self.parent.wizard.show()


if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    parent = QtGui.QWidget()
    obj = WizardJournal(parent)
    sys.exit(app.exec_())

This dialog box will be opened and closed by my main program. What is better regarding memory consumption:

  • self.ok_button = QtGui.QPushButton("OK", self)
  • self.ok_button = QtGui.QPushButton("OK")

Basically, I would like to know if I should mention the parent widget when I create a widget. When I will close this dialog box, will the OK button be released from memory if I didn't mention the parent widget when I created it ?

like image 432
JPFrancoia Avatar asked Jun 20 '16 08:06

JPFrancoia


People also ask

Why use PySide instead of PyQt?

Advantages of PySide PySide represents the official set of Python bindings backed up by the Qt Company. PySide comes with a license under the LGPL, meaning it is simpler to incorporate into commercial projects when compared with PyQt. It allows the programmer to use QtQuick or QML to establish the user interface.

Is PySide same as PyQt?

PyQt is significantly older than PySide and, partially due to that, has a larger community and is usually ahead when it comes to adopting new developments. It is mainly developed by Riverbank Computing Limited and distributed under GPL v3 and a commercial license.

What is QWidget in PyQt5?

The QWidget widget is the base class of all user interface objects in PyQt5. We provide the default constructor for QWidget . The default constructor has no parent. A widget with no parent is called a window.

Is PyQt paid?

PyQt is free software developed by the British firm Riverbank Computing. It is available under similar terms to Qt versions older than 4.5; this means a variety of licenses including GNU General Public License (GPL) and commercial license, but not the GNU Lesser General Public License (LGPL).


1 Answers

Given the way your example is currently structured, neither the dialog nor any of its child widgets will be deleted when it is closed.

You can see this by changing the end of the example to look like this:

app.exec_()
print('\n'.join(repr(w) for w in app.allWidgets()))

which will give output like this (once the dialog is closed):

<__main__.WizardJournal object at 0x7fcd850f65e8>
<PyQt4.QtGui.QPushButton object at 0x7fcd850f6708>
<PyQt4.QtGui.QWidget object at 0x7fcd850f6558>
<PyQt4.QtGui.QDesktopWidget object at 0x7fcd850f6828>
<PyQt4.QtGui.QWidget object at 0x7fcd850f6678>

In PyQt, you have to be aware that there may be two kinds of reference held for a object: one on the Python side (the PyQt wrapper object) and one on the C++ side (the underlying Qt object). So to fully delete an object, you need to remove all of these references.

In general, Qt does not delete objects unless you explictly tell it to do so. This is something you need to be aware of when creating dialogs with a parent, because it is very easy to produce a memory leak otherwise. It is common to see code written like this:

def openDialog(self):
    dialog = MyDialog(self)
    dialog.show()

Which looks harmless at first glance - but the method will create a new dialog every time it is called, and Qt will end up holding on to every single one of them (because of the parent reference on the C++ side). One way to avoid this is to re-write the method so that it only keeps a reference on the Python side:

def openDialog(self):
    self.dialog = MyDialog()
    self.dialog.show()

But what to do about a modal dialog, which must have a parent? In that case, you could initialise the dialog class like this:

class MyDialog(QtGui.QDialog):
    def __init__(self, parent):
        super(MyDialog, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

And now Qt will automatically delete the dialog when it is closed, and also recursively delete all of its child objects as well. This will just leave behind an empty PyQt wrapper object, which will (eventually) be removed by the Python garbage-collector.

So for your particular example, I think I would re-write it to look something like this:

import sys
from PyQt4 import QtGui, QtCore

class WizardJournal(QtGui.QDialog):
    def __init__(self, parent):
        super(WizardJournal, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.initUI()

    def initUI(self):
        self.ok_button = QtGui.QPushButton("OK", self)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.ok_button)
        self.show()

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    parent = QtGui.QWidget()
    obj = WizardJournal(parent)
    app.exec_()
    print('\n'.join(repr(w) for w in app.allWidgets()))

The dialog class is now completely self-contained, and there is only one external python reference to the instance of it. (If you need to access the parent widget from within the dialog class, you can use self.parent()).

PS: when widgets are added to a layout, they will be automatically re-parented to whatever top-level widget eventually contains the layout. So, strictly speaking, it is not necessary to explicitly set a parent for such widgets in your code.

like image 191
ekhumoro Avatar answered Sep 21 '22 14:09

ekhumoro