14. Contributing to QPolaris#
Developing and testing QGIS plugins is quite different from developing a python package for two main reasons:
The virtual environment required for development is extremely complex, as it is composed of the entire QGIS software/capabilities
Testing software with Graphical User Interfaces requires different resources than testing terminal tools
QGIS is under constant development and its releases are more frequent than the reviews to this documentation, and therefore slight changes to the development setup described below may be required.
14.1. QGIS environments#
We recommend using a dedicated virtual environment to develop QPolaris, using the version of Python related to the most recent QGIS long-term release. When this section was updated (October/2025),LTR 3.40.12 was coming with a default 3.12.11 Python environment.
We also assume you are using one of PyCharm or VSCode, which are good IDEs for Python. If you are using a different IDE, we would welcome if you could contribute with instructions to set that up.
(For us,) The easiest way of developing a QGIS plugin is using a Docker container to build
an image containing a QGIS installation. When cloning QPolaris repository into your local
machine you will find a Dockerfile with this recipe.
git clone https://git-out.gss.anl.gov/polaris/code/QPolaris.git
Then all you have to do is activate the virtual environment and adding the environment variables. Without adding these variables, your installation of Polaris-Studio in QGIS is going to be useless.
. .venv/bin/activate
export PYTHONPATH=$(pwd)/QPolaris/packages:$PYTHONPATH
export QT_QPA_PLATFORM=offscreen
We understood that the creation of a virtual development environment within a container would be redundant, however after facing some developing issues related to PEP 668, we believe that using a virtual environment would be a good practice.
If you have to manually test changes in QPolaris after its installed in QGIS, we strongly recommend using the Plugin Reloader, a plugin to reload another plugins.
14.2. Documentation#
The documentation is written in Restructured Text, and it lives in the docs/source, accessible from the root directory of you project.
14.3. Testing#
QPolaris testing is done with some tools:
Ruff, a linter and code formatter
pytest, a Python testing tool
pytest-cov, a tool for measuring test code coverage
pytest-qt, a tool for testing PyQt5 applications
pytest-qgis, a tool for writing QGIS tests
Running these tests locally requires a correct setup of the QGIS Python environment, and may be challenging. Relying on the CI system for testing is an adequate compromise.
All tests are required to pass in order to somebody manually review the code before merging it into main (or returning for corrections).
In some cases, test targets need to be updated to match the new results produced by the code since these
are now the correct results. In order to update the test targets, first determine which tests are
failing and then review the failing lines in the source files. These are easy to identify since each
test ultimately comes down to one of Python’s various types of assert statements. Once you identify
which assert is failing, you can work your way back through the code that creates the test targets in
order to update it. After updating the test targets, re-run the tests to confirm the new code passes all
the tests.
Tip
If you want to check if the test values are at the right place in the UI, qtbot can help you. Add qtbot in the function definition and take a screenshot of the UI. To visualize it, don’t forget to use a print statement.
path = qtbot.screenshot(dialog)
print(path)
14.4. Dialog boxes#
Designing good dialog boxes can be challenging, especially making sure that it works properly on any modern monitor size/resolution, that it can be resized and has a sensible sequence of active tools when the user navigates it with the keyboard (i.e. using tab).
For inexperienced users, it is recommended to use QT Designer, which is automatically installed when QGIS is installed, as it has an easy-to-use GUI and contains QGIS-specific tools. Starting from an existing widget/dialog box is an easy easy to go about it.
In terms of code organisation, dialog boxes should be under the corresponding ui folder for the tool being developed.
14.5. Plugin Structure#
The plugin structure is determined by the content of the polaris_menu.py file, where all procedures are exposed to the user through the various QGIS interface elements (toolbar, panel, and processing provider).
When developing a new tool, deciding how the user will interact with it is critical. All tools MUST BE AVAILABLE THROUGH THE PROCESSING PROVIDER. This ensures discoverability of the tool, as the processing provider allows for text search (so use appropriate keywords in the tool description).
You should consider carefully before adding new tools to the toolbar or panel, as these interface elements can become quickly cluttered and hard to use. Only frequently used tools should be added to these interfaces, with the toolbar reserved for the most common tasks.
As time passes, tools could be demoted and removed from the toolbar or panel if they are not used frequently.
14.5.1. Adding to Processing provider#
Adding a new tool to the processing provider requires creating a new class that inherits from PolarisProcessingAlgorithm (~QPolarismodulesprocessing_providerpolaris_base_algorithm.py) and adding this tool to the QPolaris processing provider interface (~QPolarismodulesprocessing_providerprovider.py). The easiest way to do so is to copy an existing tool and modify it to represent the new tool.
14.5.2. Adding to Panel#
After deciding in which menu/sub-menu of the panel the new tool will be added, a number of steps are required and, again, replicating existing code is recommended.
If new menus and/or submenus are required, the first required step is to add to the self.menuActions dictionary, around line 70 of ~QPolarispolaris_menu.py.
self.menuActions = {
...
"MY NEW MENU": {"root": []},
"MY NEW MENU": {"root": [], "SUBMENU1": [], "SUBMENU2": [], "SUBMENU3": []},
...
}
Adding the new tool to the desired menu/submenu is done from around line 85 of the same file, and we try to keep the code organized by menu name.
self.add_menu_action("MENU NAME", "TOOL_NAME", partial(tool_function_name_imported_from_other_module, self), sub="SUBMENU NAME")
# or, in case it is in the root of the menu
self.add_menu_action("MENU NAME", "TOOL_NAME", partial(tool_function_name_imported_from_other_module, self), sub="root")
14.5.3. Adding to Toolbar#
Adding a new tool to the toolbar is similar to adding it to the panel, and it is done in the same file (~QPolarispolaris_menu.py), from around line 175. Again, replicating existing code is recommended.
Each new tool is added with a line like this:
self.add_action("open_file.jpg", "Open Supply", partial(load_supply_file, self))
Remember to choose a meaningful icon (and check for its license before commiting to the repository) and to commit said
icon to the ~\QPolaris\icons folder.