Dependencies are managed strictly using uv, and the
uv.lock
lockfile
is checked into source, to ensure a reproducible environment for all developers.
The lockfile also dictates the exact dependencies that will go into the bundled
application.
To get started, make sure you have uv installed, then run
git clone https://github.com/pymmcore-plus/pymmcore-gui.git
cd pymmcore-gui
uv sync
That will create a virtual environment at .venv
in the root directory, and
install all dependencies. Note: uv sync
will also install
python for you if necessary,
so you don't need to have it installed before running the command.
At any time, you can re-run uv sync
to ensure that your
current environment matches the requirements specified in uv.lock
.
Note
The lockfile itself shouldn't be manually edited, but if you need to
modify the constraints of the dependencies, you should do so as usual
in pyproject.toml
, and then run uv lock
to update the lockfile, then
commit it and open a PR.
We use pre-commit to run code checks on CI. These
checks are defined in .pre-commit-config.yaml
. If
you want to catch errors locally before pushing to github, you can install the
pre-commit hooks with:
pre-commit install
To run the tests manually at any time, you can use:
pre-commit run --all-files
If you want to activate the virtual environment to run commands directly (without
preceding everything with uv run
), run:
On windows:
.venv\Scripts\activate
On linux/mac:
source .venv/bin/activate
The "primary" version of python that we target at any time is defined in
.python-version
. This is the version that will be installed by uv
if it's
not already present on your system. We also test against all versions greater
than the minimum version defined in pyproject.toml
under the
[project.requires-python]
section.
To run the GUI, just run
uv run mmgui
(or... just mmgui
if you've activated your virtual
environment)
Tip
This script is defined in pyproject.toml
under the [project.scripts]
section.
You can run the tests with
uv run pytest
(or... just pytest
if you've activated your virtual
environment)
We use PyInstaller to freeze the application into a
single, double-clickable executable. The configuration for PyInstaller is
defined in app/mmgui.spec
.
Tip
For details pyinstaller
and the mmgui.spec
file, see the PyInstaller
documentation.
To create a bundled application locally , run
uv run pyinstaller app/mmgui.spec
Or, if you have just
installed, you can use
the bundle
task defined in justfile:
just bundle
This will create a double-clickable application in the dist/
directory.
The bundled application is also created on CI using GitHub Actions (it runs on
every push to main, and even for pull-requests). The workflow is defined in
.github/workflows/bundle.yml
.
You can download the latest build artifacts from main
branch
here
For a list of all bundles created (including those from PRs), see the GitHub Actions page for the bundle workflow. Click on any given run, and you'll see the artifacts produced by that run at the bottom.
We use
pydantic-settings
to manage settings for the application.
Loading of user preferences and settings is done in pymmcore_gui/settings.py
,
from the following sources, in order of decreasing precedence:
-
Values passed directly to the
Settings()
constructor (highest precedence) -
Environment variables starting with
PMM_
-
User settings stored in a
USER_DATA_DIR/settings.json
file. (lowest precedence).USER_DATA_DIR
is determined in a platform-specific way usingplatformdirs.user_data_dir
with the appnamepymmcore-gui
.- Windows: C:\Users\[user]\AppData\Local\pymmcore-gui\pymmcore-gui
- macOS: ~/Library/Application Support/pymmcore-gui
- Linux: ~/.local/share/pymmcore-gui
Additional sources could be added in the future (e.g. a system-wide settings, in addition to user-specific settings). See documentation for pydantic-settings for more information.
The settings schema is versioned, and the version is stored in the settings. We aim to be backwards-compatible with older settings files (i.e. we should always be able to load settings from an older version of the schema) and, when possible, forward-compatible (i.e. we should be able to load settings from a newer version of the schema, even if we don't understand all the new fields).
One must bump the schema version for any breaking changes. Breaking changes include:
- Renaming or removing a setting
- Changing the type of a setting
- Adding a new required setting (without a default value). Note: all settings should have a default value... so this should not happen.