Best IDE for QGIS Plugin Development

The best IDE for QGIS plugin development is JetBrains PyCharm Professional, with Visual Studio Code as a highly capable lightweight alternative. PyCharm delivers the most reliable integrated debugger, accurate static analysis for the compiled qgis.* modules, and seamless environment routing when it is pointed at the QGIS-bundled Python interpreter. VS Code matches most of that functionality through the Python/Pylance extensions and debugpy, but it needs a manual launch.json to attach to a running QGIS process. Spyder rounds out the field for exploratory analysis but is a poor fit for packaging and shipping a plugin.

This page is a decision guide: it weighs the three editors against the tasks that actually matter for plugin work, then gives you the exact routing and debugger recipes for each. If you have already settled on JetBrains and just want it wired to QGIS correctly, jump straight to setting up PyCharm for QGIS, the parent guide this article sits under.

Prerequisites

Before you can meaningfully compare editors, the same underlying components must be in place — the IDE is only a front end for the QGIS Python runtime.

  • QGIS 3.44 LTR or 4.0+ installed via OSGeo4W (Windows), the standalone installer, or your Linux package manager.
  • A 64-bit Python — specifically the interpreter bundled with QGIS. QGIS is strictly 64-bit, so a 32-bit IDE interpreter will never load qgis._core.
  • The QGIS Python bindings resolving on the command line: python3 -c "import qgis.core" must run without error using the bundled interpreter.
  • Working familiarity with the QGIS Python Console, since every debugger recipe below injects a bridge from code that runs inside QGIS.
  • An editor installed: PyCharm Professional 2023.2+, VS Code 1.85+ with ms-python.python and ms-python.debugpy, or Spyder 5+.

Quick compatibility matrix

ComponentRequirementNotes
QGIS3.44 LTR or 4.0+Python bindings match the installer's bundled version
Python3.12 (QGIS 3.34+, incl. 3.44 LTR)Must exactly match QGIS's internal build; avoid conda/system Python
PyQtPyQt5 (QGIS 3.x) / PyQt6 (QGIS 4.x)QGIS 4.0 moved to Qt6; plugins targeting both need compatible imports
PyCharmProfessional 2023.2+Community edition lacks remote debugger attachment
VS Code1.85+Requires ms-python.python and ms-python.debugpy
OSWin/macOS/LinuxPath separators and QGIS_PREFIX_PATH resolution differ

How the editors compare for plugin work

The choice comes down to five capabilities that separate a comfortable plugin workflow from a frustrating one: live process debugging, static analysis of the compiled bindings, Qt Designer integration, project tooling for packaging, and resource footprint.

IDE comparison matrix for QGIS plugin developmentPyCharm Professional earns full marks for live QGIS-process debugging, static analysis of the compiled qgis bindings, Qt Designer integration, and plugin packaging, but only weak marks on a light resource footprint. VS Code scores full on static analysis and footprint, partial on debugging, packaging and Qt Designer because they need manual configuration. Spyder scores partial on static analysis and footprint and weak on debugging, Qt Designer and packaging.CapabilityPyCharm Propaid · heavyVS Codefree · lightSpyderfree · analysisLive QGIS-process debuggingStatic analysis of thecompiled qgis.* bindingsQt Designer integrationPlugin packaging &project toolingLight resource footprintFull supportPartial — needs manual setupWeak or absent
  • PyCharm Professional wins on the integrated debugger. Its Python Debug Server is a guided run configuration, and pydevd_pycharm attaches to the live GUI thread so breakpoints fire inside a plugin action. Static analysis of qgis.core, qgis.gui, and qgis.analysis is the most accurate of the three because PyCharm indexes the bundled interpreter's docstrings thoroughly. The cost is a heavier memory footprint and a paid licence — the debugger attachment is Professional-only.
  • VS Code matches PyCharm on IntelliSense (via Pylance) and attach-mode debugging (via debugpy), at a fraction of the RAM. The trade-off is configuration: you hand-write launch.json and a debugpy.listen(...) call rather than clicking through a run configuration. For developers already living in VS Code, this is the strongest free option.
  • Spyder is excellent for console-driven data analysis but lacks polished remote-attach debugging and plugin-oriented project tooling. Keep it for ad-hoc spatial queries; reach for PyCharm or VS Code the moment you start packaging.

Environment routing: point the IDE at QGIS's Python

