Skip to content

Commit 95b4027

Browse files
committed
Implement LastItemHolder for managing last selected items in CurveStatsTool and ImageStatsTool
1 parent 58ce252 commit 95b4027

File tree

3 files changed

+52
-53
lines changed

3 files changed

+52
-53
lines changed

plotpy/tools/base.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
from qtpy.QtCore import QPointF
2424

25+
from plotpy.interfaces import ICurveItemType, IImageItemType
26+
from plotpy.items import BaseImageItem, CurveItem
2527
from plotpy.plot import BasePlot, PlotManager
2628

2729

@@ -644,3 +646,40 @@ def end_rect(
644646
if self.switch_to_default_tool:
645647
shape = self.get_last_final_shape()
646648
plot.set_active_item(shape)
649+
650+
651+
class LastItemHolder:
652+
"""Class to hold a weak reference to the last item"""
653+
654+
def __init__(self, item_type: IImageItemType | ICurveItemType) -> None:
655+
self._item_type = item_type
656+
self._last_item: weakref.ReferenceType[CurveItem | BaseImageItem] | None = None
657+
658+
def set(self, item: CurveItem | BaseImageItem) -> None:
659+
"""Set the last item
660+
661+
Args:
662+
item: BaseImageItem instance
663+
"""
664+
self._last_item = weakref.ref(item)
665+
666+
def get(self) -> CurveItem | BaseImageItem | None:
667+
"""Get the last item
668+
669+
Returns:
670+
BaseImageItem instance or None
671+
"""
672+
if self._last_item is not None:
673+
return self._last_item()
674+
return None
675+
676+
def update_from_selection(self, plot: BasePlot) -> CurveItem | BaseImageItem | None:
677+
"""Update the last item from the selected items of the plot, and return it.
678+
679+
Args:
680+
plot: BasePlot instance
681+
"""
682+
items = plot.get_selected_items(item_type=self._item_type)
683+
if len(items) == 1:
684+
self.set(items[0])
685+
return self.get()

plotpy/tools/curve.py

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
from __future__ import annotations
55

6-
import weakref
76
from typing import TYPE_CHECKING, Any, Callable
87

98
import numpy as np
@@ -28,7 +27,12 @@
2827
from plotpy.interfaces import ICurveItemType
2928
from plotpy.items import Marker, XRangeSelection
3029
from plotpy.items.curve.base import CurveItem
31-
from plotpy.tools.base import DefaultToolbarID, InteractiveTool, ToggleTool
30+
from plotpy.tools.base import (
31+
DefaultToolbarID,
32+
InteractiveTool,
33+
LastItemHolder,
34+
ToggleTool,
35+
)
3236
from plotpy.tools.cursor import BaseCursorTool
3337

3438
if TYPE_CHECKING:
@@ -66,7 +70,7 @@ def __init__(
6670
tip: str | None = None,
6771
) -> None:
6872
super().__init__(manager, toolbar_id, title=title, icon=icon, tip=tip)
69-
self._last_item: weakref.ReferenceType[CurveItem] | None = None
73+
self.last_item_holder = LastItemHolder(ICurveItemType)
7074
self.label: DataInfoLabel | None = None
7175
if labelfuncs is None:
7276
labelfuncs: tuple[tuple[str, Callable[..., Any]], ...] = (
@@ -102,12 +106,6 @@ def set_labelfuncs(
102106
"""
103107
self.labelfuncs = labelfuncs
104108

105-
def get_last_item(self) -> CurveItem | None:
106-
"""Get last item on which the tool was used"""
107-
if self._last_item is not None:
108-
return self._last_item()
109-
return None
110-
111109
def create_shape(self) -> XRangeSelection:
112110
"""Create shape associated with the tool"""
113111
return XRangeSelection(0, 0)
@@ -119,7 +117,7 @@ def create_label(self) -> DataInfoLabel:
119117
from plotpy.builder import make
120118

121119
plot = self.manager.get_plot()
122-
curve = self.get_associated_item(plot)
120+
curve = self.last_item_holder.update_from_selection(plot)
123121
specs = [(curve, label, func) for label, func in self.labelfuncs]
124122
title: QwtText = curve.title()
125123
label = make.computations(self.shape, "TL", specs, title.text())
@@ -151,27 +149,13 @@ def end_move(self, filter: StatefulEventFilter, event: QG.QMouseEvent) -> None:
151149
filter.plot.add_item_with_z_offset(self.label, SHAPE_Z_OFFSET)
152150
self.label = None
153151

154-
def get_associated_item(self, plot: BasePlot) -> CurveItem | None:
155-
"""Get associated item
156-
157-
Args:
158-
plot: BasePlot instance
159-
160-
Returns:
161-
curve item or None
162-
"""
163-
items = plot.get_selected_items(item_type=ICurveItemType)
164-
if len(items) == 1:
165-
self._last_item = weakref.ref(items[0])
166-
return self.get_last_item()
167-
168152
def update_status(self, plot: BasePlot) -> None:
169153
"""Update tool status
170154
171155
Args:
172156
plot: BasePlot instance
173157
"""
174-
item = self.get_associated_item(plot)
158+
item = self.last_item_holder.update_from_selection(plot)
175159
self.action.setEnabled(item is not None)
176160

177161

plotpy/tools/image.py

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
from __future__ import annotations
55

6-
import weakref
76
from typing import TYPE_CHECKING, Any, Callable, Literal
87

98
from guidata.configtools import get_icon
@@ -39,6 +38,7 @@
3938
DefaultToolbarID,
4039
GuiTool,
4140
InteractiveTool,
41+
LastItemHolder,
4242
PanelTool,
4343
ToggleTool,
4444
)
@@ -243,7 +243,7 @@ def __init__(
243243
icon,
244244
tip,
245245
)
246-
self._last_item = None
246+
self.last_item_holder = LastItemHolder(IImageItemType)
247247
self.stats_func = stats_func
248248
self.replace_stats = replace
249249

@@ -263,16 +263,6 @@ def set_stats_func(
263263
self.stats_func = stats_func
264264
self.replace_stats = replace
265265

266-
def get_last_item(self) -> BaseImageItem | None:
267-
"""Last image item getter
268-
269-
Returns:
270-
Returns last image item or None
271-
"""
272-
if self._last_item is not None:
273-
return self._last_item()
274-
return None
275-
276266
def create_shape(self) -> tuple[ImageStatsRectangle, Literal[0], Literal[2]]:
277267
"""Returns a new ImageStatsRectangle instance and the index of handles to
278268
display.
@@ -311,7 +301,7 @@ def register_shape(self, shape: ImageStatsRectangle, final=False) -> None:
311301
final: unused argument. Defaults to False.
312302
"""
313303
plot = shape.plot()
314-
image = self.get_last_item()
304+
image = self.last_item_holder.get()
315305
if plot is not None and image is not None:
316306
plot.unselect_all()
317307
plot.set_active_item(shape)
@@ -326,28 +316,14 @@ def handle_final_shape(self, shape: ImageStatsRectangle) -> None:
326316
super().handle_final_shape(shape)
327317
self.register_shape(shape, final=True)
328318

329-
def get_associated_item(self, plot: BasePlot) -> BaseImageItem | None:
330-
"""Return a reference to the last image item associated with the tool
331-
332-
Args:
333-
plot: Plot instance
334-
335-
Returns:
336-
Reference to the last image item associated with the tool
337-
"""
338-
items = plot.get_selected_items(item_type=IImageItemType)
339-
if len(items) == 1:
340-
self._last_item = weakref.ref(items[0])
341-
return self.get_last_item()
342-
343319
def update_status(self, plot: BasePlot) -> None:
344320
"""Update tool status if the plot type is not PlotType.CURVE.
345321
346322
Args:
347323
plot: Plot instance
348324
"""
349325
if update_image_tool_status(self, plot):
350-
item = self.get_associated_item(plot)
326+
item = self.last_item_holder.update_from_selection(plot)
351327
self.action.setEnabled(item is not None)
352328

353329

0 commit comments

Comments
 (0)