WOLF

Contents:

  • Code organization
  • Installation
  • Launch apps
  • Tutorials
  • Supported formats
  • Graphical User Interface
  • Blender
  • WOLF Hydro
  • WOLF 1D
  • WOLF 2D
  • Jupyter Notebooks
  • Scenarios Manager
  • Hydrometry
  • Particle System
  • Report
  • Acceptability
  • Dike breach
  • Drowning
  • Development tools
  • Gitlab
  • Python
  • GDAL and Python
  • Support - Repositories
  • Contributors
  • HECE References
  • Development Guide
    • Structure interne
    • Native Crash — Re-entrant Import Under the Python Import Lock
    • Dialog Provider
    • Add Object Pipeline
    • Mouse Action System
    • Active-Object Slots (ActiveSlot)
    • Scripting wolfhece from a VSCode Jupyter Notebook
    • Plugin System
    • Action plugin — minimal example
    • Action plugin — complete reference
    • Plugin overload — replacing and restoring an existing handler
    • Menu-less companion - minimal example
      • Why skip a menu?
      • What AbstractUICompanion does for you
      • 1 - Start wx
      • 2 - Create the viewer
      • 3 - The companion class
      • 4 - Instantiate and start
      • 5 - Inspect collected points
      • 6 - Stop and clean up
      • Summary - full class
    • Companion plugin — minimal example
    • Companion plugin — complete reference
    • Companion plugin — overload and restore
    • Companion factory — interactive geometry selection
  • API Reference
WOLF
  • Development Guide
  • Menu-less companion - minimal example
  • View page source

Menu-less companion - minimal example

This notebook shows the shortest possible companion:

  • No menu: no menu_spec() / build() required

  • No hidden graphics helper: OpenGL drawing is explicit

  • Single responsibility: right click -> add a point

The whole class fits in about twenty lines.

Why skip a menu?

From the notebook, actions are triggered by code (comp.start(), comp.stop()).
A menu is only useful if the end user must trigger the action from the interface.

What AbstractUICompanion does for you

API

Role

self.proxy.register_action(...)

Registers handlers on the viewer

self.proxy.start_action(id, hint)

Activates the action and shows the hint

self.proxy.end_action()

Deactivates the current action

self.proxy.action_id(local)

Returns a namespaced id (namespace.local)

self.proxy._viewer.Refresh()

Forces a redraw

self.destroy()

Unregisters all companion actions

1 - Start wx

[1]:
import sys
%gui wx

2 - Create the viewer

[ ]:
from wolfhece.PyDraw import WolfMapViewer

viewer = WolfMapViewer(None, title="Menu-less companion", w=1200, h=800)
viewer.Show()
INFO:root:Importing wolfhece modules
INFO:root:wolfhece modules imported
INFO:root:Plugin manager init: skipping auto-discovery because no global configuration is available yet.
False

3 - The companion class

The only requirement is to implement start() (required by AbstractUICompanion).
Everything else is optional.
[ ]:
from wolfhece.plugins.abc import AbstractUICompanion
from wolfhece._viewer_plugin_handlers import MouseContext
from OpenGL.GL import glBegin, glEnd, glVertex2f, glColor4f, glLineWidth, GL_LINES


class DotCompanion(AbstractUICompanion):
    """Collect right-clicks and draw them on the map.

    No menu, no boilerplate - the minimal companion.
    """

    def __init__(self):
        #: Collected points - readable from the notebook.
        self.points: list[tuple[float, float]] = []

    def start(self) -> None:
        """Register handlers and activate the action."""
        self.proxy.register_action('pick', rdown=self._rdown, paint=self._paint)
        self.proxy.start_action('pick', 'Right-click to add a point...')

    def _rdown(self, ctx: MouseContext) -> None:
        self.points.append((ctx.x_snap, ctx.y_snap))
        print(f'#{len(self.points):3d}  X={ctx.x_snap:.3f}  Y={ctx.y_snap:.3f}')
        self.proxy._viewer.Refresh()

    def _paint(self) -> None:
        half = (self.proxy._viewer.xmax - self.proxy._viewer.xmin) * 0.01
        glLineWidth(2.0)
        glColor4f(0.0, 1.0, 0.0, 1.0)
        glBegin(GL_LINES)
        for (x, y) in self.points:
            glVertex2f(x - half, y); glVertex2f(x + half, y)
            glVertex2f(x, y - half); glVertex2f(x, y + half)
        glEnd()

4 - Instantiate and start

No build() to call - start directly.

[ ]:
comp = DotCompanion()
viewer.attach_companion(comp)
comp.start()

print(comp)
print('Action active - right-click on the map to add points.')
INFO:root:ACTION : Clic droit pour ajouter un point…
<DotCompanion ns='dotcompanion' menu=not built actions=[dotcompanion.pick]>
Action active — clic droit sur la carte pour ajouter des points.

5 - Inspect collected points

[5]:
print(f"{len(comp.points)} point(s) :")
for i, (x, y) in enumerate(comp.points, 1):
    print(f"  {i:3d}.  X={x:.3f}   Y={y:.3f}")
0 point(s) :

6 - Stop and clean up

[ ]:
viewer.detach_companion(comp)

print("Companion destroyed.")
print(comp)
INFO:root:ACTION : End of action
Compagnon détruit.
<DotCompanion ns='dotcompanion' menu=not built actions=[—]>
The Kernel crashed while executing code in the current cell or a previous cell.

Please review the code in the cell(s) to identify a possible cause of the failure.

Click <a href='https://aka.ms/vscodeJupyterKernelCrash'>here</a> for more info.

View Jupyter <a href='command:jupyter.viewOutput'>log</a> for further details.

Summary - full class

from wolfhece.plugins.abc import AbstractUICompanion
from wolfhece._viewer_plugin_handlers import MouseContext
from OpenGL.GL import glBegin, glEnd, glVertex2f, glColor4f, glLineWidth, GL_LINES

class DotCompanion(AbstractUICompanion):
    def __init__(self):_()
        self.points: list[tuple[float, float]] = []

    def start(self) -> None:
        self.proxy.register_action('pick', rdown=self._rdown, paint=self._paint)
        self.proxy.start_action('pick', 'Right-click to add a point...')

    def _rdown(self, ctx: MouseContext) -> None:
        self.points.append((ctx.x_snap, ctx.y_snap))
        self.proxy._viewer.Refresh()

    def _paint(self) -> None:
        half = (self.proxy._viewer.xmax - self.proxy._viewer.xmin) * 0.01
        glLineWidth(2.0)
        glColor4f(0.0, 1.0, 0.0, 1.0)
        glBegin(GL_LINES)
        for (x, y) in self.points:
            glVertex2f(x - half, y); glVertex2f(x + half, y)
            glVertex2f(x, y - half); glVertex2f(x, y + half)
        glEnd()
Previous Next

© Copyright 2025, Pierre Archambeau.

Built with Sphinx using a theme provided by Read the Docs.