QGIS ships a self-contained Python environment. Any external IDE defaults to a global or virtual environment that has no compiled qgis.core, qgis.analysis, or Qt bindings, so the first job in every editor is identical: route the interpreter at the QGIS-bundled python.exe (Windows/OSGeo4W) or python3 (macOS/Linux). The mechanics differ per editor — the PyCharm setup guide walks the JetBrains dialog step by step — but the target path is the same.

When you cannot point the interpreter directly (for example when a shared linter runs against a system Python), inject the QGIS paths at runtime before any import qgis statement:

import sys
import os

# Adjust to your QGIS installation root
QGIS_PREFIX = os.environ.get("QGIS_PREFIX_PATH", r"C:\OSGeo4W\apps\qgis-ltr")
PYTHON_DIR = os.path.join(QGIS_PREFIX, "python")
PLUGINS_DIR = os.path.join(QGIS_PREFIX, "python", "plugins")

# Prepend QGIS paths
for p in (PYTHON_DIR, PLUGINS_DIR):
    if os.path.isdir(p) and p not in sys.path:
        sys.path.insert(0, p)

# Set mandatory runtime variables
os.environ["QGIS_PREFIX_PATH"] = QGIS_PREFIX
os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = os.path.join(QGIS_PREFIX, "plugins", "platforms")

from qgis.core import QgsApplication, QgsProject

If your plugin depends on third-party packages that must not pollute the bundled interpreter, isolate them in a dedicated virtual environment for GIS work — but keep the QGIS core modules resolving to the QGIS-provided Python to avoid ABI mismatches. For running finished tools headlessly, the same routing underpins running Python scripts outside QGIS Desktop.

Attach the debugger in each IDE

Live debugging is where editor choice pays off most, so here are the two recipes side by side.

PyCharm Professional. Create a Python Debug Server run configuration (default localhost:5678) and start it so PyCharm listens. Then, from your plugin's __init__.py, a target function, or the QGIS Python Console, install the bridge and trigger the plugin action:

# pip install pydevd-pycharm~=<your PyCharm build number> into the QGIS interpreter first
import pydevd_pycharm
pydevd_pycharm.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True, suspend=False)

VS Code. Add an attach configuration to .vscode/launch.json:

{
    "name": "Attach to QGIS",
    "type": "python",
    "request": "attach",
    "connect": { "host": "localhost", "port": 5678 },
    "justMyCode": false,
    "subProcess": true
}

Insert import debugpy; debugpy.listen(("localhost", 5678)); debugpy.wait_for_client() in your plugin entry point, start the VS Code debugger, then launch QGIS. In both editors, breakpoints halt execution against the actual QGIS GUI thread so you can inspect QgsFeature attributes, geometry objects, and signal payloads in real time. For logging strategies and post-mortem tactics that complement the attach workflow, see debugging PyQGIS scripts.

Two debugging models

Every editor attaches to a running QGIS in one of two ways, and the difference decides how much setup each one needs.

Attach to QGIS, or have QGIS connect outIn the attach model the IDE finds the running QGIS process and injects its debugger, which requires process-attach permission and matching architectures. In the listen model the IDE opens a port and waits, and a line inside the plugin connects out to it, which works across containers and remote machines.Which end initiates the connectionattach to processthe IDErunning QGISno code change in the pluginneeds local process accesslisten for a connectionthe IDEport 5678running QGISsettrace(...)one line added to the pluginworks across containers and hosts

The listen model is the more portable of the two and the one worth learning first: it is the only option when QGIS runs in a container or on another machine, and the single settrace line can be guarded by an environment variable so it never reaches production.

What actually differs between editors

Once the interpreter and the debugger are configured, the day-to-day differences narrow to a few things that genuinely affect plugin work.

Where the editors actually diverge for plugin workFour rows compare a full IDE, a lightweight editor and a plain text editor. Autocompletion against the QGIS bindings is strong in the full IDE, good in the lightweight editor with a stubs package, and absent in the plain editor. Qt Designer integration, debugger setup effort and memory use follow the same ordering in reverse.Four things that matter once the setup is doneaspectfull IDElightweightplain editorPyQGIS autocompletionstronggood, with stubsnoneQt Designer launchbuilt inan extensionseparate appdebugger setupa dialoga JSON fileprint statementsmemory footprintlargemoderatetiny

QGIS-version compatibility notes

