Skip to content

Instantly share code, notes, and snippets.

@NicolaiLolansen
Last active June 18, 2024 17:15
Show Gist options
  • Save NicolaiLolansen/acfd8723720c4761aefef3cdfc2aa55a to your computer and use it in GitHub Desktop.
Save NicolaiLolansen/acfd8723720c4761aefef3cdfc2aa55a to your computer and use it in GitHub Desktop.
Debugging QGIS 3.x python plugins on Windows using VS Code

A windows version inspired by this lovely gist: https://gist.github.com/AsgerPetersen/9ea79ae4139f4977c31dd6ede2297f90

Debugging QGIS 3.x python plugins on Windows 10 using VS Code

Plugin

In QGIS install the plugin debugvs.

Python dependencies

The debugvs plugin needs the python module ptvsd to function. This module is not installed by default.

In principle you just pip install ptvsd in the python interpreter used by QGIS.

I am using the OSGEO installation of QGIS, that comes with both ltr and dev versions of QGIS. Installing the plugin is not as straight forward as it might seem, and there are multiple options available to you. I'll share what i did here, it involves changing the settings.json file in the .vscode folder.

Setting up VSCode

Before we set up the debugger configuration, we are going to setup the VScode python interpreter for the project. Firstly my file looks like this:

settings.json

{   
    // I like changing colors for my projects. QGIS projects are usually green. 
    "workbench.colorCustomizations": {
        "titleBar.activeBackground": "#006823",
        "editor.background": "#1c2c2a"
    },
    
    "python.linting.pylintEnabled": true,
    "python.jediEnabled": true,

    // Wait.. this isen't a python interpreter..
    "python.pythonPath": "C:/OSGeo4W64/bin/python-qgis.bat",

    // Some arguments to make pylint behave nicely with PyQt5 and qgis bindinds
    "python.linting.pylintArgs": [
        "--extension-pkg-whitelist=PyQt5,db_manager",
        "--disable=all",
        "--enable=F,E,unreachable,duplicate-key,unnecessary-semicolon,global-variable-not-assigned,unused-variable,binary-op-exception,bad-format-string,anomalous-backslash-in-string,bad-open-mode"
    ],

    // Replicate the QGIS environment on the terminal
    // {{INSERT_YOUR_USER}} or any path really should be replaced with your own installation path
    "terminal.integrated.env.windows": {
        // Path
        "PATH": "C:\\Program Files\\Git\\cmd;C:\\Users\\{{INSERT_YOUR_USER}}\\AppData\\Local\\GitHubDesktop\\bin;C:\\OSGEO4~1\\apps\\qgis\\bin;C:\\OSGEO4~1\\apps\\Python37;C:\\OSGEO4~1\\apps\\Python37\\Scripts;C:\\OSGEO4~1\\apps\\qt5\\bin;C:\\OSGEO4~1\\apps\\Python27\\Scripts;C:\\OSGEO4~1\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\system32\\WBem;C:\\OSGEO4~1\\apps\\Python37\\lib\\site-packages\\pywin32_system32;C:\\OSGEO4~1\\apps\\Python37\\lib\\site-packages\\numpy\\.libs",
        // Python
        "PYTHONHOME": "C:\\OSGEO4~1\\apps\\Python37",
        "PYTHONPATH": "C:\\OSGEO4~1\\apps\\qgis\\python;%PYTHONPATH%",
        // GDAL
        "GDAL_DATA": "C:\\OSGEO4~1\\share\\gdal",
        "GDAL_DRIVER_PATH": "C:\\OSGEO4~1\\bin\\gdalplugins",
        "GDAL_FILENAME_IS_UTF8": "YES",
        // GeoTIFF
        "GEOTIFF_CSV": "C:\\OSGEO4~1\\share\\epsg_csv",
        // Qt
        "O4W_QT_BINARIES": "C:/OSGEO4~1/apps/Qt5/bin",
        "O4W_QT_DOC": "C:/OSGEO4~1/apps/Qt5/doc",
        "O4W_QT_HEADERS": "C:/OSGEO4~1/apps/Qt5/include",
        "O4W_QT_LIBRARIES": "C:/OSGEO4~1/apps/Qt5/lib",
        "O4W_QT_PLUGINS": "C:/OSGEO4~1/apps/Qt5/plugins",
        "O4W_QT_PREFIX": "C:/OSGEO4~1/apps/Qt5",
        "O4W_QT_TRANSLATIONS": "C:/OSGEO4~1/apps/Qt5/translations",
        "QT_PLUGIN_PATH": "C:\\OSGEO4~1\\apps\\qgis\\qtplugins;C:\\OSGEO4~1\\apps\\qt5\\plugins",
        // QGIS
        "QGIS_PREFIX_PATH": "C:/OSGEO4~1/apps/qgis",
        // Cache
        "VSI_CACHE": "TRUE",
        "VSI_CACHE_SIZE": "1000000"
    },
    "python.languageServer": "Jedi", // PyLancer just came out recently, but i havent tested it out with this configuration
}

