"""Drowning-victim companion object for WolfMapViewer.
All drowning-simulation management logic lives here.
``WolfMapViewer`` holds a single instance as ``self._drowning`` and exposes
one-line delegators so that external callers remain unaffected.
Design notes
------------
* ``DrowningManager`` owns the ``active_drowning`` reference, the
``mydrownings`` list, and all menu state (``menudrowning`` hierarchy +
check-item references).
* The wx parent for dialogs and menus is always ``self._viewer``.
* ``menu_build()`` is idempotent — the first call creates and appends the
"Drowning" menu to the menubar; subsequent calls are no-ops.
* ``on_menu()`` is bound to the wx menu and dispatches all drowning commands.
* ``new_drowning()`` handles both "Create" and "Add" from the object toolbar.
* ``register()`` is called from ``WolfMapViewer.add_object`` to record a
newly created ``Drowning_victim_Viewer`` instance and update the active
reference.
"""
from __future__ import annotations
import logging
import os
from typing import TYPE_CHECKING
import wx
from .drowning_victims.drowning_class import Drowning_victim_Viewer
from .PyTranslate import _
if TYPE_CHECKING:
from .PyDraw import WolfMapViewer
__all__ = ['DrowningManager']
[docs]
class DrowningManager:
"""Companion object that owns drowning-victim simulation state.
Instantiated once as ``viewer._drowning = DrowningManager(viewer)`` inside
``WolfMapViewer.__init__``.
"""
def __init__(self, viewer: 'WolfMapViewer') -> None:
# ── Object registry ──────────────────────────────────────────────────
[docs]
self.mydrownings: list['Drowning_victim_Viewer'] = []
[docs]
self.active: 'Drowning_victim_Viewer | None' = None
# ── Menu state ───────────────────────────────────────────────────────
# Check-items (created once in menu_build)
[docs]
self._item_plot_runs: wx.MenuItem | None = None
[docs]
self._item_plot_cells: wx.MenuItem | None = None
[docs]
self._item_plot_kde: wx.MenuItem | None = None
# ------------------------------------------------------------------
# Object registration (called by WolfMapViewer.add_object)
# ------------------------------------------------------------------
[docs]
def register(self, newobj: 'Drowning_victim_Viewer') -> None:
"""Add *newobj* to the registry and make it the active drowning."""
self.mydrownings.append(newobj)
self.active = newobj
# ------------------------------------------------------------------
# Menu construction (idempotent)
# ------------------------------------------------------------------
# ------------------------------------------------------------------
# Menu event handler
# ------------------------------------------------------------------
[docs]
def _on_last_result(self, event: wx.MenuEvent) -> None:
viewer = self._viewer
if self.active is None:
logging.warning(_('No active drowning ! -- Please activate a drowning first'))
return
self.active.read_last_result()
viewer.Refresh()
viewer._update_tooltip()
[docs]
def _on_explore(self, event: wx.MenuEvent) -> None:
viewer = self._viewer
if self.active is None:
logging.warning(_('No active drowning ! -- Please activate a drowning first'))
return
from ._sim_panels import Drowning_Explorer
which = self.active
viewer.sim_explorers[which] = Drowning_Explorer(
viewer,
title=f'Explore drowning results: {self.active.idx}',
mapviewer=viewer,
sim=which,
)
[docs]
def _on_plot_runs(self, event: wx.MenuEvent) -> None:
viewer = self._viewer
if self._item_plot_runs.IsChecked():
self.active.prepare_plot_runs_positions()
else:
self.active.reset_plot_runs_positions()
viewer._update_tooltip()
viewer.Refresh()
[docs]
def _on_plot_cells(self, event: wx.MenuEvent) -> None:
viewer = self._viewer
if self._item_plot_cells.IsChecked():
self.active.prepare_plot_cells_positions()
else:
self.active.reset_plot_cells_positions()
viewer._update_tooltip()
viewer.Refresh()
[docs]
def _on_plot_kde(self, event: wx.MenuEvent) -> None:
viewer = self._viewer
if self._item_plot_kde.IsChecked():
self.active.prepare_plot_kde()
else:
self.active.reset_plot_kde()
viewer._update_tooltip()
viewer.Refresh()
[docs]
def _on_zoom_hotspots(self, event: wx.MenuEvent) -> None:
from ._sim_panels import Memory_Views, Memory_Views_GUI
viewer = self._viewer
viewer.memory_views = Memory_Views()
self.active.zoom_on_hotspots(viewer.memory_views)
viewer._memory_views_gui = Memory_Views_GUI(
viewer, _('Memory view manager'), viewer.memory_views, mapviewer=viewer,
)
[docs]
def _on_bodies(self, event: wx.MenuEvent) -> None:
self.active.get_bodies_characteristics()
[docs]
def _on_vpos(self, event: wx.MenuEvent) -> None:
self.active.get_vertical_position_proportion()
[docs]
def _on_video(self, event: wx.MenuEvent) -> None:
logging.info(_('Not yet implemented'))
# ------------------------------------------------------------------
# Object creation (called by WolfMapViewer menu handlers)
# ------------------------------------------------------------------
[docs]
def new_drowning(self, itemlabel: str) -> None:
"""Handle 'Create a drowning...' and 'Add a drowning result...' items."""
from .drowning_victims.drowning_class import Drowning_victim_Viewer
viewer = self._viewer
if itemlabel == _('Create a drowning...'):
new = Drowning_victim_Viewer(mapviewer=viewer)
viewer.add_object(which='drowning', newobj=new, ToCheck=True)
elif itemlabel == _('Add a drowning result...'):
dialog = wx.DirDialog(
None,
_("Select the folder containing your drowning"),
style=wx.DD_DEFAULT_STYLE,
)
if dialog.ShowModal() != wx.ID_OK:
dialog.Destroy()
return
filedir = dialog.GetPath()
dialog.Destroy()
if not os.path.exists(os.path.join(filedir, "Results.npz")):
logging.error(_("The selected folder does not contain any Results.npz"))
return
new = Drowning_victim_Viewer(mapviewer=viewer, filedir=filedir)
new.file_drowning = 1
viewer.add_object(which='drowning', newobj=new, ToCheck=True)
new.load_results()
new.time_id = len(new.wanted_time) - 2
new.init_plot()
self.menu_build()