Cooking with Gispo: QGIS Plugin Development in VS Code
In this guide we’ll set up a full working QGIS plugin development environment for VS Code on Windows.
This setup supports:
- Autocompletion for qgis and pyqt libraries
- Runtime debugging
- Tests
- VS Code test integration
- Test coverage
- Test debugging
- Linting and formatting
If you are developing a public QGIS plugin I recommend using QGIS Long term release for development and having that as a minimum QGIS version requirement.
This setup should work with “Standalone installer for long term release (continued with dependencies from old OSGeo4W)” and QGIS installed with “Long-term release in old OSGeo4W (continued with previous dependencies)”. The current installer also known as the new OSGeo4W V2 installer is not supported by this setup yet since at the time of the writing there are some issues with how python 3.9 is handling dll loading.
In this tutorial we’ll set up a development environment to work with the following project structure:
🗁 my-qgis-plugin
┣ 🗋.git
┣ 🗁 myqgisplugin
┃ ┣ 🗋metadata.txt
┃ ┣ 🗋plugin.py
┃ ┗ 🗋__init__.py
┣ 🗁tests
┃ ┣ 🗋test_plugin.py
┣ 🗋.gitignore
┣ 🗋LICENSE
┣ 🗋README.md
┣ 🗋requirements-dev.txt
┗ 🗋pyproject.toml
Creating new sample plugin
Create new plugin using cookiecutter-qgis-plugin template.
- Make sure you have git installed
- Install cookiecutter into some (virtual) python environment (you can create one just for cookiecutter)
>\bin\python-qgis-ltr.bat -m venv .venv > .venv\scripts\activate (.venv) > python -m pip install -U pip (.venv) > pip install cookiecutter
- The python-qgis-ltr.bat messes up with the environment (git is not in the path anymore), so it is a good idea to restart the command prompt at this point
- Create a new plugin and answer the questions as you wish
> cookiecutter https://github.com/GispoCoding/cookiecutter-qgis-plugin
VS Code startup
Right-click the plugin directory created in previous step and click Open with Code. If you don’t have the Python extension installed, install it at this point.
Setting up a virtual python environment
We’ll use a virtual python environment to install all our development tools and packages so those don’t interfere with the QGIS environment.
First add all the paths containing dll files inside QGIS installation directory to the user’s PATH environment variable.
<QGIS-INSTALLATION-FOLDER\>\bin
<QGIS-INSTALLATION-FOLDER\>\apps\qgis-ltr\bin
<QGIS-INSTALLATION-FOLDER\>\apps\Qt5\bin
With command prompt create a virtual python environment and save path to QGIS python libraries as .pth file by giving a commands:
# Navigate to the plugin folder > cd my-qgis-plugin >\bin\python-qgis-ltr.bat -m venv --system-site-packages .venv > \bin\python-qgis-ltr.bat -c "import pathlib;import qgis;print(str((pathlib.Path(qgis.__file__)/'../..').resolve()))" > .venv\qgis.pth
If you are running PowerShell, make sure to change the encoding of the .venv\qgis.pth from
UTF-16 LE
toUTF-8
.
Now with a different command prompt window activate the virtual python environment and upgrade pip by giving a commands:
> .venv\scripts\activate (.venv) > python -m pip install -U pip
If you are running PowerShell and get an error about
Activate.ps1 cannot be loaded because running scripts is disabled on this system
, you can run the following line and then try again: Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Set that just created environment as a Python Interpreter for this project.Ctrl+Shift+P
-> “Python: Select Interpreter”
If the .venv
interpreter is not showing in the list use “Enter interpreter path” > “Find” and browse for .venv/Scripts/python.exe
.
Install recommended development tools
With the virtual python environment activated install the recommended development tools.
(.venv) > pip install pytest pytest-qgis pytest-cov flake8 flake8-qgis black isort
Tool | Purpose |
---|---|
pytest | Test framework |
pytest-qgis | pytest plugin for making it easier to write QGIS plugin tests |
pytest-cov | Test coverage tool with pyproject.toml configuration support |
flake8 | Linting support |
flake8-qgis | Linting support for PyQGIS specific code |
black | Python code formatter |
isort | Sorts imports automatically as recommended |
You can also automate the linting and formatting to happen before committing via pre-commit tool. It can be installed with:
(.venv) > pip install pre-commit (.venv) > pre-commit install
It will automatically download the linting and formatting tools (configured in .pre-commit-config.yaml) and run those before each commit.
pytest config
You could configure pytest and coverage further by giving configurations in pyproject.toml
file. For example:
# pyproject.toml [tool.pytest.ini_options] addopts = "-v --tb=short --cov=myqgisplugin" testpaths = [ "tests" ] [tool.coverage.run] omit="myqgisplugin/somepackage/*"
flake8 configuration
We must set up few settings for flake8 so it is black compatible. Flake8 doesn’t yet support pyproject.toml
so we must use its own .flake8
configuration file.
# .flake8 max-line-length = 88 extend-ignore = E203
isort configuration
For isort to be black compatible we must set up it to use black profile.
# pyproject.toml [tool.isort] # Black compatible values for isort https://black.readthedocs.io/en/stable/compatible_configs.html#isort profile = "black" multi_line_output = 3
Project Settings
Paste the following settings to the workspace settings file: ./.vscode/settings.json
.
{ "python.languageServer": "Jedi", "python.linting.enabled": false, "python.linting.flake8Enabled": true, "python.formatting.provider": "black", "editor.formatOnSave": true, "[python]": { "editor.codeActionsOnSave": { "source.organizeImports": true } }, "python.testing.unittestEnabled": false, "python.testing.nosetestsEnabled": false, "python.testing.pytestEnabled": true, "files.associations": { "metadata.txt": "ini" } }
Note 1: Currently the only one language server supporting autocompletion for qgis and PyQt5 libraries is the
Jedi
language server. Qgis and PyQt5 packages are using compiled python code and other language servers are having troubles parsing the API from those libraries (Actually this is for security reasons).
In the following QGIS releases python stub files that describes the API are included in theqgis
package so also much betterPylance
language server can be then used. If you have*.pyi
files inC:/OSGeo4W64/apps/qgis-ltr/python/qgis
go with thePylance
language server.
Note 2: If you choose to
add the vscode config
in Cookiecutter template a./myqgisplugin.code-workspace
file will be created to the root. You can use it instead of manually copying the settings.
Testing
With the above settings VS Code should be able to discover your tests automatically.
Deploying the plugin
The main plugin lies in the myqgisplugin directory (name differs if you chose a different name in cookiecutter). To make the plugin available for QGIS, it has to has access to that directory (or copy of it).
1) Copying the plugin manually You can copy the directory manually to C:/Users//AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins everytime you do changes and then install / reload the plugin with QGIS. You can also utilize tools like qgis_plugin_tools or pb_tool to accomplish this in a easier way.
3) Using symbolic link With symbolic links you can achieve the same result of having the code in QGIS plugin folder but you don’t have to worry about doing the copying again each time you change the code. There is a huge risk though of losing your code if you uninstall the plugin by accident.
2) Modifying QGIS_PLUGINPATH Other alternative is to modify the environment variable QGIS_PLUGINPATH
to make QGIS search the plugins from different directories. This way you can have QGIS find the plugin straight from your development environment. As with the upper method, there is a hugre risk of losing your code if you uninstall the plugin.
Starting the plugin
Now that the plugin is deployed, it can be started by installing it from QGIS Plugins > Manage and Install Plugins.

