[PyQt] Realizing memory used by signals with objects

Jan Kotanski jan.kotanski at desy.de
Thu Apr 23 07:44:26 BST 2015


Hi,

Great. Thanks. Indeed.

Bests,
Jan

On 04/22/2015 05:41 PM, Phil Thompson wrote:
> On 16/04/2015 10:59 am, Jan Kotanski wrote:
>> Hi,
>>
>> I've just tested memory leaks in my application and I've accounted
>> a problem how to realize memory used by signals with objects, e.g.
>> QSignalMapper.mapped signal with a QWidget parameter.
>> After disconnecting this signal using garbage collector one can find
>> that PyQt leaves a weakref in the memory.
>> If I change the QWidget parameter to an int parameter this problem
>> disappears.
>>
>> I've tested this issue in PyQt 4.9.3 (wheezy), 4.11.2 (jessie),  5.3.2
>> (jessie).
>>
>> Is there any way to remove this weak reference (except catching it by
>> gc)?
>>
>> Bellow I append my test script for PyQt4.
>>
>> Thanks,
>> Jan
>>
>>
>> from PyQt4.QtGui import (QWidget, QPushButton, QDialog, QVBoxLayout)
>> from PyQt4.QtCore import (QSignalMapper, pyqtSlot, QObject)
>>
>> import sys
>> import gc
>> from collections import Counter
>>
>>
>> class Con(QObject):
>>     def __init__(self, parent=None):
>>         super(Con, self).__init__(parent)
>>         self.mapper = QSignalMapper(self)
>>         self.mapper.mapped.connect(self.checked)
>>
>>     @pyqtSlot(QWidget)
>> #    @pyqtSlot(int)
>>     def checked(self, widget):
>>         print( "TYPE: %s" % type(widget))
>>
>>     def close(self):
>>         self.mapper.mapped.disconnect(self.checked)
>>         self.mapper.setParent(None)
>>         self.mapper = None
>>
>>
>> class Dialog(QDialog):
>>     def __init__(self, parent=None):
>>         super(Dialog, self).__init__(parent)
>>         self.button = QPushButton("Run")
>>         vbl = QVBoxLayout()
>>         vbl.addWidget(self.button)
>>         self.con = None
>>         self.setLayout(vbl)
>>         self.button.clicked.connect(self.reset)
>>
>>     def test(self):
>>         self.con = Con(self)
>>         self.con.close()
>>         self.con.setParent(None)
>>         del self.con
>>         self.con = None
>>
>>     @pyqtSlot()
>>     def reset(self):
>>         for i in range(1000):
>>             self.test()
>>             gc.collect()
>>             print ("i = %s #OBJ = %s: %s" % (
>>                 i, len(gc.get_objects()),
>>                 Counter([type(g).__name__ for g in gc.get_objects()]
>>                     ).most_common(4)))
>>
>>
>> def main():
>>     from PyQt4.QtGui import QApplication
>>     app = QApplication(sys.argv)
>>     form = Dialog()
>>     form.show()
>>     app.exec_()
>>
>> if __name__ == "__main__":
>>     main()
> 
> There is no leak in the above script - just things aren't being tidied
> up when you expect - the Qt event loop needs a chance to run in order to
> handle internal deleteLater() calls.
> 
> If you print the number of weak ref objects returned by get_objects() at
> the start of reset() you should see that this is the same each time.
> 
> BTW, if you want to avoid the weak refs in the first place then connect
> to the correct mapped() overload...
> 
>     self.mapper.mapped[QWidget].connect(self.checked)
> 
> Phil
> _______________________________________________
> PyQt mailing list    PyQt at riverbankcomputing.com
> http://www.riverbankcomputing.com/mailman/listinfo/pyqt


More information about the PyQt mailing list