Companion sans menu — exemple minimal

Ce notebook montre le compagnon le plus court possible :

  • Aucun menu — pas de menu_build() à écrire

  • Aucun import OpenGL — le dessin est géré par les helpers de la classe de base

  • Une seule responsabilité : clic droit → ajoute un point, qui est immédiatement dessiné sur la carte

La classe entière tient en une vingtaine de lignes.

Pourquoi se passer de menu ?

Depuis le notebook, les actions se déclenchent par code (comp.start(), comp.stop()).
Un menu n’est utile que si l’utilisateur final doit déclencher l’action depuis l’interface graphique.
Pour une session d’exploration ou de débogage, il est inutile.

Ce que fait AbstractCompanion pour vous

Méthode héritée

Rôle

self._action_id('x')

Construit un identifiant namespaced sans collision

self._register_action(id, rdown=..., paint=...)

Enregistre les handlers sur le viewer

self._start_action(id, hint)

Active l’action et affiche l’indice dans la barre de statut

self._end_action()

Désactive l’action

self._force_redraw()

Force un rafraîchissement du viewer (viewer.Paint())

self._viewport_fraction(f)

Renvoie f × largeur_visible — taille qui suit le zoom

self._draw_crosses(points, half_size)

Dessine des croix aux positions données

self.destroy()

Désenregistre toutes les actions d’un seul appel

1 — Démarrage wx

[1]:
import sys
%gui wx

2 — Créer le viewer

[2]:
from wolfhece.PyDraw import WolfMapViewer

viewer = WolfMapViewer(None, title="Companion sans menu", w=1200, h=800)
viewer.Show()
INFO:root:Importing wolfhece modules
INFO:root:wolfhece modules imported
[2]:
False

3 — La classe compagnon

Seule obligation : implémenter start() (méthode abstraite de AbstractCompanion).
Tout le reste est optionnel.
[ ]:
from wolfhece._menu_companion_abc import AbstractCompanion, MouseContext


class DotCompanion(AbstractCompanion):
    """Collecte des clics droits et les dessine sur la carte.

    Pas de menu, pas d'import OpenGL — le compagnon minimal.
    """

    def __init__(self, viewer):
        super().__init__(viewer)
        #: Points collectés — lisibles depuis le notebook.
        self.points: list[tuple[float, float]] = []

    # ── Méthode abstraite obligatoire ─────────────────────────────────────
    def start(self) -> None:
        """Enregistre les handlers et active l'action."""
        self._register_action(
            self._action_id('pick'),
            rdown=self._rdown,
            paint=self._paint,
        )
        self._start_action(self._action_id('pick'), 'Clic droit pour ajouter un point…')

    # ── stop() est hérité : appelle self._end_action() par défaut ─────────

    # ── Handlers privés ───────────────────────────────────────────────────
    def _rdown(self, viewer, ctx: MouseContext) -> None:
        """Enregistre le point et redessine."""
        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._force_redraw()

    def _paint(self, viewer) -> None:
        """Dessine une croix à chaque point collecté."""
        self._draw_crosses(self.points, self._viewport_fraction(0.01))

4 — Instancier et démarrer

Pas de menu_build() à appeler — on démarre directement.

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

print(comp)
print("Action active — clic droit sur la carte pour ajouter des 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 — Inspecter les points collectés

[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}")
4 point(s) :
    1.  X=5.353   Y=27.536
    2.  X=26.764   Y=29.880
    3.  X=7.271   Y=16.884
    4.  X=30.492   Y=14.328

6 — Arrêter et nettoyer

[6]:
comp.stop()     # désactive l'action (points conservés)
comp.destroy()  # désenregistre tous les handlers

print("Compagnon détruit.")
print(comp)
INFO:root:ACTION :
Compagnon détruit.
<DotCompanion ns='dotcompanion' menu=not built actions=[—]>

Récapitulatif — classe complète

from wolfhece._menu_companion_abc import AbstractCompanion
from wolfhece._viewer_plugin_handlers import MouseContext


class DotCompanion(AbstractCompanion):

    def __init__(self, viewer):
        super().__init__(viewer)
        self.points: list[tuple[float, float]] = []

    def start(self) -> None:
        self._register_action(
            self._action_id('pick'),
            rdown=self._rdown,
            paint=self._paint,
        )
        self._start_action(self._action_id('pick'), 'Clic droit pour ajouter un point…')

    def _rdown(self, viewer, ctx: MouseContext) -> None:
        self.points.append((ctx.x_snap, ctx.y_snap))
        self._force_redraw()

    def _paint(self, viewer) -> None:
        self._draw_crosses(self.points, self._viewport_fraction(0.01))

Prochaines étapes

Pour ajouter…

Voir…

Un menu contextuel

plugin_companion_example.ipynb

Sélection, undo, raccourcis clavier, hook paint complet

plugin_companion_complete.ipynb

Superposition temporaire d’un second compagnon

plugin_companion_overload.ipynb