Action plugin — minimal example

This notebook illustrates the per-instance registration mechanism for custom mouse handlers in WolfMapViewer.

Workflow

  1. Start wx and create the viewer

  2. Define the plugin handler inline in the notebook

  3. Register the plugin on the instance with viewer.register_action()

  4. Activate the action with viewer.start_action()

  5. Every right-click on the map triggers the handler

  6. Deactivate / unregister when done

Handlers registered on viewer are isolated to that instance and do not affect other WolfMapViewer windows open in the same session.

1 — Imports and wx startup

Important: in a Jupyter notebook, always use the %gui wx magic instead of creating a wx.App() manually. Creating a wx.App() explicitly hands the event loop to Python code, which blocks the kernel or causes conflicts with the notebook’s own event loop. %gui wx delegates the loop to IPython, which integrates it correctly into its own async loop.

[1]:
import sys
%gui wx

2 — Creating the MapViewer

[2]:
from wolfhece.PyDraw import WolfMapViewer

viewer = WolfMapViewer(None, title="Plugin demo", w=1200, h=800)
viewer.Show()
INFO:root:Importing wolfhece modules
INFO:root:wolfhece modules imported
INFO:wolfhece.tablet_wintab:WinTab : contexte ouvert sur HWND=0x20006F6 (pressure_max=32767)
[2]:
False

3 — Defining the plugin: position capture

The plugin records every right-click into a clicked_points list that remains accessible from the notebook.

A handler signature is:

def my_handler(viewer: WolfMapViewer, ctx: MouseContext) -> None: ...

MouseContext exposes: | Attribute | Description | |—|—| | ctx.x, ctx.y | Raw world coordinates | | ctx.x_snap, ctx.y_snap | Grid-snapped world coordinates | | ctx.x_pixel, ctx.y_pixel | Screen pixel coordinates | | ctx.shift, ctx.ctrl, ctx.alt | Keyboard modifiers |

[ ]:
from wolfhece._viewer_plugin_handlers import MouseContext

# Collected points — accessible from anywhere in the notebook
clicked_points: list[tuple[float, float]] = []


def _rdown_capture_position(v, ctx: MouseContext) -> None:
    """Record the right-click position and print a summary."""
    clicked_points.append((ctx.x, ctx.y))
    n = len(clicked_points)
    print(f"[Capture #{n}]  X = {ctx.x:.3f}   Y = {ctx.y:.3f}")
    if ctx.shift:
        print("  → Shift held: point flagged as reference")
    # Do NOT call end_action() here — keeps the action alive for multiple clicks.


# Register on this instance only
viewer.register_action(
    'capture position',
    rdown_handler=_rdown_capture_position,
)

print("Plugin 'capture position' registered.")
Plugin 'capture position' enregistré.

4 — Activating the action

Once active, every right-click on the map triggers _rdown_capture_position.

[ ]:
viewer.start_action('capture position', 'Right-click on the map to capture positions…')
print("Action active — click on the map.")
INFO:root:ACTION : Cliquez avec le bouton droit pour capturer des positions…
Action active — cliquez sur la carte.

5 — Inspecting the collected points

[ ]:
# Run this cell at any time to display the points collected so far.
print(f"{len(clicked_points)} point(s) captured:")
for i, (x, y) in enumerate(clicked_points, 1):
    print(f"  {i:3d}.  X = {x:.3f}   Y = {y:.3f}")
0 point(s) capturé(s) :

6 — Deactivating and cleaning up

[ ]:
# End the action (sets viewer.action back to None)
viewer.end_action('End capture position')

# Optional: remove the handler from this instance
viewer.unregister_action('capture position')

print("Action deactivated and handler unregistered.")
INFO:root:ACTION : Fin capture position
Action désactivée et handler dé-enregistré.

7 — Advanced example: motion handler (live preview)

A motion handler can also be registered to provide real-time feedback while the action is active. Here, the current cursor coordinates are displayed in the viewer’s status bar on every mouse move.

[ ]:
def _motion_show_coords(v: WolfMapViewer, ctx: MouseContext) -> None:
    """Display the current cursor coordinates in the viewer status bar."""
    v.set_statusbar_text(f"X={ctx.x:.2f}  Y={ctx.y:.2f}")


viewer.register_action(
    'capture position with preview',
    rdown_handler=_rdown_capture_position,   # reuse the same rdown handler
    motion_handler=_motion_show_coords,
)

viewer.start_action(
    'capture position with preview',
    'Right-click to capture; mouse movement updates the status bar'
)
print("Action with live preview active.")
INFO:root:ACTION : Cliquez (droit) pour capturer ; le mouvement met à jour la barre de statut
Action avec preview active.