Skip to content

Latest commit

 

History

History
118 lines (93 loc) · 5.66 KB

File metadata and controls

118 lines (93 loc) · 5.66 KB

Packaging the diive desktop GUI

Build a standalone Windows app so others can run the GUI without installing Python, uv, or any dependencies. They unzip one folder and double-click diive-gui.exe.

Build

From the repo root:

uv sync --extra gui --group build      # one-time: install GUI + PyInstaller
.\packaging\build_gui.ps1              # build + zip

To include the 3-D surface tab (Plot ▸ 3D surface), add the gui3d extra before building — the spec bundles VTK/PyVista only when it's present:

uv sync --extra gui --extra gui3d --group build
.\packaging\build_gui.ps1

Without gui3d the app builds fine and the 3-D tab shows an install notice.

To include InfluxDB support (the Database tabs), add the db group before building — the spec bundles influxdb-client only when present:

uv sync --extra gui --group db --group build
.\packaging\build_gui.ps1

Without db the app builds fine and the Database tabs show an install notice. Combine extras/groups freely, e.g. uv sync --extra gui --extra gui3d --group db --group build for everything.

build_gui.ps1 automatically renders the GUI user manual (diive/gui/MANUAL.md → diive/gui/MANUAL.html) before PyInstaller runs, so the exe always ships a fresh copy (Help ▸ User manual opens it). To regenerate it by hand — e.g. after editing MANUAL.md, or to preview the HTML without doing a full build:

uv run python -m diive.gui.build_manual    # or: python diive\gui\build_manual.py

The script (diive/gui/build_manual.py) is dependency-free; MANUAL.md is the source of truth, MANUAL.html is generated — don't hand-edit the HTML.

Output:

  • dist\diive-gui\ — the runnable app folder (diive-gui.exe + dependencies)
  • dist\diive-gui-<version>+build.<stamp>-win64.zip — share this; users unzip and run the exe. <stamp> is the build timestamp (yyyyMMdd.HHmmss), so each build of the same version gets its own name, e.g. diive-gui-0.91.0+build.20260716.101500-win64.zip

Useful flags: .\packaging\build_gui.ps1 -Clean (wipe build\/dist\ first), -NoZip (skip the archive).

Use -Clean for release builds. PyInstaller's COLLECT step overwrites but never prunes dist\, so files from a previous build linger and get shipped — e.g. VTK from a gui3d build you've since dropped (hundreds of MB). -Clean guarantees the output folder matches exactly this build. (Skipping it is fine for dev iteration.)

Files

File Purpose
diive_gui.spec PyInstaller build recipe (one-folder; bundles diive/configs data + diive/gui/MANUAL.html; collects shap/xgboost/sklearn/... ; conditionally bundles the gui3d and db optionals when installed; excludes unused Qt modules)
launch_diive_gui.py Frozen-app entry point (calls diive.gui.launch)
build_gui.ps1 Build + zip helper (also renders MANUAL.md → MANUAL.html first)
make_icon.py Renders the splash-motif app icon to diive.ico (run once when the icon art changes)
diive.ico Embedded EXE / taskbar icon (multi-size; generated by make_icon.py)

If a frozen run crashes

PyInstaller's static analysis can miss dynamically-imported modules. If the exe crashes with ModuleNotFoundError or a missing-data-file error, add the offending package to the _collect list in diive_gui.spec (or a specific name to hiddenimports) and rebuild. The GUI registry imports menu tabs by string on first open, so the spec bundles all of diive.gui.tabs via collect_submodules("diive.gui.tabs"); a new menu tab must live in that package. The GUI runs some computes in a spawn worker process, so the entry script (launch_diive_gui.py) calls multiprocessing.freeze_support() before importing diive (launch() and the CLI do too). In a built exe, check that the Seasonal trend tab opens no second window, that one extra diive-gui.exe appears while it is in use, and that quitting removes both processes. Test by clicking through every tab and menu once — that exercises the lazy imports.

Known build quirks (already handled / expected)

  • collect_all("xgboost") aborts the build. It walks xgboost.testing, which imports test-only hypothesis/pytest. xgboost is collected manually instead (collect_dynamic_libs + collect_data_files + explicit hiddenimports). Don't add xgboost back to the _collect loop.
  • Library not found: tbb12.dll during the build is harmless — it's numba's optional Intel-TBB threading layer; numba falls back to its workqueue layer.
  • Bundled test suites are filtered out. collect_all would otherwise pull in the full test trees of numba / sklearn / statsmodels / pyarrow (thousands of *.tests.* modules — dead weight at runtime). The spec's _no_test_submodules() filter drops every tests / testing / conftest submodule at collection time. If a future run unexpectedly needs one, loosen that filter rather than re-adding the whole tree.
  • VTK/PyVista (the gui3d extra) is collected conditionally. The spec bundles vtkmodules / vtk / pyvista / pyvistaqt (via collect_all) only if they're installed in the build env, so a 2-D-only build skips them. VTK routes its render window through PySide6's OpenGL modules — do not add PySide6.QtOpenGL / PySide6.QtOpenGLWidgets to excludes. VTK adds several hundred MB; expect a noticeably larger build with 3-D enabled.

Notes

  • One-folder, not one-file — diive's scientific stack is large; a one-file exe would unpack everything to a temp dir on each launch (slow/flaky).
  • Windows only — PyInstaller does not cross-compile. A macOS/Linux build must run on that OS.