To use the plugin, just open the Python console first and the click Plugins > MyQGISPlugin > MyQGISPlugin and a friendly text should be printed at the bottom of the console.

Debugging
With VS Code debugging works so that you should start a debugging server on QGIS side and then attach to it in VS Code.
There are two alternative ways to start the debugging server on QGIS. Either use a Debugvs -plugin or add a small code snippet to your plugin code.
1) Debugvs plugin
Install the Debugvs plugin from QGIS plugin repository. Debugvs needs a ptvsd
python package available so install it first.
In your Osgeo4W Shell (C:\OSGeo4W64\OSGeo4W.bat
).
> py3_env > python3 -m pip install ptvsd
If you get an error that you don’t have module pip then install it first by downloading get-pip.py and executing it with python3 get-pip.py
ptvsd
is though now deprecated project and the recommended debugging library is now debugpy
. There are not yet any QGIS plugin for debugpy.
2) Code snippet in plugin code
This method is using the new debugpy
library.
In your Osgeo4W Shell (C:\OSGeo4W64\OSGeo4W.bat
).
> py3_env > python3 -m pip install debugpy
Insert the following code in to the __init__.py
file of your plugin’s main package (if you are using qgis_plugin_tools)
Note: If you are using qgis_plugin_tools, you don’t have to insert the following code, just add the following environment variable to enable debugging:
QGIS_PLUGIN_USE_DEBUGGER=debugpy
.
import os import shutil if os.environ.get("QGIS_DEBUGPY_HAS_LOADED"): try: import debugpy debugpy.configure(python=shutil.which("python")) debugpy.listen(('localhost', 5678)) except Exception as e: print("Unable to create debugpy debugger: {}".format(e)) else: os.environ["QGIS_DEBUGPY_HAS_LOADED"] = "1"
Debug launch configuration
To launch the debugging, create the following launch configuration in file ./.vscode/launch.json
.
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Python: Remote Attach", "type": "python", "request": "attach", "connect": { "host": "localhost", "port": 5678 }, "pathMappings": [ { "localRoot": "${workspaceFolder}/myqgisplugin", "remoteRoot": "C:/Users/${env:USERNAME}/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins/myqgisplugin" } ], "justMyCode": false, }, { "name": "Debug Tests", "type": "python", "request": "test", "console": "integratedTerminal", "justMyCode": false, "env": { "PYTEST_ADDOPTS": "--no-cov" } } ] }
The first configuration is for attaching to the debugging server running on QGIS side.
The second makes sure that tests can be debugged in VS Code even if using testing coverage.
Note: Cookiecutter’s
./myqgisplugin.code-workspace
file also has these configurations set if you choose to include vs code configs.
To start the debugging:
- Start QGIS
- Launch the
Python: Remote Attach
launch configuration - Put some breakpoints and start the plugin from QGIS
Recommended VS Code extensions
- EditorConfig for VS Code