wolfhece.plugins.abc

Abstract companion base class.

This module intentionally focuses on AbstractUICompanion.

Related helpers now live in dedicated modules:

For backward compatibility, their main symbols are re-exported here.

Module Contents

class wolfhece.plugins.abc.AbstractCompanionModel[source]

Bases: abc.ABC

Inheritance diagram of wolfhece.plugins.abc.AbstractCompanionModel

Pure business model contract for companion plugins.

Keep all business state and domain transitions in a model class that does not depend on wx/viewer/OpenGL APIs. The UI companion can then compose this model and stay focused on event wiring and rendering.

This default contract is intentionally lightweight to support plugins that only need simple dataclasses. Override reset() when your model must clear transient state.

reset() None[source]

Reset transient domain state (no-op by default).

class wolfhece.plugins.abc.AbstractUICompanion[source]

Bases: abc.ABC

Inheritance diagram of wolfhece.plugins.abc.AbstractUICompanion

Abstract base class for companion objects that integrate with WolfMapViewer.

Viewer access policy

Companion code should use proxy as the primary entry point for viewer integration (actions, menus, dialogs, redraw, OpenGL helpers).

During the migration period, direct viewer access is still possible via the private bridge attribute self.proxy._viewer for advanced cases where no dedicated helper exists yet.

A companion is an object that:

  1. Owns the transient state needed by its interactive actions.

  2. Registers (and later cleans up) custom mouse/keyboard handlers in the viewer’s action dispatch tables.

  3. Optionally appends its own wx.Menu to the viewer’s menu bar

    (declare it through menu_spec()).

Subclasses must implement start(). All other methods are either concrete helpers or optional overrides. In particular, menu_spec() has a no-op default — companions that interact purely via mouse/keyboard events do not need a menu.

proxy: wolfhece.plugins.viewer_proxy.ViewerProxy[source]
model: AbstractCompanionModel | None[source]
create_model() AbstractCompanionModel | None[source]

Create the companion’s optional pure business model.

Override this to keep domain logic independent from UI concerns while staying in the same plugin module (companion + model in one file).

Default implementation returns None.

get_namespace() str[source]

Return the namespace string used to prefix action ids.

The default is the companion’s class name lowercased. Override in a subclass to choose a different prefix:

class MyCompanion(AbstractUICompanion):
    def get_namespace(self) -> str:
        return 'myfeature'
configure()[source]

Configure the companion’s runtime behaviour.

menu_spec() tuple[str, list[MenuEntry]] | None[source]

Declare the companion’s menu as a (title, items) pair.

Override this to add a menu without writing wx code:

def menu_spec(self) -> tuple[str, list[MenuEntry]] | None:
    return (_('My Tool'), [
        MenuItem(_('Run'), self._on_run, _('Execute the tool')),
        SEPARATOR,
        SubMenuSpec(_('Settings'), [
            MenuItem(_('Configure…'), self._on_configure),
        ]),
    ])

Return None (the default) when no top-level menu is needed.

Called automatically by build(). Do not call directly.

menu_host() str[source]

Return where this companion menu should be hosted.

Supported values are:

  • 'companions_root': create a submenu under the shared viewer menu

    “Companions” (default).

  • 'top_level': create a direct top-level menu entry in the viewer

    menubar.

Override this in companions that must remain top-level.

actions_spec() Iterable[ActionSpec | MultiStepSpec] | None[source]

Declare interactive handlers to register with the viewer.

Override this method declaratively by returning ActionSpec and/or MultiStepSpec rows. The proxy performs registration and handler adaptation:

def actions_spec(self):
    return [
        ActionSpec(self._pick.action_id, ldown=self._ldown, key=self._key),
        MultiStepSpec(
            'wall',
            steps=[
                StepSpec(hint='Pick first point', ldown=self._step1),
                StepSpec(hint='Pick second point', ldown=self._step2),
            ],
        ),
    ]

For MultiStepSpec, handlers can return StepTransition values (or equivalent strings) to drive the runtime state machine.

Return None (or an empty iterable) when there is no action to register.

Called automatically by build(). Do not call directly.

start() None[source]

Activate the companion’s primary interactive action.

Default behaviour is declarative and driven by actions_spec():

  • select the ActionSpec marked primary=True,

  • otherwise fall back to the first declared ActionSpec,

  • call ViewerProxy.start_action() with its action_id and optional start_message.

Override this method when startup needs additional business logic.

Raises:

RuntimeError – If no actions are declared or more than one ActionSpec is marked primary.

stop() None[source]

Deactivate the companion’s primary interactive action.

The default implementation calls proxy.end_action(). Override to add post-processing (e.g. printing a summary):

def stop(self) -> None:
    self.proxy.end_action()
    print(f"{len(self.points)} item(s) recorded.")
build() None[source]

Build and attach the companion’s menu to the viewer menubar.

The default implementation calls menu_spec() to build any declared menu, then actions_spec() to wire up interactive handlers. Override this method directly only for advanced cases that the two hooks cannot express.

Preferred approach — override menu_spec() and/or actions_spec():

def menu_spec(self) -> tuple[str, list[MenuEntry]] | None:
    return (_('My Feature'), [
        MenuItem(_('Do something'), self._on_do),
        SEPARATOR,
        SubMenuSpec(_('Tools'), [
            MenuItem(_('Export'), self._on_export),
        ]),
    ])

def actions_spec(self):
    return [
        ActionSpec(self._pick.action_id, ldown=self._ldown),
    ]

Advanced — full wx control (bypass both hooks):

def build(self) -> None:
    if self.proxy._menu is not None:
        return
    import wx
    self.proxy._menu = wx.Menu()
    ...
    self.proxy.register_action(...)

Implementations must be idempotent.

destroy() None[source]

Unregister all custom actions, remove the companion’s menu, and release viewer resources.

Call this when the companion is no longer needed. The method is safe to call multiple times.

Override this method to add companion-specific teardown:

def destroy(self) -> None:
    # custom pre-teardown ...
    super().destroy()
Raises:

RuntimeError – Never — all wx errors are caught and logged.

_iter_action_specs() Iterable[ActionSpec][source]

Yield action specs normalized to ActionSpec.

MultiStepSpec entries are normalized to a synthetic ActionSpec for primary-action selection logic.

_primary_action_spec() ActionSpec | None[source]

Return the action spec used by the default start() method.

Selection rules:

  1. If one spec has primary=True, use it.

  2. If none are marked primary, use the first declared spec.

  3. If multiple specs are marked primary, raise RuntimeError.