34. Processes
A process is the instance of a program that is being executed by one or many threads. The operating system keeps its processes separate and allocates the resources (memory, CPU time, file handles, network connections) they need. Each process runs in its own memory space, meaning one process cannot directly read or modify the memory of another process1.
Running external processes allows a program to delegate work to existing tools instead of reimplementing their functionality. Communicating between processes on the same computer typically happens through standard streams (stdin, stdout, stderr), pipes, sockets, or shared files. A parent process is a process that has created one or more child processes2.
In Qt, both creating external processes and communicating with them is handled by the QProcess class.
In this chapter we provide several examples of QProcess usage, starting with simple program execution and communicating via standard stream channels, before proceeding to setting a process environment, changing the process working directory, synchronous process execution, and terminating a running process.
34.1 Basic Process Execution
To start a process, create an object of this class and call its start() method:
1 process.start(program, arguments, mode)
where:
programis the executable name.argumentsis a list of command line arguments.modeis aQIODevice.OpenMode,ReadWriteby default.
After creating a QProcess, it can be in one of three states:
| Constant | Value | Description |
|---|---|---|
QProcess::NotRunning |
0 | The process is not running. |
QProcess::Starting |
1 | The process is starting, but the program has not yet been invoked. |
QProcess::Running |
2 | The process is running and is ready for reading and writing. |
![]() |
You are building a system monitoring tool that runs external commands such as |
To start an external process:
-
Create a
QProcessobject and connect its signals.pingcommand line arguments are different between Windows and Unix derived operating systems, so check on which platform the program is running and set the arguments accordingly - on Windows just set the target (‘google.com’) as the command exits automatically after four pings. Otherwise, set the target, the switch to limit the number of packets (‘-c’), and the number of packets (‘4’). Create aQProcessobject and connect its signals to the slots:readyReadStandardOutput()is emitted when there is data available throughstdout.readyReadStandardError()is emitted when there is data available through the standard error channel,stderr.errorOccurred(), emitted when an error occurs with the process.stateChanged(), emitted when the state of the process changed.finished(), emitted when the process finishes. The difference between thereadyReadStandardError()anderrorOccurred()is thatreadyReadStandardError()is emitted when the process itself writes data to its error stream, whileerrorOccurred()is emitted when a process-level failure occurs, such as the executable not being found or the process crashing.
Start the external process. When the user clicks the ‘RunCommand’ button, disable the button, clear the log widget (a
QTextBrowser), and callQProcess.start(), passing it the executable name (ping) and its command line arguments.-
Handle signals:
log_stdout()reads all standard output data, decodes it and appends the output to the log widget.log_stderr()reads all standard error data, decodes it and appends it to the log.log_error()appends the error code and error string to the log.log_state_change()logs process state changes.log_finished()logs the process exit status and enables the ‘Run Command’ so the process can be run again.
When you run the application the following window is shown:

If the command runs successfully the log should look like this:
1 Running process: ping
2 Argument(s): ['google.com', '-c', '4']
3 State changed: ProcessState.Starting
4 State changed: ProcessState.Running
5 stdout: ...
6 ...
7 State changed: ProcessState.NotRunning
8 Process finished!
9 Exit code: 0
10 Exit status: ExitStatus.NormalExit
The process state changes from Starting to Running, stdout is captured, the state changes to NotRunning and the process exits.
If there is an error in the QProcess setup, like
1 self.executable = 'nonexistent'
the output is different:
1 Running process: nonexistent
2 Argument(s): ['google.com', '-c', '4']
3 State changed: ProcessState.Starting
4 State changed: ProcessState.NotRunning
5 Error Code: ProcessError.FailedToStart
6 Error: Child process set up failed: execve: No such file or directory
The process state changes from Starting to NotRunning, and errorOccurred() is emitted with the FailedToStart error code.
If the executable exists, but you pass it an invalid command line argument:
1 self.arguments = ['google.com', '-c', 'invalid']
The output is:
1 Running process: ping
2 Argument(s): ['google.com', '-c', 'invalid']
3 State changed: ProcessState.Starting
4 State changed: ProcessState.Running
5 stderr: /usr/bin/ping: invalid argument: 'invalid'
6
7 State changed: ProcessState.NotRunning
8 Process finished!
9 Exit code: 1
10 Exit status: ExitStatus.NormalExit
The process runs but the invalid argument causes the process to fail so it outputs a message through stderr and exits.
34.2 Providing Fixed Input to a Process
Some processes are interactive by nature: they prompt the user for input and proceed based on the responses. In such cases QProcess allows you to write data directly to a child precess stdin, so you can feed predefined values programmatically as if a user had typed them.
![]() |
You are building a GUI tool that needs to run an interactive Python script that prompts for a project name, author, and Python version and creates a Python project based on the entered values. Instead of prompting the user each time, you want to feed predefined values via stdin automatically. |
Suppose you have a third-party Python script that prompts the user for some values:
Instead of typing responses at a terminal, you want to feed all inputs automatically via stdin. (The above script does nothing but prints strings to the terminal, simulating actual processing with time.sleep().)
To provide a fixed input to a process:
1.Create a QProcess and connect its signals. Similarly to the previous example, connect readyReadStandardOutput(), readyReadStandardError() and errorOccurred() to the slots that log the child process output to a QTextEdit. Connect stateChanged() to on_state_changed() which is responsible for feeding the predefined input values to the child process.
Start the process. Use
QProcess.start()to run the Python executable, passing-uas the first argument followed by the script path. The-uflag forces stdout and stderr to be unbuffered. Python buffers stdout by default when it detects it isn not attached to a real terminal -without it, all output would arrive in a single burst at process exit instead of line by line.Write the predefined input values. In
on_state_changed()check whether the process state isRunning, then useQProcess.write()to write each input value to the child process stdin. The values must bebytesobjects with a trailing newline (b'my_app\n',b'Joe\n',b'12\n'), matching what a user would type at a terminal. Eachinput()call in the child script reads the next line from the stdin buffer as it is needed - all values can be written upfront because the OS buffers them untill they are consumed. Finally, callQProcess.closeWriteChannel()to signal EOF.
When you run the application and start the child process, the end result should look like this:

