Dragging the stage's averaging span and shock windows.
The handles are spheres viz.marks puts on each slab's floor edge —
left edge, centre, right edge: resize, move, resize, the 2-D region's
own grammar. This class turns interactor events into those drags and
commits them through the same rules the 2-D overlays use
(core.averaging.from_span, core.shocks.drag_settled), so the two
editors cannot disagree about what a drag means.
Split on purpose into a semantic layer (begin / drag_to /
finish) and the VTK plumbing that feeds it: the semantics are what
the tests drive, because a synthetic mouse cannot reach an offscreen
interactor. While a drag is live the interactor style is stood down,
so the camera does not orbit under the handle; the press is also
aborted at our observer so the style never begins a rotation.
Dragging previews by redrawing the marks (their actors replace by
name, a few hundred points) and commits on release — the modal fit's
lesson stands: the expensive recompute happens once, at the end.
Classes:
| Name |
Description |
StageMarksDragger |
One per stage plotter, armed with whatever the marks mean now.
|
Classes
StageMarksDragger
StageMarksDragger(plotter: Any)
One per stage plotter, armed with whatever the marks mean now.
Methods:
| Name |
Description |
arm_averaging |
preview(averaging) redraws the marks; commit(averaging)
|
arm_truncation |
preview(truncation) redraws the marks; commit(truncation)
|
arm_shocks |
preview(shocks) redraws; commit(shocks) stores the
|
begin |
Grab the named handle; returns whether it was one of ours.
|
drag_to |
The pointer moved: redraw the marks where it asks.
|
finish |
The pointer let go: settle and commit through the shared
|
Source code in src/visualdynamics/gui/stage_drag.py
| def __init__(self, plotter: Any) -> None:
self.plotter: Any = plotter
self._context: dict[str, Any] | None = None
self._grab: dict[str, Any] | None = None
self._style = None
iren = getattr(plotter, 'iren', None)
self._iren = getattr(iren, 'interactor', None)
if self._iren is not None:
# priority above the interactor style's, so a grabbed press
# can be aborted before the style starts a camera rotation
self._tags = [
self._iren.AddObserver('LeftButtonPressEvent',
self._pressed, 10.0),
self._iren.AddObserver('MouseMoveEvent',
self._moved, 10.0),
self._iren.AddObserver('LeftButtonReleaseEvent',
self._released, 10.0)]
|
Methods:
arm_averaging
arm_averaging(
averaging: Averaging,
sample_rate: float,
samples: int,
extents,
preview,
commit,
) -> None
preview(averaging) redraws the marks; commit(averaging)
is the window's own drag handler.
Source code in src/visualdynamics/gui/stage_drag.py
| def arm_averaging(self, averaging: Averaging, sample_rate: float,
samples: int, extents, preview, commit) -> None:
"""`preview(averaging)` redraws the marks; `commit(averaging)`
is the window's own drag handler."""
self._context = {
'kind': 'averaging', 'averaging': averaging,
'rate': float(sample_rate), 'samples': int(samples),
'extents': tuple(extents), 'preview': preview,
'commit': commit}
|
arm_truncation
arm_truncation(
truncation,
first: float,
last: float,
extents,
preview,
commit,
) -> None
preview(truncation) redraws the marks; commit(truncation)
is the window's own drag handler. first/last are the
record's walls, which a drag cannot leave.
Source code in src/visualdynamics/gui/stage_drag.py
| def arm_truncation(self, truncation, first: float, last: float,
extents, preview, commit) -> None:
"""`preview(truncation)` redraws the marks; `commit(truncation)`
is the window's own drag handler. `first`/`last` are the
record's walls, which a drag cannot leave."""
self._context = {
'kind': 'truncation', 'truncation': truncation,
'first': float(first), 'last': float(last),
'extents': tuple(extents), 'preview': preview,
'commit': commit}
|
arm_shocks
arm_shocks(
shocks, common: bool, limit, extents, preview, commit
) -> None
preview(shocks) redraws; commit(shocks) stores the
settled series.
Source code in src/visualdynamics/gui/stage_drag.py
| def arm_shocks(self, shocks, common: bool, limit, extents,
preview, commit) -> None:
"""`preview(shocks)` redraws; `commit(shocks)` stores the
settled series."""
self._context = {
'kind': 'shocks', 'shocks': tuple(shocks),
'common': bool(common), 'limit': limit,
'extents': tuple(extents), 'preview': preview,
'commit': commit}
|
begin
begin(name: str, at_seconds: float | None = None) -> bool
Grab the named handle; returns whether it was one of ours.
Source code in src/visualdynamics/gui/stage_drag.py
| def begin(self, name: str, at_seconds: float | None = None) -> bool:
"""Grab the named handle; returns whether it was one of ours."""
context = self._context
if context is None:
return False
matched = _HANDLE.match(name or '')
if matched is None:
return False
averaging_kind, truncation_kind, index, role = matched.groups()
handle_kind = ('averaging' if averaging_kind else
'truncation' if truncation_kind else 'shocks')
if handle_kind != context['kind']:
return False # a stale handle from another view
if context['kind'] == 'averaging':
averaging = context['averaging']
rate = context['rate']
low = averaging.start_sample(rate) / rate
high = averaging.stop(rate)
elif context['kind'] == 'truncation':
truncation = context['truncation']
low, high = truncation.start, truncation.stop
else:
index = int(index)
if index >= len(context['shocks']):
return False
shock = context['shocks'][index]
low, high = shock.start, shock.stop
self._grab = {
'role': role, 'index': None if index is None else int(index),
'low': low, 'high': high,
'anchor': (low + high) / 2.0 if at_seconds is None
else float(at_seconds)}
return True
|
drag_to
drag_to(seconds: float) -> None
The pointer moved: redraw the marks where it asks.
Source code in src/visualdynamics/gui/stage_drag.py
| def drag_to(self, seconds: float) -> None:
"""The pointer moved: redraw the marks where it asks."""
if self._grab is None or self._context is None:
return
self._context['preview'](self._proposal(seconds))
|
finish
finish(seconds: float) -> None
The pointer let go: settle and commit through the shared
rules — or snap back when the drag asked for the impossible.
Source code in src/visualdynamics/gui/stage_drag.py
| def finish(self, seconds: float) -> None:
"""The pointer let go: settle and commit through the shared
rules — or snap back when the drag asked for the impossible."""
grab, context = self._grab, self._context
if grab is None or context is None:
self._grab = None
return
try:
if context['kind'] in ('averaging', 'truncation'):
context['commit'](self._proposal(seconds))
return
low, high = self._span_for(seconds)
settled = drag_settled(context['shocks'], grab['index'],
low, high, context['common'],
context['limit'])
if settled is None:
# nothing changed, or no room: the preview may have
# moved the slab, so the marks go back to the stored
context['preview'](context['shocks'])
else:
context['commit'](settled)
finally:
# the commit re-renders, which re-arms through
# _stage_marks; released after so a half-finished drag
# can never read a cleared grab
self._grab = None
|
Functions: