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 tabpyaermod-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 ¶
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). |
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"}.
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.