I have a question of functionality that I am uncertain about. I'm not stuck on code implementation, so there is no need to share code for this question. I use
layout.removeWidget
widget.deleteLater()
a couple/few times in a project I'm building. I once read here on SO that deleteLater() does not function as described when deleting a widget with child widgets. The comment said that child widgets do not get deleted correctly, and will stay in memory, which could lead to memory bloat/leaks. Simply put (my question), is this true?
The docs mention nothing of this that I could find. And it was just one comment from years back, that was actually written for PyQt5 (or 4), I believe. So, have any of you guys done any tests on this? Is this a bug in older versions of PyQt that has been fixed, or was the commentor outright wrong?
As you can see from my question, my issue isn't how to write something, it's about behind the scenes functionality of deleteLater().
First of all, it is important to remember that PyQt (as PySide) is a binding to Qt, which is written in C++.
All Qt objects and functions are accessed from python using wrappers.
When a Qt object is created from Python, there are actually two objects:
the C++ object;
the python object that allows us to use the above Qt object;
The lifespan of those two objects may not always coincide. In fact, the C++ object can be destroyed while the python reference remains, or the other way around.
C++ object destruction
deleteLater() is guaranteed to destroy the C++ objects along with any object for which they have ownership (including indirect ownership for grand[grand, ...]children). Note that the object is not destroyed immediately, but only as soon as control returns to the event loop: then the object emits the destroyed() signal and after that all its children are destroyed along with it, recursively.
This will not delete the python reference, if it existed. For instance:
class MyLabel(QLabel):
def __init__(self, value, parent=None):
super().__init__(value, parent)
self.value = value
If you keep a python reference to an instance of the class above, you will still be able to access its self.value even after the object has been destroyed with deleteLater(). If you try to access any Qt functions or properties, instead, you'll get an exception:
>>> print(mylabel.value)
'some value'
>>> print(mylabel.text())
RuntimeError: wrapped C/C++ object of type MyLabel has been deleted
So, the python object will obviously keep using memory resources until it get garbage collected (its reference count becomes 0). This can be a problem if you keep references to objects that have large memory footprint on their python side (i.e., a large numpy array).
Python object destruction
Deleting a python reference doesn't guarantee the destruction of the Qt object, as it only deletes the python object. For instance:
>>> parent = QWidget()
>>> label = MyLabel('hello', parent)
>>> parent.show()
>>> del label
>>> print(parent.findChild(QLabel).text())
'hello'
This is because when an object is added to a parent, that parent takes ownership of the child.
Note that Qt objects that have no parent will also get destroyed when all python references are destroyed as well.
That is the reason of one of the most common questions here on SO, when somebody is trying to create another window using a local reference and without any parent object, but the window is not shown because it gets immediately garbage collected.
Note that this is valid for Qt6 as it is for Qt5 and as it was for Qt4, and it is also for their relative python bindings.
Considering what written in the first part:
>>> parent = QWidget()
>>> label = MyLabel('hello', parent)
>>> parent.show()
>>> del parent
>>> print(label.value)
'hello'
>>> print(label.text())
RuntimeError: wrapped C/C++ object of type MyLabel has been deleted
So, what must be always kept in mind is where objects live, including their C++ and python properties and attributes. If you're worried about memory usage for standard (not python-subclassed) widgets, deleteLater() is always guaranteed to release the memory resources of those widgets, and you only need to ensure that no remaining python reference still exists (but that would be true for any python program).
Finally, some considerations:
in some very complex situations it's not always possible to keep track of all python references, so it's possible that even if you destroyed a parent, the references to some children objects still exist;
not all Qt objects are QObjects, and their usage, behavior or memory management depends both on Qt and the C++ implementation; those object can be sometimes destroyed when their python references are, or when Qt "decides so";
not all QObjects take ownership of other QObjects when they are "added" to them: one common case is QAction, which can be shared among many objects (QMenu, QMenuBar, QToolBar, and even standard QWidgets); always look for the "...takes ownership" phrase in the documentation whenever in doubt;
Related
I want to understand what is the difference between deleting a widget(include it's layout and children in this layout) using sip.delete() and deleteLater(). I know that removeWidget() and setParent(None) is just removing the widget from layout, but it's not deleting the object itself from the memory. If I want to delete an object from a memory, which one shall I use? I know this question is asked before, but I hope to get detailed answer:)
I recommend you read this answer since I will use several concepts explained there.
The sip.delete() is used to directly invoke the destructor of the C++ object from the wrapper, something like:
delete wraper_instance->_cpp_object;
Instead deleteLater() is a method of the QObjects that sends an event so that the eventloop calls the destructor of the C++ object, something like:
post QDeferredDeleteEvent.
Run all pending events.
Destroy the object.
emit the destroyed signal.
Why do QObjects have as an alternative to deleteLater()? Well, directly deleting a QObject can be unsafe, for example let's assume that some QWidget (which is a QObject) is deleted invoking the destructor directly but a moment before in another part of the application it asks to update the entire GUI, as the GUI is not notified removing the object will then cause unallocated memory to be accessed causing the application to crash.
Therefore if you want to delete a QObject then it is safer to use deleteLater(), for other C++ objects (like QImage, QPixmap, QGraphicsItems, etc) you should use sip.delete().
For more information read:
https://doc.qt.io/qt-5/qobject.html#dtor.QObject
https://doc.qt.io/qt-5/qobject.html#deleteLater
I have a GUI programmed in PyQt with many widgets and different windows, etc. The data for the GUI is stored in a python object. This object should be reachable from every widget in the program. Where should I put this object?
Right now I have it in the QMainWindow instance that is used for the program's main window. The problem is that it is hard to reach the QMainWindow object from deeply nested widgets. It seems much simpler to use the QApplication instance for that, because you can get it with QtCore.QCoreApplication.instance() (as shown in this answer). However, I couldn't find any other examples encouraging you to change the QApplication class, so I wonder if it really should be used that way.
What approach would you suggest?
The correct approach is to put this data/settings object in a separate module. This can then be simply imported wherever it is needed. Ideally, the module (and the code which creates the data/settings object) should be largely independent from the rest of the application.
There is no real value in tying the data/settings object to the application instance, since you would still have to import either QApplication or qApp to access it anyway. And the same thing applies to the QMainWindow - it just moves the problem to a different location, and adds an unnecessary layer of indirection. Another big problem with this approach is that the data/settings object cannot be accessed until an instance of the application or main window becomes available. Very often, the data/settings object will be required during the initialisation of various parts of the application, so tying it to specific GUI elements can easily lead to circular dependencies, or other ordering problems.
I suppose the key design principle here is Loose Coupling: once you've decoupled your data/settings object from the GUI, the rest of the application can access it wherever and whenever it is required.
I tried creating GUI with a few widgets, all of them without secifying a parent.
It worked fine.
Is this Ok, or there is reason to specify the parent?
Thanks!
In general, it's better to specify a parent wherever possible, because it can help avoid problems with object cleanup. This is most commonly seen when exiting the program, when the inherent randomness of the python garbage-collector can mean objects sometimes get deleted in the wrong order, causing the program to crash.
However, this find of problem does not usually affect the standard GUI widgets, because Qt will automatically reparent them once they have been added to a layout. The more problematic objects are things like item-models, item-delegates, graphics-scenes, etc, which are closely linked to a view.
Ideally, a pyqt program should have one root window, with all the other objects connected to it in a parent-child hierarchy. When the root is deleted/closed, Qt will recursively delete all its child objects as well. This should leave only the pyqt wrapper objects behind, which can be safely left to the python garbage-collector to clean up.
A more constructive benefit of specifying parents, is that it simply makes objects more accessible to one another. For instance, a common idiom is to iterate over a group of buttons via their parent:
for button in parent.findChildren(QAbstractButton):
print(button.text())
I'm using PyGtk.
Will a runtime-generated function connected to the signal "drag_data_get" of a widget be garbage-collected when the widget is destroyed ?
Same question about the Gtk.TargetList that are created and associated with drag source/dest target?
I did found Python and GTK+: How to create garbage collector friendly objects? but it does not help too much.
In short: yes, it does, dynamically created functions are created just like any other Python objects created during run-time.
Longer answer: For resources managed by the garbage collector, such as objects not tied to an external resource, Python and PyGTK will correctly dispose of unused objects. For external resources, such as open files or running threads, you need to take steps to ensure their correct cleanup. To answer your question precisely, it would be useful to see concrete code. In general, the following things apply to Python and GTK:
Python objects, including dynamically created functions, are deallocated some time after they can no longer be reached from Python. In some cases deallocation happens immediately after the object becomes unreachable (if the object is not involved in reference cycles), while in others you must wait for the garbage collector to kick in.
Destroying a widget causes GTK resources associated with the widget to be cleared immediately. The object itself can remain alive. Callbacks reachable through the widget should be dereferenced immediately and, provided nothing else holds on to them from Python, soon deallocated.
You can use the weak reference type from the weakref module to test this. For example:
>>> import gtk
>>>
>>> def report_death(obj):
... # arrange for the death of OBJ to be announced
... def announce(wr):
... print 'gone'
... import weakref
... report_death.wr = weakref.ref(obj, announce)
...
>>> def make_dynamic_handler():
... def handler():
... pass
... # for debugging - we want to know when the handler is freed
... report_death(handler)
... return handler
...
>>> w = gtk.Window()
>>> w.connect('realize', make_dynamic_handler())
10L
>>> w.destroy()
gone
Now, if you change the code to handler to include a circular reference, e.g. by modifying it to mention itself:
def handler():
handler # closure with circular reference
...the call to destroy will no longer cause gone to be immediately printed - that will require the program to keep working, or an explicit call to gc.collect(). In most Python and PyGTK programs automatic deallocation "just works" and you don't need to make an effort to help it.
Ultimately, the only reliable test whether there is a memory leak is running the suspect code in an infinite loop and monitoring the memory consumption of the process - if it grows without bounds, something is not getting deallocated and you have a memory leak.
I love both python and Qt, but it's pretty obvious to me that Qt was not designed with python in mind. There are numerous ways to crash a PyQt / PySide application, many of which are extraordinarily difficult to debug, even with the proper tools.
I would like to know: what are good practices for avoiding crashes and lockups when using PyQt and PySide? These can be anything from general programming tips and support modules down to highly specific workarounds and bugs to avoid.
General Programming Practices
If you must use multi-threaded code, never-ever access the GUI from a non-GUI thread. Always instead send a message to the GUI thread by emitting a signal or some other thread-safe mechanism.
Be careful with Model/View anything. TableView, TreeView, etc. They are difficult to program correctly, and any mistakes lead to untraceable crashing. Use Model Test to help ensure your model is internally consistent.
Understand the way Qt object management interacts with Python object management and the cases where this can go wrong. See http://python-camelot.s3.amazonaws.com/gpl/release/pyqt/doc/advanced/development.html
Qt objects with no parent are "owned" by Python; only Python may delete them.
Qt objects with a parent are "owned" by Qt and will be deleted by Qt if their parent is deleted.
Example: Core dump with PyQt4
A QObject should generally not have a reference to its parent or any of its ancestors (weak references are ok). This will cause memory leaks at best and occasional crashes as well.
Be aware of situations where Qt auto-deletes objects. If the python wrapper has not been informed that the C++ object was deleted, then accessing it will cause a crash. This can happen in many different ways due to the difficulty PyQt and PySide have in tracking Qt objects.
Compound widgets such as a QScrollArea and its scroll bars, QSpinBox and its QLineEdit, etc. (Pyside does not have this problem)
Deleting a QObject will automatically delete all of its children (however PyQt usually handles this correctly).
Removing items from QTreeWidget will cause any associated widgets (set with QTreeWidget.setItemWidget) to be deleted.
# Example:
from PyQt4 import QtGui, QtCore
app = QtGui.QApplication([])
# Create a QScrollArea, get a reference to one of its scroll bars.
w = QtGui.QWidget()
sa = QtGui.QScrollArea(w)
sb = sa.horizontalScrollBar()
# Later on, we delete the top-level widget because it was removed from the
# GUI and is no longer needed
del w
# At this point, Qt has automatically deleted all three widgets.
# PyQt knows that the QScrollArea is gone and will raise an exception if
# you try to access it:
sa.parent()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: underlying C/C++ object has been deleted
# However, PyQt does not know that the scroll bar has also been deleted.
# Since any attempt to access the deleted object will probably cause a
# crash, this object is 'toxic'; remove all references to it to avoid
# any accidents
sb.parent()
# Segmentation fault (core dumped)
Specific Workarounds / Bugs
Changing the bounds of QGraphicsItems without calling prepareGeometryChange() first can cause crash.
Raising exceptions inside QGraphicsItem.paint() can cause crashes. Always catch exceptions inside paint() and display a message rather than letting the exception proceed uncaught.
QGraphicsItems should never keep a reference to the QGraphicsView they live in. (weakrefs are ok).
Using QTimer.singleShot repeatedly can cause lockups.
Avoid using QGraphicsView with QGLWidget.
Practices for Avoiding Exit Crashes
QGraphicsItems that are not part of a QGraphicsScene can cause crash on exit.
QObjects that reference their parent or any ancestor can cause an exit crash.
QGraphicsScene with no parent can cause an exit crash.
The easiest way to avoid exit crashes is to call os._exit() before python starts collecting Qt objects. However, this can be dangerous because some part of the program may be relying on proper exit handling to function correctly (for example, terminating log files or properly closing device handles). At a minimum, one should manually invoke the atexit callbacks before calling os._exit().
Just adding to the point:
If you must use threads in your qt-based program, you really must disable the automatic garbage collector and do manual collections on the main thread (as described in http://pydev.blogspot.com.br/2014/03/should-python-garbage-collector-be.html) -- note that you should do that even if you make sure your objects don't have cycles (with a cycle you basically make your objects live until the python cyclic garbage collector bumps in, but sometimes if you have something as an exception it's possible that a frame is kept alive, so, in such a situation your object may still be kept alive longer than you anticipate)... in those cases, it's possible that the garbage collector bumps in in a secondary thread, which may cause qt to segfault (qt widgets must always be collected in the main thread).
Just for the reference, I post the comment from the author of PyQt to the answer by Luke: "It's rubbish."
I think it's important because somebody can jump into this post and remained perplexed with all these (nonexistent) "problems".
Emitting signals instead of synchronic UI control was the key to avoid problems for me during implementation of logic circuit simulator