riverarchitect.gui

Graphical interface for River Architect.

Two front ends ship with the package and main() picks between them:

Qt (riverarchitect.gui.qt) is the default. It renders with the platform’s native widgets, so the window looks like the rest of the desktop on Linux, Windows and macOS, and it scales correctly on HiDPI screens. It runs on PySide6 or on the PyQt5 that QGIS brings.

tkinter (riverarchitect.gui.main) is the fallback, used when no Qt binding is installed. It needs nothing beyond the standard library, so the interface always opens.

Start it with the riverarchitect console script, with python -m riverarchitect, or:

from riverarchitect.gui import main
main()

Set RIVERARCHITECT_GUI to qt or tk to force a front end, which is mostly useful for testing or when a Qt install is misbehaving.

riverarchitect.gui.main(argv=None)

Console-script entry point. Starts the graphical interface.

A single positional argument sets the project directory.

Returns:

process exit status.

Return type:

int

riverarchitect.gui.available_backends()[source]

Return the front ends that can run here, best first.

Returns:

some of "qt" and "tk"; empty if neither toolkit is importable.

Return type:

list

riverarchitect.gui.select_backend(preferred=None)[source]

Choose a front end.

Parameters:

preferred (str) – "qt", "tk", or None to read RIVERARCHITECT_GUI and otherwise take the best available.

Returns:

the chosen backend, or None when no toolkit is available.

Return type:

str

Qt front end

Qt front end for River Architect.

The default interface. Runs on PySide6, or on the PyQt5 that comes with QGIS - see riverarchitect.gui.qt.qtcompat for why both are supported. When neither binding is installed, QT_AVAILABLE is False and riverarchitect.gui falls back to the tkinter interface.

Qt binding shim: run on PySide6, or on the PyQt5 that ships with QGIS.

River Architect is used from two different interpreters. Analysis runs in a conda environment, where PySide6 installs cleanly from PyPI or conda-forge. Mapping has to run in whichever interpreter owns the QGIS bindings, and QGIS brings its own PyQt5 - installing a second Qt into that interpreter is how you get two Qt libraries in one process and a segfault. So the interface targets whatever Qt is already there:

  1. PySide6, if it is importable, and

  2. PyQt5 otherwise, which is what a QGIS-enabled interpreter has.

Only the handful of names the interface actually uses are re-exported, and only in the fully-qualified enum form (Qt.AlignmentFlag.AlignLeft, not Qt.AlignLeft), which is the spelling both bindings accept.

QT_BINDING names the binding in use; QT_AVAILABLE is False when neither is installed, which is what makes the tkinter fallback kick in.

riverarchitect.gui.qt.qtcompat.exec_app(app)[source]

Start the event loop. exec_() on PyQt5, exec() on PySide6.

riverarchitect.gui.qt.qtcompat.exec_dialog(dialog)[source]

Show a modal dialog and return its result code.

Shared behaviour for Qt module tabs.

The tkinter base class (riverarchitect.gui.base) owns its own menu bar and resizes the whole window whenever its tab is selected. Neither is right in a Qt application: menus belong to the main window, and a tab must not move the window under the user. What is left here is what a tab genuinely shares - the unit system, the condition list, and a way to run work off the interface thread.

class riverarchitect.gui.qt.base.RaTab(*args, **kwargs)[source]

Bases: QWidget

Base class for a module tab.

Subclasses set title and subtitle, then build their widgets into body. Long-running work goes through run_in_background(), which keeps the interface responsive and marshals the result back onto the Qt thread.

title = 'River Architect'

Tab label.

subtitle = ''

One or two lines shown under the tab heading.

run_in_background(work, on_success, on_error)[source]

Run work() on a worker thread and deliver the outcome on the Qt thread.

Qt widgets may only be touched from the thread that created them, so the worker emits a signal instead of calling back directly. A queued signal connection is the Qt equivalent of tkinter’s widget.after(0, ...).

set_unit(unit)[source]

Switch the unit system and notify the subclass through on_unit_change().

on_unit_change()[source]

Hook called after the unit system changed. Does nothing by default.

