Deadlock On Exit With PySide And QFileSystemWatcher

 class=

Last year Nokia started developing their own Python bindings for Qt, PySide, when they couldn’t persuade Riverbank Computing to relicense PyQt under a more liberal license. While developing DjangoDE I made the choice of which library to use configurable. When running under PyQt everything worked fine, but when using PySide the program hung on exit.

Using gdb to see where it was hanging points to QFileSystemWatcher, which has the following comment in the destructor.

Note: To avoid deadlocks on shutdown, all instances of QFileSystemWatcher need to be destroyed before QCoreApplication. Note that passing QCoreApplication::instance() as the parent object when creating QFileSystemWatcher is not sufficient.

The following code will demonstrate the issue.

import sys
#from PyQt4 import QtGui
from PySide import QtGui
app = QtGui.QApplication(sys.argv)
file_browser = QtGui.QTreeView()
file_model = QtGui.QFileSystemModel()
file_model.setRootPath("/")
file_browser.setModel(file_model)
file_browser.show()
sys.exit(app.exec_())

As the comment says, we need to ensure that the QFileSystemWatcher object, which is created by QFileSystemModel, is destroyed before QApplication. To do this we can connect to the lastWindowClosed and ensure that the QFileSystemModel is fully destroyed.

import gcn
def app_quit():
     global file_browser
     file_browser = None
     gc.collect()
app.lastWindowClosed.connect(app_quit)

It’s not clear why this code would work on PyQt and not PySide, but it is clearly related to the order the objects are deleted. Given the comment in the Qt documentation though you should probably not rely on it working on PyQt and ensure yourself that the QApplication is the last Qt object to be destroyed.

Want to read more like this? Follow me with your favourite feed reader (e.g. Feedly), or subscribe to my SubStack newsletter.

Comments