The single detail that breaks reproducible plugin code is the Qt/PyQt boundary, so pin your examples to a known QGIS release.

  • QGIS 3.x (including the 3.44 LTR) builds against PyQt5; QGIS 4.0 moves to PyQt6. A plugin that imports PyQt5.QtWidgets directly will not load under a Qt6 QGIS, and vice versa. Where you need to support both, import through the qgis.PyQt compatibility shim rather than the concrete binding.
  • The bundled interpreter tracks the QGIS release — QGIS 3.34+ ships Python 3.12, older 3.28 builds shipped 3.9. Point every editor at the interpreter that matches the QGIS you are targeting, not the newest Python on the machine.
  • The pydevd-pycharm egg is versioned to your PyCharm build. A mismatched egg fails the handshake silently, so re-pin it whenever you update the IDE.

Before committing example code to a repository, cross-check the QGIS Python version compatibility guide so your interpreter, bindings, and imports all line up.

Troubleshooting

  • Architecture mismatch. QGIS is strictly 64-bit; a 32-bit IDE interpreter fails to load qgis._core. Verify with python -c "import platform; print(platform.architecture())".
  • Use .pth files for static resolution. Drop a qgis_paths.pth file into your IDE's site-packages containing absolute paths to QGIS's python and plugins directories. This forces static resolution without editing plugin source.
  • Isolate profile conflicts. Launch QGIS with a clean profile — qgis --profile-name debug — because a corrupted user profile often masks IDE configuration errors.
  • Fall back to the QGIS message log. If a firewall blocks the debugger port, route diagnostics to QGIS's built-in log instead: from qgis.core import QgsMessageLog, Qgis; QgsMessageLog.logMessage("Debug: state", "MyPlugin", Qgis.Warning).

If the qgis import itself keeps failing whatever you try, work through the ordered checklist in fixing PyQGIS module import errors, which traces exactly how sys.path, PYTHONPATH, and the bundled site-packages are consulted.

Conclusion

For serious QGIS plugin development, PyCharm Professional is the most productive editor thanks to its guided remote debugger and deep static analysis of the compiled bindings; VS Code is the best free alternative once you have written the launch.json; and Spyder stays useful only for exploratory analysis. Whichever you pick, the decisive factor is not the editor's feature list but environment alignment — the interpreter, Qt bindings, and debugger ports all pointing at the same QGIS runtime. Get that right and you gain full static analysis, step-through execution against the live GUI thread, and reproducible example code. With the editor chosen, the natural next step is wiring it up in detail through setting up PyCharm for QGIS and grounding your work in the wider PyQGIS Fundamentals & Environment Setup hub.

Frequently Asked Questions

Is PyCharm Community Edition enough for QGIS plugin development? Community Edition handles interpreter routing, static analysis, and local script execution, so it works for most scripting and library code. It does not include the remote debugger attachment that connects to a running QGIS process, which is the key gap for live plugin debugging. If you need to step through plugin actions inside QGIS, the Professional edition or VS Code with debugpy is the better fit.

How does VS Code compare to PyCharm for PyQGIS work? VS Code matches most of PyCharm's functionality through the ms-python.python and ms-python.debugpy extensions, including IntelliSense and attach-mode debugging. The main trade-off is that VS Code requires a manual launch.json for process attachment, whereas PyCharm exposes the debug server as a guided run configuration. For lightweight setups and developers already in the VS Code ecosystem, it is a strong choice.

Can I use Spyder for QGIS plugin development? Spyder is comfortable for exploratory, console-style data analysis but is not well suited to building and debugging full plugins. It lacks the polished remote-attach debugging and the plugin-oriented project tooling that PyCharm and VS Code provide. Most developers keep Spyder for ad-hoc analysis and reach for PyCharm or VS Code when packaging a plugin.

Why does my IDE fail to import qgis._core even after pointing at the right interpreter? The most common cause is an architecture mismatch: QGIS is strictly 64-bit, so a 32-bit interpreter cannot load the compiled module. Verify with python -c "import platform; print(platform.architecture())". If the architecture is correct, ensure no system PyQt5/PyQt6 shadows the QGIS Qt bindings and that the QGIS bin directory is on PATH.

What should I do if a firewall blocks the debugger port? Route diagnostic output to QGIS's built-in log instead of the debugger, using QgsMessageLog.logMessage(...) with a custom tag so you can filter your messages. This gives you a reliable observation channel without any open port. Once you can run on a network that permits the debug port, switch back to full step-through debugging for deeper inspection.