Skip to content

Commit 07371db

Browse files
authored
Blocked set_clim() callbacks to prevent inconsistent state (#29522) (#29590)
* Blocked set_clim() callbacks to prevent inconsistent state (#29522) * added test for set_clim() callback count * Relocated test_set_clim_emits_single_callback to test_colors.py * test case corrections --------- Co-authored-by: prafulgulani555 <59774145+prafulgulani555@users.noreply.github.com>
1 parent ed3b739 commit 07371db

File tree

2 files changed

+33
-5
lines changed

2 files changed

+33
-5
lines changed

lib/matplotlib/colorizer.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -261,16 +261,27 @@ def set_clim(self, vmin=None, vmax=None):
261261
.. ACCEPTS: (vmin: float, vmax: float)
262262
"""
263263
# If the norm's limits are updated self.changed() will be called
264-
# through the callbacks attached to the norm
264+
# through the callbacks attached to the norm, this causes an inconsistent
265+
# state, to prevent this blocked context manager is used
265266
if vmax is None:
266267
try:
267268
vmin, vmax = vmin
268269
except (TypeError, ValueError):
269270
pass
270-
if vmin is not None:
271-
self.norm.vmin = colors._sanitize_extrema(vmin)
272-
if vmax is not None:
273-
self.norm.vmax = colors._sanitize_extrema(vmax)
271+
272+
orig_vmin_vmax = self.norm.vmin, self.norm.vmax
273+
274+
# Blocked context manager prevents callbacks from being triggered
275+
# until both vmin and vmax are updated
276+
with self.norm.callbacks.blocked(signal='changed'):
277+
if vmin is not None:
278+
self.norm.vmin = colors._sanitize_extrema(vmin)
279+
if vmax is not None:
280+
self.norm.vmax = colors._sanitize_extrema(vmax)
281+
282+
# emit a update signal if the limits are changed
283+
if orig_vmin_vmax != (self.norm.vmin, self.norm.vmax):
284+
self.norm.callbacks.process('changed')
274285

275286
def get_clim(self):
276287
"""

lib/matplotlib/tests/test_colors.py

+17
Original file line numberDiff line numberDiff line change
@@ -1605,6 +1605,23 @@ def test_norm_deepcopy():
16051605
assert norm2.vmin == norm.vmin
16061606

16071607

1608+
def test_set_clim_emits_single_callback():
1609+
data = np.array([[1, 2], [3, 4]])
1610+
fig, ax = plt.subplots()
1611+
image = ax.imshow(data, cmap='viridis')
1612+
1613+
callback = unittest.mock.Mock()
1614+
image.norm.callbacks.connect('changed', callback)
1615+
1616+
callback.assert_not_called()
1617+
1618+
# Call set_clim() to update the limits
1619+
image.set_clim(1, 5)
1620+
1621+
# Assert that only one "changed" callback is sent after calling set_clim()
1622+
callback.assert_called_once()
1623+
1624+
16081625
def test_norm_callback():
16091626
increment = unittest.mock.Mock(return_value=None)
16101627

0 commit comments

Comments
 (0)