Skip to content

Commit 9f0a60e

Browse files
authored
Merge pull request #28666 from meeseeksmachine/auto-backport-of-pr-28546-on-v3.9.1-doc
Backport PR #28546 on branch v3.9.1-doc (DOC: Clarify/simplify example of multiple images with one colorbar)
2 parents 0373818 + 6c7dbc0 commit 9f0a60e

File tree

1 file changed

+51
-37
lines changed

1 file changed

+51
-37
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
"""
2-
===============
3-
Multiple images
4-
===============
2+
=================================
3+
Multiple images with one colorbar
4+
=================================
55
6-
Make a set of images with a single colormap, norm, and colorbar.
6+
Use a single colorbar for multiple images.
7+
8+
Currently, a colorbar can only be connected to one image. The connection
9+
guarantees that the data coloring is consistent with the colormap scale
10+
(i.e. the color of value *x* in the colormap is used for coloring a data
11+
value *x* in the image).
12+
13+
If we want one colorbar to be representative for multiple images, we have
14+
to explicitly ensure consistent data coloring by using the same data
15+
normalization for all the images. We ensure this by explicitly creating a
16+
``norm`` object that we pass to all the image plotting methods.
717
"""
818

919
import matplotlib.pyplot as plt
@@ -12,47 +22,53 @@
1222
from matplotlib import colors
1323

1424
np.random.seed(19680801)
15-
Nr = 3
16-
Nc = 2
1725

18-
fig, axs = plt.subplots(Nr, Nc)
26+
datasets = [
27+
(i+1)/10 * np.random.rand(10, 20)
28+
for i in range(4)
29+
]
30+
31+
fig, axs = plt.subplots(2, 2)
1932
fig.suptitle('Multiple images')
2033

21-
images = []
22-
for i in range(Nr):
23-
for j in range(Nc):
24-
# Generate data with a range that varies from one plot to the next.
25-
data = ((1 + i + j) / 10) * np.random.rand(10, 20)
26-
images.append(axs[i, j].imshow(data))
27-
axs[i, j].label_outer()
34+
# create a single norm to be shared across all images
35+
norm = colors.Normalize(vmin=np.min(datasets), vmax=np.max(datasets))
2836

29-
# Find the min and max of all colors for use in setting the color scale.
30-
vmin = min(image.get_array().min() for image in images)
31-
vmax = max(image.get_array().max() for image in images)
32-
norm = colors.Normalize(vmin=vmin, vmax=vmax)
33-
for im in images:
34-
im.set_norm(norm)
37+
images = []
38+
for ax, data in zip(axs.flat, datasets):
39+
images.append(ax.imshow(data, norm=norm))
3540

3641
fig.colorbar(images[0], ax=axs, orientation='horizontal', fraction=.1)
3742

38-
39-
# Make images respond to changes in the norm of other images (e.g. via the
40-
# "edit axis, curves and images parameters" GUI on Qt), but be careful not to
41-
# recurse infinitely!
42-
def update(changed_image):
43-
for im in images:
44-
if (changed_image.get_cmap() != im.get_cmap()
45-
or changed_image.get_clim() != im.get_clim()):
46-
im.set_cmap(changed_image.get_cmap())
47-
im.set_clim(changed_image.get_clim())
48-
49-
50-
for im in images:
51-
im.callbacks.connect('changed', update)
52-
5343
plt.show()
5444

5545
# %%
46+
# The colors are now kept consistent across all images when changing the
47+
# scaling, e.g. through zooming in the colorbar or via the "edit axis,
48+
# curves and images parameters" GUI of the Qt backend. This is sufficient
49+
# for most practical use cases.
50+
#
51+
# Advanced: Additionally sync the colormap
52+
# ----------------------------------------
53+
#
54+
# Sharing a common norm object guarantees synchronized scaling because scale
55+
# changes modify the norm object in-place and thus propagate to all images
56+
# that use this norm. This approach does not help with synchronizing colormaps
57+
# because changing the colormap of an image (e.g. through the "edit axis,
58+
# curves and images parameters" GUI of the Qt backend) results in the image
59+
# referencing the new colormap object. Thus, the other images are not updated.
60+
#
61+
# To update the other images, sync the
62+
# colormaps using the following code::
63+
#
64+
# def sync_cmaps(changed_image):
65+
# for im in images:
66+
# if changed_image.get_cmap() != im.get_cmap():
67+
# im.set_cmap(changed_image.get_cmap())
68+
#
69+
# for im in images:
70+
# im.callbacks.connect('changed', sync_cmaps)
71+
#
5672
#
5773
# .. admonition:: References
5874
#
@@ -63,6 +79,4 @@ def update(changed_image):
6379
# - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
6480
# - `matplotlib.colors.Normalize`
6581
# - `matplotlib.cm.ScalarMappable.set_cmap`
66-
# - `matplotlib.cm.ScalarMappable.set_norm`
67-
# - `matplotlib.cm.ScalarMappable.set_clim`
6882
# - `matplotlib.cbook.CallbackRegistry.connect`

0 commit comments

Comments
 (0)