34.3 Handling Chunked Output: A Common Pitfall
Python, like many other languages, buffers its stdout differently depending on whether it is writing to a terminal or to a pipe. When stdout is connected to a real terminal, Python uses line buffering, flushing after each newline. When connected to a pipe, it uses block buffering, holding output untill the buffer fills up or the process exits. When you launch a child process via QProcess Qt sets up two pipes automatically: one connecting your application to the child process stdin, and another connecting the child’s stdout back to your application. This means that you can not expect the child process output to arrive in complete, newline-separated chunks.
![]() |
You are creating a logging application that streams output from a long-running external command. Handle partial chunks to avoid displaying garbled or incomplete lines in your UI. |
You are dealing with a program that sends long strings to its stdout:
The full output is close to 800 characters spread across five repeated sentences. It deliberately splits its output into 20-byte chunks, calling sys.stdout.flush(), which forces readyReadStandardOutput() to be emitted on every chunk. When you read the output in the main application you need to buffer those chunks untill you encounter a newline character and only then display the complete line.
To handle chunked output from an external process:
-
Create a
QProcessobject, connect the signals, and run the child process. When the user clicks the ‘Run with Buffering’ button:- Initialize an empty string buffer to accumulate incoming output chunks.
- Create a
QProcess. - Connect the
readyReadStandardOutput()tohandle_stdout_buffered()andfinished()tofinished_buffered(). - Start the process.
Read the child process stdout data, decode it, and append it to the buffer. When new data is available, use
readAllStandardOutput()to read all currently available bytes , decode from UTF-8, and the result to the buffer string.Check whether the buffer contains any complete lines and display them. Split the buffer
os.linesep. Every element except the last one is a complete line so you append it to the text box and discard it. Keep the last element in the buffer, as it is a partial line that has not yet been terminated by a newline.When the child process finishes, flush any remaining content from the buffer. If the buffer is not empty, after the process exits, append it to the text edit as there won’t be no further output from the child process.

For comparison, running the same child process without buffering produces output like this, where each readyReadStandardOutput() signal delivers a raw 20-byte chunk, splitting sentences at arbitrary boundaries:

34.4 Interactive Process Communication
The previous two sections covered one-directional communication - feeding a process predefined input or reading its output as it arrives. Some processes, however, are designed to stay alive and accept input repeatedly, responding to each command before waiting for the next one. Communicating with such a process requires keeping the write channel open and writing to stdin on demand, in response to user actions.
![]() |
You are developping an embedded Python REPL inside your Qt application. |
To establish interactive process communication:
-
Create the process object, connect the signals, and start the process. Add a
QLineEditto the main window to let the user send commands to the Python shell, and aQTextBrowserto display the shell output. ConnectQLineEdit.returnPressed()tosend_input(). ConnectreadyReadStandardOutput()andreadyReadStandardError()signals to the slots. Start the Python executable with the following flags:-uto force the stdout and stderr streams to be unbuffered.-ito enter interactive mode, keeping the shell alive and waiting for input.qto suppress the copyright and version banner on startup. Once thestartedsignal is emitted, the Python interactive shell is ready to accept commands. Enable theQLineEditand give it focus.
Send user input to the process. In
send_input(), read the command fromQLineEdit.text(). Send the command to the process viaQProcess.write(), appending a newline to submit it, then clear the line edit.Handle the process output. in
handle_stdout()andhandle_stderr(), read and decode the available data and apply the same line-buffering technique from the previous section - accumulate chunks until a newline is found, then display the complete line. Display stderr output in red using an HTML<font>tag.Terminate the process cleanly. In
closeEvent(), if the process is still running, sendexit()to the shell and give it a short time to finish withwaitForFinished(). If it has not exited by then, callQProcess.kill()to terminate it before allowing the window to close.

