When using QProcess to run external programs from your PyQt5 application, you'll often need to launch a program that lives in a completely different folder from your Python script. Maybe the external program also depends on input files or configuration in its own directory. In that case, you need to tell QProcess both where the program is and what directory it should run in.
The problem
Suppose you have an external program at C:/program_folder/test.exe, and your PyQt5 application lives at C:/qt_source/test.py. The external program expects to find its input files in C:/program_folder/.
You might try something like this:
p = QProcess()
p.setWorkingDirectory('C:/program_folder/')
p.start('test.exe')
This looks reasonable, but it won't work. The issue is that setWorkingDirectory() sets the working directory for the process once it's running — it doesn't affect how Qt searches for the executable itself. When you pass just test.exe to start(), Qt doesn't know where to find it.
The solution
You need to provide the full path to the executable so Qt can locate it, and set the working directory so the process runs in the correct folder. The cleanest way to do this is with setProgram() and setWorkingDirectory() together, then call start() with no arguments:
p = QProcess()
p.setProgram('C:/program_folder/test.exe')
p.setWorkingDirectory('C:/program_folder')
p.start()
By using setProgram() with the full path, Qt knows exactly which executable to launch. And setWorkingDirectory() ensures that once the program is running, its current directory is C:/program_folder/ — so any relative file paths the program uses will resolve correctly.
Why does this happen?
In older versions of Qt, you could pass the program name and arguments as a single string to start():
p.start('test.exe') # Deprecated approach
This form of start() is now deprecated. The current API expects you to either:
Purchasing Power Parity
Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]- Use
setProgram()and (optionally)setArguments()before callingstart(), or - Pass the program and arguments separately to
start():
p.start('C:/program_folder/test.exe', [])
The empty list [] represents the arguments — in this case, none. You must always pass arguments as a separate list.
Mixing the deprecated start('program') call with setWorkingDirectory() is what causes the confusion. For a deeper look at how QProcess works and how to handle external programs in your Qt applications, see our complete guide to QProcess.
A complete example
Here's a small working example that launches an external program from a specific directory, with a button to trigger the process. This example uses signals and slots to connect the process output to the UI:
import sys
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QPushButton, QTextEdit, QVBoxLayout, QWidget,
)
from PyQt5.QtCore import QProcess
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("QProcess Working Directory Example")
self.text_output = QTextEdit()
self.text_output.setReadOnly(True)
self.button = QPushButton("Run External Program")
self.button.clicked.connect(self.run_process)
layout = QVBoxLayout()
layout.addWidget(self.button)
layout.addWidget(self.text_output)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
self.process = None
def run_process(self):
if self.process is not None:
return # Already running
self.process = QProcess()
self.process.setProgram("C:/program_folder/test.exe")
self.process.setWorkingDirectory("C:/program_folder")
self.process.readyReadStandardOutput.connect(self.handle_stdout)
self.process.readyReadStandardError.connect(self.handle_stderr)
self.process.finished.connect(self.process_finished)
self.process.start()
def handle_stdout(self):
data = self.process.readAllStandardOutput()
text = bytes(data).decode("utf-8")
self.text_output.append(text)
def handle_stderr(self):
data = self.process.readAllStandardError()
text = bytes(data).decode("utf-8")
self.text_output.append(text)
def process_finished(self):
self.text_output.append("Process finished.")
self.process = None
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
Update the path in setProgram() and setWorkingDirectory() to match your actual program location, and you're good to go.
Passing arguments
If your external program needs command-line arguments, use setArguments() with a list:
p = QProcess()
p.setProgram("C:/program_folder/test.exe")
p.setArguments(["--input", "data.txt", "--verbose"])
p.setWorkingDirectory("C:/program_folder")
p.start()
Each argument is a separate string in the list. Qt handles the quoting and escaping for you, so you don't need to worry about spaces in filenames or other shell-related headaches. If you also need to handle command-line arguments passed to your own PyQt5 application, that's handled separately through QApplication.
Summary
When launching external programs with QProcess from a different directory:
- Use
setProgram()with the full path to the executable. - Use
setWorkingDirectory()to set the directory the process should run in. - Call
start()with no arguments. - If you need to pass arguments, use
setArguments()with a list of strings.
This approach avoids the deprecated single-string form of start() and ensures the working directory is applied correctly every time. Once your application is ready for distribution, you can package it with PyInstaller to create a standalone executable.
Create GUI Applications with Python & Qt6 by Martin Fitzpatrick — (PyQt6 Edition) The hands-on guide to making apps with Python — Save time and build better with this book. Over 15K copies sold.