refresh_conditions()[source]

Reload the list of conditions available in the project directory.

on_project_home_change()[source]

Hook called after the project directory changed. Refreshes the condition list.

choose_file(caption, patterns='Raster files (*.tif *.tiff *.flt *.asc);;All files (*)')[source]
choose_directory(caption, start=None)[source]
warn(title, text)[source]
fail(title, text)[source]
static elide(path, limit=54)[source]

Shorten a path for a button label, keeping the file name readable.

Qt main window: a tabbed shell with one tab per module.

Structurally the same as the tkinter window it replaces, with the differences a real toolkit makes possible: the menu bar belongs to the window rather than being rebuilt on every tab change, the status bar reports the project directory, and a tab that fails to construct is replaced by a message instead of taking the window down.

class riverarchitect.gui.qt.main.RiverArchitectWindow(*args, **kwargs)[source]

Bases: QMainWindow

Top-level window holding one tab per module.

select_tab(group, tab=None)[source]

Bring a module tab to the front. Used by the Live Guide.

Parameters:
  • group (str) – the top-level tab, as TAB_GROUPS names it.

  • tab (str) – the module tab within it. Defaults to the group itself.

Returns:

True when the tab was found and selected.

Return type:

bool

static open_documentation()[source]
open_live_guide()[source]

Open the step-by-step walkthrough of the sample-data example.

set_unit(unit)[source]

Switch the unit system for every tab.

choose_project_home()[source]
run_reconcile_nodata()[source]
show_about()[source]
riverarchitect.gui.qt.main.run(argv=None)[source]

Create the application, show the window and run the event loop.

riverarchitect.gui.qt.main.TAB_GROUPS = (('Get Started', (<class 'riverarchitect.gui.qt.getstarted_tab.GetStartedTab'>,)), ('Lifespan', (<class 'riverarchitect.gui.qt.lifespan_tab.LifespanTab'>, <class 'riverarchitect.gui.qt.maxlifespan_tab.MaxLifespanTab'>)), ('Morphology', (<class 'riverarchitect.gui.qt.terraforming_tab.TerraformingTab'>, <class 'riverarchitect.gui.qt.riverbuilder_tab.RiverBuilderTab'>, <class 'riverarchitect.gui.qt.volume_tab.VolumeTab'>)), ('Ecohydraulics', (<class 'riverarchitect.gui.qt.sharc_tab.SharcTab'>, <class 'riverarchitect.gui.qt.stranding_tab.StrandingTab'>, <class 'riverarchitect.gui.qt.recruitment_tab.RecruitmentTab'>)), ('Project Maker', (<class 'riverarchitect.gui.qt.projectmaker_tab.ProjectMakerTab'>,)), ('Maps', (<class 'riverarchitect.gui.qt.mapping_tab.MappingTab'>,)))

Top-level tabs and their sub-tabs, reproducing the grouping of the ArcGIS version: a module either stands alone or sits inside a themed group. None means the entry is a single tab rather than a group.

riverarchitect.gui.qt.main.TABS = (<class 'riverarchitect.gui.qt.getstarted_tab.GetStartedTab'>, <class 'riverarchitect.gui.qt.lifespan_tab.LifespanTab'>, <class 'riverarchitect.gui.qt.maxlifespan_tab.MaxLifespanTab'>, <class 'riverarchitect.gui.qt.terraforming_tab.TerraformingTab'>, <class 'riverarchitect.gui.qt.riverbuilder_tab.RiverBuilderTab'>, <class 'riverarchitect.gui.qt.volume_tab.VolumeTab'>, <class 'riverarchitect.gui.qt.sharc_tab.SharcTab'>, <class 'riverarchitect.gui.qt.stranding_tab.StrandingTab'>, <class 'riverarchitect.gui.qt.recruitment_tab.RecruitmentTab'>, <class 'riverarchitect.gui.qt.projectmaker_tab.ProjectMakerTab'>, <class 'riverarchitect.gui.qt.mapping_tab.MappingTab'>)

Flat list of every module tab, in the order they appear.

tkinter fallback

Shared tkinter base class for River Architect module tabs.