Note that communication is asynchronous in both directions. If you send the shell a long-running command, the UI remains responsive and you are still able to continue sending commands to it. Output lines appear in the text browser as they arrive rather than all at once when the command completes.
34.5 Setting Process Environment
Bu default, a child process launched via QProcess inherits the environment of the parent process. Sometimes this is not whast you want - you may need to set additional environment variables or isolate the process from the parent’s environment entirely. QProcess lets you construct a custom environment assign it to the process before it starts.
![]() |
You are building a tool that lets the user run Qt applications with a different style and scale factor. |
To set the process environment:
Get the system environment.
QProcessEnvironmentholds the set environment variables that can be passed to the child process. Use itssystemEnvironment()static method to obtain a copy of the current process environment. Add two checkboxes to the main window: one to override the Qt style toWindows, and one to set the scale factor to 1.5.-
Update environment variables when the checkboxes change. A process environment is a set of
key=valuepairs. UseQProcessEnvironment.insert()to set a variable andQProcessEnvironment.remove()to clear it:- Insert
QT_STYLE_OVERRIDE=windowswhen the style checkbox is checked, remove it when unchecked. - Insert
QT_SCALE_FACTOR=1.5when the scale checkbox is checked, remove it when unchecked.
Apply the environment to the process. Call
QProcess.setProcessEnvironment()to assign the environment before starting the process.
With this the user is able to launch the child Qt application with a different style and scale factor.
We are using this simple PySide6 demo for testing the application:
When you change the environment variables from the main application and run it, the test app should reflect the changes - appearing with the Windows style when the style checkbox is checked, rendered at 1.5x size when the scale checkbox is checked, or both at once if both are checked.

34.6 Synchronous Process Execution
All the previous examples in this chapter use asynchronous process exexution: start a process, connect signals and let the event loop handle the rest. Sometimes, however, you want to vait for a process to finish before proceeding. Common scenarios include:
- Checking for external tool availability - verifying that
git,ffmpeg, or some other dependency present before your application continues. - Sequential operations - step B depends on the output or exit code of step A.
- Short fast operations - the process is known to complete in milliseconds.
All of the above can also be achieved asynchronously, but synchronous execution trades UI responsiveness for simpler code.
![]() |
You are building a utility that quickly checks for presence of external tools such as |
To execute a process synchronously:
Start the process and wait for it to start. Call
QProcess.start()to launch the process, then immediatelly callQProcess.waitForStarted().QProcess.start()is not guaranteed to be synchronous - on Linux, process creation doesfork()andexec(), which makes it asynchronous, so the process may still be in theStartingstate by the timestart()returns. On Windows the transition toRunning(orNotRunning) may happen beforestart()returns, but this is not guaranteed either.waitForStarted()is a reliable way to know the process has either started or failed to start, regardless of the platform. If it returnsFalse, the process failed to start.Wait for the process to finish. Call
QProcess.waitForFinished()to block until the process exits. At this point the output is available and the exit code can be checked.

Note that both waitForStarted() and waitForFinished() block the event loop and the UI will become non-responsive while waiting. This is acceptable for fast operations like a version check, but for anythong that might take more than that, the asynchronous approach is preferred.
34.7 Terminating Processes
A long-running child process may need to be stopped before it finishes normally, maybe if the user wants to cancel it or the process has hung. QProcess provides two ways to stop a running process:
QProcess.terminate()asks the process to stop gracefully. On Linux it sendsSIGTERM, giving the process a chance to clean up before exiting. On Windows it posts aWM_CLOSEmessage, which terminal processes likepingtypically ignore.- QProcess.kill(). Kills the process, causing it to exit immediatelly. On WIndows,
kill()usesTerminateProcess, and on Linux, theSIGKILLsignal is sent to the process.
![]() |
You are building a task manager that starts a long-running process (such as |
To terminate a running process:
Start the process and track its state.
Try to terminate the process gracefully. When the user clicks the ‘Stop’ button, call
QProcess.terminate()torequest a graceful shutdown, then callwaitForFinished()with a short timeout to give the process a chance to exit cleanly.If necessary, kill the process. If
waitFOrFinish()returnsFalse, the process did not exit within the timeout. CallQProcess.kill()andwaitForFinished()to make sure the process has ended.Handle process termination on window close. Override
closeEvent()to apply the same pattern to make sure no orphaned child processes are left running after the application exits.

