Skip to content

Commit bda2439

Browse files
authored
feat: Allow to replace selection content in with one event emmited (#387)
1 parent e1a3955 commit bda2439

File tree

2 files changed

+27
-3
lines changed

2 files changed

+27
-3
lines changed

src/psygnal/containers/_selection.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from psygnal._signal import Signal
77

8-
from ._evented_set import BailType, EventedOrderedSet, SetEvents
8+
from ._evented_set import BailType, EventedOrderedSet, SetEvents, _reduce_events
99

1010
if TYPE_CHECKING:
1111
from collections.abc import Iterable
@@ -131,8 +131,19 @@ def toggle(self, obj: _T) -> None:
131131

132132
def select_only(self, obj: _T) -> None:
133133
"""Unselect everything but `obj`. Add to selection if not currently selected."""
134-
self.intersection_update({obj})
135-
self.add(obj)
134+
with self.events.items_changed.paused(_reduce_events):
135+
self.intersection_update({obj})
136+
self.add(obj)
137+
138+
def replace_selection(self, new_selection: Iterable[_T]) -> None:
139+
"""Replace the current selection with `new_selection`.
140+
141+
This is equivalent to calling `intersection_update` followed by `update`,
142+
but is more efficient because it only emits a single `items_changed` event.
143+
"""
144+
with self.events.items_changed.paused(_reduce_events):
145+
self.intersection_update(new_selection)
146+
self.update(new_selection)
136147

137148
def _update_active(self) -> None:
138149
"""On a selection event, update the active item based on selection.

tests/containers/test_selection.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,13 @@ def test_active_setter():
5959

6060

6161
def test_select_only():
62+
mock = Mock()
6263
selection = Selection([1, 2])
6364
selection.active = 1
6465
assert selection.active == 1
66+
selection.events.items_changed.connect(mock)
6567
selection.select_only(2)
68+
mock.assert_called_once_with((2,), (1,))
6669
assert selection.active == 2
6770

6871

@@ -94,3 +97,13 @@ def test_emit_change():
9497

9598
def test_hash():
9699
assert hash(Selection())
100+
101+
102+
def test_replace_selection():
103+
mock = Mock()
104+
selection = Selection([1, 2, 3])
105+
106+
selection.events.items_changed.connect(mock)
107+
selection.replace_selection([3, 4, 5])
108+
mock.assert_called_once_with((4, 5), (1, 2))
109+
assert set(selection) == {3, 4, 5}

0 commit comments

Comments
 (0)