Skip to content

gui_v2

gui_v2

PyAERMOD GUI v2 — NiceGUI-based replacement for the Streamlit GUI.

The new GUI ships side-by-side with the legacy Streamlit GUI through the 1.x cycle. NiceGUI gives us:

  • No rerun-on-keystroke model — real reactive state binding
  • Real components: AG-Grid tables, Leaflet maps, Plotly plots
  • Same Python-only dev model as Streamlit
  • Optional native desktop window via :mod:pyaermod.gui_v2.desktop (pywebview wrapper) — single codebase, two delivery modes
Entry points
  • pyaermod-app — launches NiceGUI in a browser tab
  • pyaermod-desktop — launches NiceGUI inside a pywebview window

Both call :func:pyaermod.gui_v2.main under the hood.

Module layout::

gui_v2/
  __init__.py        -- entry point ``main()``
  state.py           -- AppState dataclass
  project_io.py      -- JSON save/load for AERMODProject
  app.py             -- top-level shell (tabs, header, status bar)
  pages/
    project.py       -- file menu + project metadata
    sources.py       -- source editor (port of the Streamlit version)
    receptors.py     -- receptor editor
    meteorology.py   -- AERMET / met-file pathway
    output.py        -- output pathway + chemistry
    run.py           -- AERMOD invocation + progress
    results.py       -- POSTFILE viewer + design values

The shell + state layer is in :mod:pyaermod.gui_v2.app and :mod:pyaermod.gui_v2.state. Pages are added incrementally — every page that hasn't been ported yet renders a placeholder banner.

main

main() -> None

Launch the NiceGUI application.

Importing :mod:nicegui is deferred to call-time so that import pyaermod doesn't pay the NiceGUI import cost when the GUI isn't being used.

State

state

Per-session app state for the NiceGUI GUI.

A single :class:AppState instance is created per browser session by :func:pyaermod.gui_v2.app.build_and_run. Each page receives the same instance and mutates it directly — there is no analogue of Streamlit's st.session_state because NiceGUI elements bind to plain Python attributes through ui.bind_* helpers.

The design intent is "fat state, dumb pages": the project tree lives on the state object, and pages are pure functions of state.

AppState dataclass

Per-session state for the GUI.

Attributes:

Name Type Description
project AERMODProject

The full AERMODProject under edit.

project_path Optional[Path]

Path to the on-disk JSON file backing this project, or None if the project has never been saved.

dirty bool

True if the in-memory project has unsaved changes.

last_run_dir Optional[Path]

Working directory of the most recent AERMOD run (drives the Results page).

title property

title: str

Window title for the GUI shell.

reset

reset() -> None

Discard the current project and start fresh.

Project I/O

project_io

JSON save/load for the GUI v2 :class:AppState.

UI-framework-agnostic: the legacy Streamlit ProjectSerializer is tightly coupled to st.session_state and lives in :mod:pyaermod.gui. This module is the headless equivalent — both GUIs can converge on it once Streamlit is deprecated in v2.0.

Format

Top-level JSON dict::

{
  "pyaermod_version": "1.9.0",
  "save_format_version": 1,
  "project": <AERMODProject as dataclass-asdict tree, with _type tags>
}

Source / receptor lists carry per-element _type discriminators so the loader can dispatch to the right dataclass on read-back. Enums are encoded as {"_enum": "EnumClass.MEMBER"}.

save_project

save_project(project: AERMODProject, path: Union[str, Path]) -> Path

Write project to path as JSON. Returns the path.

load_project

load_project(path: Union[str, Path]) -> AERMODProject

Read an AERMODProject from a JSON file written by :func:save_project.

Tolerates older save formats by reading what's there and filling missing fields with dataclass defaults.

Desktop wrapper

desktop

pywebview wrapper for the NiceGUI app.

Runs the NiceGUI server on the loopback in a background thread, then opens a native OS window pointed at the local URL. Single codebase, two delivery modes:

  • pyaermod-app — browser tab (opens default browser)
  • pyaermod-desktop — native OS window (this module)

The desktop entry is the path PyInstaller bundles for distribution (see packaging/pyinstaller.spec once v1.9-E lands).

Requires the [gui-desktop] extra (pywebview).

main

main(*, title: str = 'PyAERMOD', width: int = 1280, height: int = 800, port: Optional[int] = None) -> None

Launch NiceGUI in a background thread and open a pywebview window.