This is the fallback interface, used when no Qt binding is installed; see riverarchitect.gui for how the two front ends are chosen and riverarchitect.gui.qt for the default one.

Carries over the structure of the original child_gui.RaModuleGui: every module tab is a tk.Frame subclass with a unit-system toggle and a condition list. What changed is that the base class no longer reaches into arcpy or into workbook templates to build itself, so a tab can be constructed and tested without any geoprocessing stack - and that the window owns the menu bar. The original rebuilt the whole menu bar, and resized and re-centred the window, on every tab change; menus are now built once by riverarchitect.gui.main.RiverArchitectGui.

class riverarchitect.gui.base.RaModuleGui(master=None)[source]

Bases: Frame

Base class for a module tab.

Subclasses set title and build their widgets in __init__.

Parameters:

master – parent widget, normally the notebook tab container.

title = 'River Architect'

Tab title.

set_unit(unit)[source]

Switch the unit system and notify the subclass through on_unit_change().

on_unit_change()[source]

Hook called after the unit system changed. Does nothing by default.

refresh_conditions(listbox=None, scrollbar=None)[source]

Reload the list of available conditions from the project directory.

on_project_home_change()[source]

Hook called after the project directory changed. Refreshes the condition list.

static set_background(frame, colour)[source]

Main tkinter window: a notebook of module tabs.

The fallback interface, used when no Qt binding is installed. See riverarchitect.gui for the backend choice and riverarchitect.gui.qt for the default front end.

Carries over the structure of the original parent_gui.RaGui, with three deliberate differences:

  • selecting a tab no longer calls os.chdir(). The original changed the process working directory per tab, which coupled unrelated modules through global state and made behaviour depend on click order. Paths are now explicit;

  • the window owns the menu bar and builds it once. The original rebuilt every menu on each tab change, which leaked a tk.Menu per click;

  • selecting a tab no longer resizes and re-centres the window, which fought whatever size the user had chosen.

class riverarchitect.gui.main.RiverArchitectGui(master=None)[source]

Bases: Frame

Top-level window holding one tab per module.

select_tab(group, tab=None)[source]

Bring a module tab to the front. Used by the Live Guide.

Parameters:
  • group (str) – the top-level tab, as TAB_GROUPS names it.

  • tab (str) – the module tab within it. Defaults to the group itself.

Returns:

True when the tab was found and selected.

Return type:

bool

set_unit(unit)[source]

Switch the unit system for every tab.

choose_project_home()[source]
run_reconcile_nodata()[source]

Run the NoData reconciliation tool on a chosen condition folder.

static open_documentation()[source]
open_live_guide()[source]

Open the step-by-step walkthrough of the sample-data example.

show_about()[source]
quit_program()[source]
riverarchitect.gui.main.main(argv=None)[source]

Start the tkinter interface.

Normally reached through riverarchitect.gui.main(), which picks a front end.

riverarchitect.gui.main.TAB_GROUPS = (('Get Started', (<class 'riverarchitect.gui.getstarted_tab.GetStartedGui'>,)), ('Lifespan', (<class 'riverarchitect.gui.lifespan_tab.LifespanGui'>, <class 'riverarchitect.gui.maxlifespan_tab.MaxLifespanGui'>)), ('Morphology', (<class 'riverarchitect.gui.terraforming_tab.TerraformingGui'>, <class 'riverarchitect.gui.riverbuilder_tab.RiverBuilderGui'>, <class 'riverarchitect.gui.volume_tab.VolumeGui'>)), ('Ecohydraulics', (<class 'riverarchitect.gui.sharc_tab.SharcGui'>, <class 'riverarchitect.gui.stranding_tab.StrandingGui'>, <class 'riverarchitect.gui.recruitment_tab.RecruitmentGui'>)), ('Project Maker', (<class 'riverarchitect.gui.projectmaker_tab.ProjectMakerGui'>,)), ('Maps', (<class 'riverarchitect.gui.mapping_tab.MappingGui'>,)))

Top-level tabs and their sub-tabs, matching the Qt front end and the ArcGIS version.