2

In the example, when the program runs, 18 MB of RAM is occupied. When the second Window runs, another 4 MB will be occupied by RAM. When we close the second Window , the memory occupied will not be returned to RAM, and if the second Window is opened for the second time, then again 4 MB of RAM will be occupied. What is your solution for this problem? This example is PyQt4 And My App is PyQt5.

This example is from this link: PyQT: how to open new window

If possible, propose a program that opens the second window with the principle method and does not occupy RAM space after closing.

from PyQt4 import QtGui, QtCore
import sys

class Second(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Second, self).__init__(parent)


class First(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(First, self).__init__(parent)
        self.pushButton = QtGui.QPushButton("click me")
        self.setCentralWidget(self.pushButton)

        self.pushButton.clicked.connect(self.on_pushButton_clicked)
        self.dialogs = list()

    def on_pushButton_clicked(self):
        dialog = Second(self)
        self.dialogs.append(dialog)
        dialog.show()

    def main():
        app = QtGui.QApplication(sys.argv)
        main = First()
        main.show()
        sys.exit(app.exec_())

if __name__ == '__main__':
    main()

Example in PyQt5

from PyQt5 import QtCore, QtGui, QtWidgets, uic
import sys

class Second(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Second, self).__init__(parent)


class First(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(First, self).__init__(parent)
        self.pushButton = QtWidgets.QPushButton("click me")
        self.setCentralWidget(self.pushButton)

        self.pushButton.clicked.connect(self.on_pushButton_clicked)
        self.dialogs = list()


    def on_pushButton_clicked(self):
        dialog = Second(self)
        self.dialogs.append(dialog)
        dialog.show()


def main():
    app = QtWidgets.QApplication(sys.argv)
    main = First()
    main.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241

1 Answers1

3

In PyQt depending on how you configure an object your property can be managed by C ++ or Python. In the case of a QObject such as the QMainWindow if a parent is passed, the memory handling is C++, and the Qt rule indicates that a child will only die if the parent dies or the child is explicitly deleted with deleteLater. So Second's life cycle depends First, that is, even when you close the window the object is not deleted. For the window to be deleted when the window is closed you must activate the attribute Qt::WA_DeleteOnClose, so the solution is to add it in the Second class:

class Second(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Second, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose) # <---

If you are going to use a container to save the reference of the objects you might have problems because when C++ deletes the object it does not notify the container. So there are the following options to avoid those problems:

  • Do not use a container.
  • Use weakref.ref() so that when the object is deleted it is also removed from the container:
def on_pushButton_clicked(self):
    dialog = Second(self)
    self.dialogs.append(weakref.ref(dialog, self.dialogs.remove))
    dialog.show()
  • Use the destroyed signal to remove objects from the container:
import sip
# ...
class First(QtWidgets.QMainWindow):
    # ...
    def on_pushButton_clicked(self):
        dialog = Second(self)
        dialog.destroyed.connect(self.on_destroyed)
        self.dialogs.append(dialog)
        dialog.show()

    @QtCore.pyqtSlot('QObject*')
    def on_destroyed(self, obj):
        self.dialogs = [dialog for dialog in self.dialogs if not sip.isdeleted(dialog)]
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • This works absolutely fine unless you reference the `Second` class object somewhere else. And OP is doing that with `dialogs` list – icwebndev Apr 26 '19 at 20:59
  • @icwebndev Which list ?, even if there was a container this only stored deferential variables since C++ eliminated the variable but not the reference to the variables. – eyllanesc Apr 26 '19 at 21:01
  • I've just checked on a quick modification of OPs example, `WA_DeleteOnClose` does it's job properly (my mistake!), but Python still keeps references in the list - `self.dialogs = list()`. – icwebndev Apr 26 '19 at 21:15
  • 1
    @icwebndev As you indicate even with the container the memory of the window has been removed, the list will only store variables that point to an unreserved address, for you to check add the following code `import sip` `QtCore.QTimer(self, interval=1000, timeout= lambda: print([sip.isdeleted(dialog) for dialog in self.dialogs])).start()` after `self.dialogs = list()` – eyllanesc Apr 26 '19 at 21:35
  • 1
    @icwebndev [cont.] So the memory was removed but the variable that pointed to that memory was not notified, so the correct thing could be not to use a container, or use weakref, or simply delete the variable in the list with the help of the destroyed signal – eyllanesc Apr 26 '19 at 21:35
  • 1
    @icwebndev I have added an explanation regarding what you point out. – eyllanesc Apr 26 '19 at 21:45