Skip to content

Updating precommit hooks, pavement to use more recent deps and support user defined profile. #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,14 @@ bundles-util.js
qgis_resources.py
venv
.idea
# Nixos Direnv Dev Environment
.envrc
shell.nix
.vscode
.vscode-extensions
.venv
.env
.privoxy-config
.privoxy-cache/ca-key.pem
.privoxy-cache/ca-cert.pem
.privoxy.pid
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ repos:
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 3.9.2
rev: 6.0.0
hooks:
- id: flake8
language_version: python3
Expand Down
12 changes: 11 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ To install the latest version of the plugin:
That will copy the code into your QGIS user plugin folder, or create a
symlink in it, depending on your OS.

**NOTE**: This ``paver`` task only installs to the 'default' QGIS profile; so, you will have to ensure that is the active profile in order to see the plugin. You will also need to initially activate the plugin inside of the QGIS plugin manager.
**NOTE**: By default, this ``paver`` task installs the plugin to the 'default' QGIS profile. However, you can optionally specify a custom plugin path using the `--pluginpath` flag. For example:

paver install --pluginpath=~/.local/share/QGIS/QGIS3/profiles/custom_profile/python/plugins

If you specify a custom `pluginpath`, the plugin will be installed to the specified location. Ensure that the specified profile is active in QGIS to see the plugin. You will also need to activate the plugin inside the QGIS plugin manager.

- To package the plugin (*not needed during development*), run

Expand All @@ -136,3 +140,9 @@ To install the latest version of the plugin:
Documentation will be built in the `docs` folder and added to the resulting
zip file. It includes dependencies as well, but it will not download them, so
the `setup` task has to be run before packaging.

- NixOS users will find a flake in the root of the repository, which can be used to create a development shell with all dependencies installed. To use it, run:

nix develop

This will set up a development environment with all necessary dependencies for the plugin.
121 changes: 121 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