The bread and butter of this configuration is "python.pythonPath": "C:/OSGeo4W64/bin/python-qgis.bat", which sets all the correct paths and bindings for us. I don't really know why it works to refer a .bat file as python interpreter, but apparently it's fine and fixes alot of linting issues.

The "terminal.integrated.env.windows": { configuration is basically the same idea, but i have copied alot of the contents from this file C:/OSGeo4W64/bin/python-qgis.bat. It makes the terminal in VScode aware of the QGIS python interpreter.

Now you can reload your VSCode window, and once the terminal starts up it will ask you to allow this terminal configuration to modify your terminal, press yes. After this happens you should now be able to get the exact same python interpreter that the OSGEO version of QGIS uses. If you use the ltr version, change all references to the ltr files.

Now you can pip install ptvsd inside your terminal. It's a good idea to verify the interpreter first by doing python to get an interactive session.

Launch.json

The default remote debugger configuration in VS Code looks like this

{
    "name": "Python: Remote Attach",
    "type": "python",
    "request": "attach",
    "port": 5678,
    "host": "localhost",
    "pathMappings": [
        {
            "localRoot": "${workspaceFolder}",
            "remoteRoot": "."
        }
    ]
},

I had to do two things to make plugin development work. I create a symlink between my Git folder and the plugin folder like this:

  • Open an elevated cmd.exe
  • Create a symlink mklink /D "C:\Users\---\AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins\your_plugin" "C:\Github\your_plugin_workspace\your_plugin"

Now you dont have to open VSCode inside the plugin folder ( you can if you want... )

Now change the pathMappings so the remote debugger knows where your files live

{
    "name": "Python: Remote Attach",
    "type": "python",
    "request": "attach",
    "port": 5678,
    "host": "localhost",
    "pathMappings": [
        {
            "localRoot": "${workspaceFolder}/your_plugin", // path to your plugin where you are developing
            "remoteRoot": "C:\\Users\\---\\AppData\\Roaming\\QGIS\\QGIS3\\profiles\\default\\python\\plugins\\your_plugin" // path to where the QGIS plugin folder lives. Only works if a symbolic link has been created.
        }
    ]
},

Now you should be ready to debug!

Debugging

  • In QGIS click Plugins -> Enable Debug for Visual Studio -> Enable Debug for Visual Studio
  • You should now see a message in the QGIS message bar saying something like DebugVS : Run the Debug in Visual Studio(Python:Attach)
  • In VS Code start debugging using the Python: Remote Attach configuration defined above.

Now you should be able to set breakpoints in VS Code.

Symlinking

You can symlink your plugin folder directly into the plugin folder of QGIS, so you don't have to change folder when developing your plugin.

In windows this is done like so:

  1. Stand in the folder of your plugin, we call this localRoot
  2. Open cmd.exe
  3. Run mklink /D C:\Users\nicolai\AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins\example_plugin C:\Users\nicolai\Desktop\Github\Qgis_boilerplate\example_plugin
  4. C:\Users\nicolai\AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins\boilerplate is the path of my qgis plugin folder. You can find your folder by going to menu Settings -> User profiles -> Open active profile folder. From there, you can go to python -> plugins. That's the plugin folder for QGIS v3.
  5. Your cmd.exe should output something like symbolic link created for C:\Users\Nicol\AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins\example_plugin <<===>> example_plugin

Now changes made in your localRoot will be reflected in your remoteRoot. Remember to set the correct paths in your launch.json file in VSCode if you are using symbolic links.

Thanks to AsgerPetersen for his guide: https://gist.github.com/AsgerPetersen/9ea79ae4139f4977c31dd6ede2297f90

@NicolaiLolansen
Copy link
Author

Update, added section about symlinking

@ClaireLush
Copy link

Does this work in QGIS 3.24? I have it configured and VS code believes the breakpoints are valid but it doesn't break when I run it via the plugin.

@NicolaiLolansen
Copy link
Author

Does this work in QGIS 3.24? I have it configured and VS code believes the breakpoints are valid but it doesn't break when I run it via the plugin.

Sorry i did not see this comment, i will try and see tomorrow and write an updated version if i find the need to.

@ClaireLush
Copy link

Does this work in QGIS 3.24? I have it configured and VS code believes the breakpoints are valid but it doesn't break when I run it via the plugin.

Sorry i did not see this comment, i will try and see tomorrow and write an updated version if i find the need to.

Hi Nicolai, sorry for not updating. It was a configuration issue due to a mapped drive so it does work on QGIS 3.24.2

@giohappy
Copy link

@NicolaiMogensen FYI I've created a gist based on this that uses debugpy: https://gist.github.com/giohappy/8a30f14678aa7e446f9b694c632d7089

@NicolaiLolansen
Copy link
Author

@NicolaiMogensen FYI I've created a gist based on this that uses debugpy: https://gist.github.com/giohappy/8a30f14678aa7e446f9b694c632d7089

Great! Thanks for sharing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment