Skip to content

Commit 29f7953

Browse files
committed
Implement LastItemHolder for managing last selected items in CurveStatsTool and ImageStatsTool
(cherry picked from commit 95b4027)
1 parent 32fd24b commit 29f7953

File tree

3 files changed

+53
-55
lines changed

3 files changed

+53
-55
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: 10 additions & 27 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,11 +27,15 @@
2827
from plotpy.interfaces import ICurveItemType
2928
from plotpy.items import Marker, XRangeSelection, YRangeSelection
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:
35-
3639
from plotpy.items.label import DataInfoLabel
3740
from plotpy.plot.base import BasePlot
3841
from plotpy.plot.manager import PlotManager
@@ -67,7 +70,7 @@ def __init__(
6770
tip: str | None = None,
6871
) -> None:
6972
super().__init__(manager, toolbar_id, title=title, icon=icon, tip=tip)
70-
self._last_item: weakref.ReferenceType[CurveItem] | None = None
73+
self.last_item_holder = LastItemHolder(ICurveItemType)
7174
self.label: DataInfoLabel | None = None
7275
self.labelfuncs = labelfuncs or self.LABELFUNCS
7376

@@ -173,34 +176,14 @@ def set_labelfuncs(
173176
"""
174177
self.labelfuncs = labelfuncs
175178

176-
def get_last_item(self) -> CurveItem | None:
177-
"""Get last item on which the tool was used"""
178-
if self._last_item is not None:
179-
return self._last_item()
180-
return None
181-
182-
def get_associated_item(self, plot: BasePlot) -> CurveItem | None:
183-
"""Get associated item
184-
185-
Args:
186-
plot: BasePlot instance
187-
188-
Returns:
189-
curve item or None
190-
"""
191-
items = plot.get_selected_items(item_type=ICurveItemType)
192-
if len(items) == 1:
193-
self._last_item = weakref.ref(items[0])
194-
return self.get_last_item()
195-
196179
def get_label_title(self) -> str | None:
197180
"""Return label title"""
198-
curve = self.get_associated_item(self.manager.get_plot())
181+
curve = self.last_item_holder.get()
199182
return curve.title().text() if curve else None
200183

201184
def get_computation_specs(self) -> list[tuple[CurveItem, str, Callable[..., Any]]]:
202185
"""Return computation specs"""
203-
curve = self.get_associated_item(self.manager.get_plot())
186+
curve = self.last_item_holder.get()
204187
return [(curve, label, func) for label, func in self.labelfuncs]
205188

206189
def update_status(self, plot: BasePlot) -> None:
@@ -209,7 +192,7 @@ def update_status(self, plot: BasePlot) -> None:
209192
Args:
210193
plot: BasePlot instance
211194
"""
212-
item = self.get_associated_item(plot)
195+
item = self.last_item_holder.update_from_selection(plot)
213196
self.action.setEnabled(item is not None)
214197

215198

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)