160 changes: 160 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
{
description = "NixOS developer environment for QGIS plugins.";

inputs.geospatial.url = "github:imincik/geospatial-nix.repo";
inputs.nixpkgs.follows = "geospatial/nixpkgs";

outputs = { self, geospatial, nixpkgs }:
let
system = "x86_64-linux";
profileName = "PLANET";
pkgs = import nixpkgs {
inherit system;
config = { allowUnfree = true; };
};
extraPythonPackages = ps: [
ps.pyqtwebengine
ps.jsonschema
ps.debugpy
ps.future
ps.psutil
];
qgisWithExtras = geospatial.packages.${system}.qgis.override {
inherit extraPythonPackages;
};
qgisLtrWithExtras = geospatial.packages.${system}.qgis-ltr.override {
inherit extraPythonPackages;
};
in {
packages.${system} = {
default = qgisWithExtras;
qgis-ltr = qgisLtrWithExtras;
};

devShells.${system}.default = pkgs.mkShell {
packages = [
pkgs.chafa
pkgs.ffmpeg
pkgs.gdb
pkgs.git
pkgs.glow # terminal markdown viewer
pkgs.gource # Software version control visualization
pkgs.gum
pkgs.gum # UX for TUIs
pkgs.jq
pkgs.libsForQt5.kcachegrind
pkgs.nixfmt-rfc-style
pkgs.pre-commit
pkgs.pyprof2calltree # needed to covert cprofile call trees into a format kcachegrind can read
pkgs.python3
pkgs.qgis
pkgs.qt5.full # so we get designer
pkgs.qt5.qtbase
pkgs.qt5.qtlocation
pkgs.qt5.qtquickcontrols2
pkgs.qt5.qtsvg
pkgs.qt5.qttools
pkgs.skate # Distributed key/value store
pkgs.vim
pkgs.virtualenv
pkgs.vscode
pkgs.privoxy
(pkgs.python3.withPackages (ps: [
ps.python
ps.pip
ps.setuptools
ps.wheel
ps.pytest
ps.pytest-qt
ps.black
ps.click # needed by black
ps.jsonschema
ps.pandas
ps.odfpy
ps.psutil
ps.httpx
ps.toml
ps.typer
ps.paver
# For autocompletion in vscode
ps.pyqt5-stubs
ps.debugpy
ps.numpy
ps.gdal
ps.toml
ps.typer
ps.snakeviz # For visualising cprofiler outputs
]))

];
shellHook = ''
unset SOURCE_DATE_EPOCH

# Create a virtual environment in .venv if it doesn't exist
if [ ! -d ".venv" ]; then
python -m venv .venv
fi

# Activate the virtual environment
source .venv/bin/activate

# Upgrade pip and install packages from requirements.txt if it exists
pip install --upgrade pip > /dev/null
if [ -f requirements.txt ]; then
echo "Installing Python requirements from requirements.txt..."
pip install -r requirements.txt > .pip-install.log 2>&1
if [ $? -ne 0 ]; then
echo "❌ Pip install failed. See .pip-install.log for details."
fi
else
echo "No requirements.txt found, skipping pip install."
fi

echo "-----------------------"
echo "🌈 Your Dev Environment is prepared."
echo "To run QGIS with your profile, use one of these commands:"
echo ""
echo " nix run .#qgis"
echo " nix run .#qgis-ltr"
echo ""
echo " Or use the helper script to launch it: "
echo " scripts/start_qgis.sh"
echo " scripts/start_qgis_ltr.sh"
echo ""
echo "📒 Note:"
echo "-----------------------"
echo "We provide a ready-to-use"
echo "VSCode environment which you"
echo "can start like this:"
echo ""
echo "scripts/vscode.sh"
echo "-----------------------"
echo "If you want to test the plugin behind an http proxy"
echo "we provide a script to run privoxy."
echo "🛡️ To start the proxy (Privoxy), run:"
echo " ./scripts/privoxy.sh start"
echo "🛑 To stop the proxy, run:"
echo " ./scripts/privoxy.sh stop"
echo "-----------------------"
echo ""

pre-commit clean > /dev/null
pre-commit install --install-hooks > /dev/null
pre-commit run --all-files || true
'';
};

apps.${system} = {
qgis = {
type = "app";
program = "${qgisWithExtras}/bin/qgis";
args = [ "--profile" "${profileName}" ];
};
qgis-ltr = {
type = "app";
program = "${qgisLtrWithExtras}/bin/qgis";
args = [ "--profile" "${profileName}" ];
};
};
};
}
32 changes: 19 additions & 13 deletions pavement.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import subprocess
import sys
import zipfile
from configparser import SafeConfigParser
from configparser import ConfigParser
from io import StringIO

from pathlib import Path
Expand Down Expand Up @@ -83,9 +83,7 @@ def setup():
try:
subprocess.check_call(
[
sys.executable,
"-m",
"pip",
"pip", # Explicitly use pip instead of relying on sys.executable
"install",
"--no-deps",
"--upgrade",
Expand All @@ -100,25 +98,33 @@ def setup():


@task
@cmdopts(
[
("pluginpath=", "p", "Custom path to install the plugin"),
]
)
def install(options):
"""install plugin to qgis"""
"""Install plugin to QGIS."""
plugin_name = options.plugin.name
src = path(__file__).dirname() / plugin_name
if os.name == "nt":
default_profile_plugins = (

# Use the plugin path provided via the command-line flag, or fallback to default paths
if hasattr(options, "pluginpath") and options.pluginpath:
dst_plugins = path(options.pluginpath).expanduser()
elif os.name == "nt":
dst_plugins = path(
"~/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins"
)
).expanduser()
elif sys.platform == "darwin":
default_profile_plugins = (
dst_plugins = path(
"~/Library/Application Support/QGIS/QGIS3"
"/profiles/default/python/plugins"
)
).expanduser()
else:
default_profile_plugins = (
dst_plugins = path(
"~/.local/share/QGIS/QGIS3/profiles/default/python/plugins"
)
).expanduser()

dst_plugins = path(default_profile_plugins).expanduser()
if not dst_plugins.exists():
os.makedirs(dst_plugins, exist_ok=True)
dst = dst_plugins / plugin_name
Expand Down
Loading
Loading