Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bf64054

Browse files
prafulgulanimeeseeksmachine
authored andcommitted
Backport PR matplotlib#29590: Blocked set_clim() callbacks to prevent inconsistent state (matplotlib#29522)
1 parent c38e0f7 commit bf64054

File tree

2 files changed

+33
-5
lines changed

2 files changed

+33
-5
lines changed

lib/matplotlib/colorizer.py

Lines changed: 16 additions & 5 deletions
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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,23 @@ def test_norm_deepcopy():
15531553
assert norm2.vmin == norm.vmin
15541554

15551555

1556+
def test_set_clim_emits_single_callback():
1557+
data = np.array([[1, 2], [3, 4]])
1558+
fig, ax = plt.subplots()
1559+
image = ax.imshow(data, cmap='viridis')
1560+
1561+
callback = unittest.mock.Mock()
1562+
image.norm.callbacks.connect('changed', callback)
1563+
1564+
callback.assert_not_called()
1565+
1566+
# Call set_clim() to update the limits
1567+
image.set_clim(1, 5)
1568+
1569+
# Assert that only one "changed" callback is sent after calling set_clim()
1570+
callback.assert_called_once()
1571+
1572+
15561573
def test_norm_callback():
15571574
increment = unittest.mock.Mock(return_value=None)
15581575

0 commit comments

Comments
 (0)