The QPower of QProcess

One of the more recent things I stumbled on was the power of QProcess within PySide/PySide2. For countless years I’ve been using Python’s Subprocess module to spin up external processes that have worked great, but when applications got bigger and optimization and threading becomes a priority, subprocess needs to be combined with the threading libraries to offload this which can be fiddly to abstract out, debug and maintain.

I’m a big fan of the Qt libraries, using PySide/PySide2 in 95% of applications and tools which I develop, mainly due to the highly powerful and flexible graphical user interface that comes with it, but more and more I found myself using it for everything from networking to threading and even file system management. The main draw for trying to keep everything in the Qt ecosystem is that it all fits together really well with its Slots and Signals, it’s highly tested and optimized for the platform it’s running on.

The QProcess is an excellent example of this Qt mindset where they wrapped the external process into something that fits so naturally and instinctively into a Qt pipeline where it can be run as a blocking process or offloaded to a thread and tied back into the main thread with signals.

from PySide import QtCore
 
proc = QtCore.QProcess()
proc.start("ping", ["www.google.com", "-n", "5"])
proc.waitForFinished()
stdOut = proc.readAllStandardOutput()
stdErr = proc.readAllStandardError()
 
print "Standard Out:"
print stdOut
print "Standard Error:"
print stdErr

In this example, the QProcess will ping www.google.com 5 times and it will block the main thread until the process is finished, before reading the standard output and standard error.

This is very similar to how it could be written in subprocess:

import subprocess

proc = subprocess.Popen(["ping", "www.google.com", "-n", "5"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdOut, stdErr = proc.communicate()

print "Standard Out:"
print stdOut
print "Standard Error:"
print stdErr

In this design pattern, there isn’t much gain over using QProcess vs Popen, although I have found it be more stable, I don’t have any anything more than anecdotal evidence to support this.

Where QProcess really shines is that the its can be run non-blocking and using signals to hook back to the main loop.

import sys
import functools
 
from PySide import QtCore
 
def _handleProcFinished(process, exitCode):
    stdOut = process.readAllStandardOutput()
    stdErr = process.readAllStandardError()
    print "Standard Out:"
    print stdOut
    print "Standard Error:"
    print stdErr
 
app = QtCore.QCoreApplication(sys.argv)
 
proc = QtCore.QProcess()
proc.finished.connect(functools.partial(_handleProcFinished, proc))
proc.start("ping", ["www.google.com", "-n", "5"])
 
sys.exit(app.exec_())

Here the process will ping back with process and its exit code when it finishes, allowing the main thread to carry on. This type of pattern is great for building a external process queue system or implementing a futures type of deisgn with an external data input.

It is important to note that a QCoreApplication/QAppliocation is needed to run this threaded code.

I hope this has shown you a gimplse into the power of QProcess!

– Geoff Samuel