Running Python Scripts Outside QGIS Desktop
To run Python scripts outside the QGIS desktop, you must manually bootstrap the PyQGIS environment before importing any qgis modules. This requires setting QGIS_PREFIX_PATH, PATH, and PYTHONPATH to your QGIS installation directory, then calling QgsApplication.initQgis() in headless mode (False). This bypasses the GUI while retaining full access to vector/raster APIs, GDAL/OGR drivers, and the Processing framework.
For reproducible deployments, isolate these dependencies to prevent system-level conflicts. A properly configured Virtual Environments for GIS ensures your standalone interpreter resolves QGIS libraries consistently across development and production machines. The foundational mapping between Python interpreters and QGIS binaries is detailed in the PyQGIS Fundamentals & Environment Setup reference, which covers version alignment and dependency resolution.
Working Code Snippet
import os
import sys
# 1. Set QGIS installation root (adjust per OS)
QGIS_PREFIX = r"C:\OSGeo4W\apps\qgis" # Windows OSGeo4W
# macOS: "/Applications/QGIS.app/Contents/MacOS"
# Linux: "/usr"
# 2. Inject environment variables BEFORE importing qgis modules
os.environ["QGIS_PREFIX_PATH"] = QGIS_PREFIX
os.environ["PATH"] = os.pathsep.join([os.path.join(QGIS_PREFIX, "bin"), os.environ.get("PATH", "")])
os.environ["PYTHONPATH"] = os.pathsep.join([os.path.join(QGIS_PREFIX, "python"), os.environ.get("PYTHONPATH", "")])
# Prepend to sys.path (required for some installations)
sys.path.insert(0, os.path.join(QGIS_PREFIX, "python"))
from qgis.core import QgsApplication, QgsVectorLayer
# 3. Initialize headless QGIS application
QgsApplication.setPrefixPath(QGIS_PREFIX, True)
qgs = QgsApplication([], False) # False disables GUI
qgs.initQgis()
# 4. Execute GIS logic
layer = QgsVectorLayer("data/roads.shp", "roads", "ogr")
if layer.isValid():
print(f"Loaded {layer.featureCount()} features")
else:
print("Layer failed to load. Check path and OGR drivers.")
# 5. Clean shutdown
qgs.exitQgis()
Compatibility Requirements
| Component | Requirement | Failure Symptom |
|---|---|---|
| Python Version | Must exactly match QGIS bundled version (e.g., 3.10 for QGIS 3.34+) | ImportError: DLL load failed or ModuleNotFoundError: qgis |
| QGIS Major Version | Scripts must target the exact installed major.minor release | AttributeError: module 'qgis.core' has no attribute 'QgsApplication' |
| Path Separators | Use os.pathsep and os.path.join to avoid OS-specific crashes | FileNotFoundError during setPrefixPath() |
| macOS Bundle | Point QGIS_PREFIX_PATH to Contents/MacOS, not the .app root | QgsApplication initializes but fails to load providers |
| Linux Headless | Export QT_QPA_PLATFORM=offscreen before execution | qt.qpa.xcb: could not connect to display crash |
Execution & Integration
- Direct CLI:
python standalone_script.py - Scheduled Tasks/Cron: Wrap execution in a batch/shell script that exports environment variables first, then invokes Python.
- Processing Framework: To use
processing.run(), register native algorithms after initialization:
from qgis.analysis import QgsNativeAlgorithms
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
Troubleshooting & Fallbacks
If initialization fails or crashes silently, apply these diagnostic steps:
- Verify Path Resolution: Print
sys.executableandos.environ["QGIS_PREFIX_PATH"]immediately after assignment. A mismatched interpreter is the most common cause of missingqgismodules. - Use
qgis_process(Recommended): QGIS 3.14+ ships with a standalone CLI that auto-configures environments. Runqgis_process run --script=your_script.pyto bypass manual path injection entirely. This is the most reliable fallback for CI/CD pipelines. - Conda Environment Fallback: If system paths are unstable or permissions restrict
PATHmodification, useconda-forge:conda create -n qgis-standalone qgis python=3.10. Activate the environment and run your script. Conda automatically resolves GDAL, PROJ, Qt, and SIP bindings. - Enable Debug Logging: Add
QgsApplication.messageLog().logMessage("Init check", "Standalone")and run withQGIS_LOG_FILE=debug.logto capture silent provider load failures. - GDAL Driver Registration: If formats fail to open, explicitly call
from osgeo import gdal; gdal.UseExceptions()and verifyQGIS_PREFIX_PATHincludes thegdalpluginsdirectory.
Standalone execution removes GUI overhead while preserving the full spatial engine. Always validate environment variables at runtime, match Python versions exactly, and prefer qgis_process or Conda when manual path mapping proves unstable.