{ "cells": [ { "cell_type": "markdown", "id": "e54f1170", "metadata": {}, "source": [ "# Action plugin — minimal example\n", "\n", "This notebook illustrates the **per-instance** registration mechanism for custom mouse handlers in `WolfMapViewer`.\n", "\n", "## Workflow\n", "1. Start wx and create the viewer\n", "2. Define the plugin handler **inline** in the notebook\n", "3. Register the plugin on the instance with `viewer.register_action()`\n", "4. Activate the action with `viewer.start_action()`\n", "5. Every right-click on the map triggers the handler\n", "6. Deactivate / unregister when done\n", "\n", "> Handlers registered on `viewer` are **isolated to that instance** and do not affect other `WolfMapViewer` windows open in the same session." ] }, { "cell_type": "markdown", "id": "2f5842c4", "metadata": {}, "source": [ "## 1 — Imports and wx startup\n", "\n", "> **Important**: in a Jupyter notebook, always use the `%gui wx` magic instead of creating a `wx.App()` manually.\n", "> 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.\n", "> `%gui wx` delegates the loop to IPython, which integrates it correctly into its own async loop." ] }, { "cell_type": "code", "execution_count": 1, "id": "ba302159", "metadata": {}, "outputs": [], "source": [ "import sys\n", "%gui wx\n" ] }, { "cell_type": "markdown", "id": "824ac8fc", "metadata": {}, "source": [ "## 2 — Creating the MapViewer" ] }, { "cell_type": "code", "execution_count": 2, "id": "68fcaafb", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:root:Importing wolfhece modules\n", "INFO:root:wolfhece modules imported\n", "INFO:wolfhece.tablet_wintab:WinTab : contexte ouvert sur HWND=0x20006F6 (pressure_max=32767)\n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from wolfhece.PyDraw import WolfMapViewer\n", "\n", "viewer = WolfMapViewer(None, title=\"Plugin demo\", w=1200, h=800)\n", "viewer.Show()" ] }, { "cell_type": "markdown", "id": "4195ce51", "metadata": {}, "source": [ "## 3 — Defining the plugin: position capture\n", "\n", "The plugin records every right-click into a `clicked_points` list that remains accessible from the notebook.\n", "\n", "A handler signature is:\n", "```python\n", "def my_handler(viewer: WolfMapViewer, ctx: MouseContext) -> None: ...\n", "```\n", "\n", "`MouseContext` exposes:\n", "| Attribute | Description |\n", "|---|---|\n", "| `ctx.x`, `ctx.y` | Raw world coordinates |\n", "| `ctx.x_snap`, `ctx.y_snap` | Grid-snapped world coordinates |\n", "| `ctx.x_pixel`, `ctx.y_pixel` | Screen pixel coordinates |\n", "| `ctx.shift`, `ctx.ctrl`, `ctx.alt` | Keyboard modifiers |" ] }, { "cell_type": "code", "execution_count": null, "id": "929dc350", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Plugin 'capture position' enregistré.\n" ] } ], "source": [ "from wolfhece._viewer_plugin_handlers import MouseContext\n", "\n", "# Collected points — accessible from anywhere in the notebook\n", "clicked_points: list[tuple[float, float]] = []\n", "\n", "\n", "def _rdown_capture_position(v, ctx: MouseContext) -> None:\n", " \"\"\"Record the right-click position and print a summary.\"\"\"\n", " clicked_points.append((ctx.x, ctx.y))\n", " n = len(clicked_points)\n", " print(f\"[Capture #{n}] X = {ctx.x:.3f} Y = {ctx.y:.3f}\")\n", " if ctx.shift:\n", " print(\" → Shift held: point flagged as reference\")\n", " # Do NOT call end_action() here — keeps the action alive for multiple clicks.\n", "\n", "\n", "# Register on this instance only\n", "viewer.register_action(\n", " 'capture position',\n", " rdown_handler=_rdown_capture_position,\n", ")\n", "\n", "print(\"Plugin 'capture position' registered.\")" ] }, { "cell_type": "markdown", "id": "74e0d716", "metadata": {}, "source": [ "## 4 — Activating the action\n", "\n", "Once active, every **right-click** on the map triggers `_rdown_capture_position`." ] }, { "cell_type": "code", "execution_count": null, "id": "620167ab", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:root:ACTION : Cliquez avec le bouton droit pour capturer des positions…\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Action active — cliquez sur la carte.\n" ] } ], "source": [ "viewer.start_action('capture position', 'Right-click on the map to capture positions…')\n", "print(\"Action active — click on the map.\")" ] }, { "cell_type": "markdown", "id": "15bff892", "metadata": {}, "source": [ "## 5 — Inspecting the collected points" ] }, { "cell_type": "code", "execution_count": null, "id": "10e4fa1c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 point(s) capturé(s) :\n" ] } ], "source": [ "# Run this cell at any time to display the points collected so far.\n", "print(f\"{len(clicked_points)} point(s) captured:\")\n", "for i, (x, y) in enumerate(clicked_points, 1):\n", " print(f\" {i:3d}. X = {x:.3f} Y = {y:.3f}\")" ] }, { "cell_type": "markdown", "id": "ad82b84d", "metadata": {}, "source": [ "## 6 — Deactivating and cleaning up" ] }, { "cell_type": "code", "execution_count": null, "id": "91a7900e", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:root:ACTION : Fin capture position\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Action désactivée et handler dé-enregistré.\n" ] } ], "source": [ "# End the action (sets viewer.action back to None)\n", "viewer.end_action('End capture position')\n", "\n", "# Optional: remove the handler from this instance\n", "viewer.unregister_action('capture position')\n", "\n", "print(\"Action deactivated and handler unregistered.\")" ] }, { "cell_type": "markdown", "id": "4d02cdb0", "metadata": {}, "source": [ "## 7 — Advanced example: motion handler (live preview)\n", "\n", "A **motion** handler can also be registered to provide real-time feedback while the action is active.\n", "Here, the current cursor coordinates are displayed in the viewer's status bar on every mouse move." ] }, { "cell_type": "code", "execution_count": null, "id": "9070022d", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:root:ACTION : Cliquez (droit) pour capturer ; le mouvement met à jour la barre de statut\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Action avec preview active.\n" ] } ], "source": [ "def _motion_show_coords(v: WolfMapViewer, ctx: MouseContext) -> None:\n", " \"\"\"Display the current cursor coordinates in the viewer status bar.\"\"\"\n", " v.set_statusbar_text(f\"X={ctx.x:.2f} Y={ctx.y:.2f}\")\n", "\n", "\n", "viewer.register_action(\n", " 'capture position with preview',\n", " rdown_handler=_rdown_capture_position, # reuse the same rdown handler\n", " motion_handler=_motion_show_coords,\n", ")\n", "\n", "viewer.start_action(\n", " 'capture position with preview',\n", " 'Right-click to capture; mouse movement updates the status bar'\n", ")\n", "print(\"Action with live preview active.\")" ] } ], "metadata": { "kernelspec": { "display_name": "python311", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }