"""Shared base companion for WMS plugins.
All WMS companions inherit from :class:`WmsLayerCompanion` instead of
:class:`AbstractUICompanion` directly. The base class provides:
* automatic layer tracking via :meth:`_add_wms`,
* automatic tree cleanup in :meth:`menu_destroy` when the plugin is
disabled (removes both the tree items and the objects from
``mywmsback`` / ``mywmsfore``).
"""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from ..plugins.abc import AbstractUICompanion
if TYPE_CHECKING:
pass
__all__ = ['WmsLayerCompanion']
[docs]
class WmsLayerCompanion(AbstractUICompanion):
"""Base companion for plugins that load WMS layers.
Subclasses only need to implement :meth:`_do_load_layers` and call
:meth:`_add_wms` instead of ``self._viewer.add_object`` directly.
:meth:`menu_build` and :meth:`start` are already wired up.
On deactivation (:meth:`menu_destroy`), all layers added via
:meth:`_add_wms` are removed from the viewer tree and from
``mywmsback`` / ``mywmsfore``.
"""
# list of (imagetexture_obj, which) accumulated by _add_wms()
# ------------------------------------------------------------------
# AbstractUICompanion interface
# ------------------------------------------------------------------
[docs]
def build(self) -> None:
"""Load WMS layers immediately on plugin activation."""
self._loaded_objects = []
self._do_load_layers()
[docs]
def start(self) -> None:
"""Reload all layers (e.g. called from a notebook)."""
if not hasattr(self, '_loaded_objects'):
self._loaded_objects = []
self._do_load_layers()
[docs]
def destroy(self) -> None:
"""Remove all tracked WMS layers from the tree and internal lists."""
v = self.proxy._viewer
for obj, which in list(getattr(self, '_loaded_objects', [])):
wms_list = v.mywmsback if which == 'wmsback' else v.mywmsfore
if obj not in wms_list:
continue
try:
v.removeobj_from_id(obj.idx)
except Exception as exc:
logging.debug('%s.destroy: removeobj_from_id failed for %r: %s',
type(self).__name__, obj.idx, exc)
# removeobj_from_id does not clean mywmsback/mywmsfore — do it here
if obj in wms_list:
wms_list.remove(obj)
if hasattr(self, '_loaded_objects'):
self._loaded_objects.clear()
super().destroy()
# ------------------------------------------------------------------
# Helper used by subclasses
# ------------------------------------------------------------------
[docs]
def _add_wms(self, which: str, obj, id: str) -> None:
"""Add *obj* to the viewer as a WMS layer and track it for cleanup.
:param which: ``'wmsback'`` or ``'wmsfore'``.
:param obj: An :class:`~wolfhece.wolf_texture.imagetexture` instance.
:param id: The tree label / identifier for the layer.
"""
self.proxy._viewer.add_object(which=which, newobj=obj, ToCheck=False, id=id)
self._loaded_objects.append((obj, which))
# ------------------------------------------------------------------
# To be overridden by each concrete WMS companion
# ------------------------------------------------------------------
[docs]
def _do_load_layers(self) -> None:
"""Load and register all WMS layers for this plugin.
Concrete subclasses must override this method and call
:meth:`_add_wms` for each layer.
"""
raise NotImplementedError(
f'{type(self).__name__} must implement _do_load_layers()'
)