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.

  • The hooks are functions. Each hook can be used to manage a specific interaction with the user (mainly mouse actions).

  • Hooks are grouped under an action so that you can define several actions, each handling one or more hooks.

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:root:Plugin manager init: skipping auto-discovery because no global configuration is available yet.
[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 |

[3]:
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' registered.

4 — Activating the action

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

[4]:
viewer.start_action('capture position', 'Right-click on the map to capture positions…')
print("Action active — click on the map.")
INFO:root:ACTION : Right-click on the map to capture positions…
Action active — click on the map.
[Capture #1]  X = 27.137   Y = 26.471
[Capture #2]  X = 23.142   Y = 16.937

5 — Inspecting the collected points

[6]:
# 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}")
3 point(s) captured:
    1.  X = 27.137   Y = 26.471
    2.  X = 23.142   Y = 16.937
    3.  X = 12.543   Y = 17.044

6 — Deactivating and cleaning up

[7]:
# 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 : End capture position
Action deactivated and handler unregistered.

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 : Right-click to capture; mouse movement updates the status bar
Action with live preview active.
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.