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

Skip to content

Commit ee796c5

Browse files
committed
Add location keyword argument to Colorbar
1 parent 98b1f82 commit ee796c5

File tree

3 files changed

+103
-15
lines changed

3 files changed

+103
-15
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
``colorbar`` now has a *location* keyword argument
2+
==================================================
3+
4+
The ``colorbar`` method now supports a *location* keyword argument to more
5+
easily position the color bar. This is useful when providing your own inset
6+
axes using the *cax* keyword argument and behaves similar to the case where
7+
axes are not provided (where the *location* keyword is passed through).
8+
*orientation* and *ticklocation* are no longer required as they are
9+
determined by *location*. *ticklocation* can still be provided if the
10+
automatic setting is not preferred. (*orientation* can also be provided but
11+
must be compatible with the *location*.)
12+
13+
An example is:
14+
15+
.. plot::
16+
:include-source: true
17+
18+
import matplotlib.pyplot as plt
19+
import numpy as np
20+
rng = np.random.default_rng(19680801)
21+
imdata = rng.random((10, 10))
22+
fig, ax = plt.subplots()
23+
im = ax.imshow(imdata)
24+
fig.colorbar(im, cax=ax.inset_axes([0, 1.05, 1, 0.05]),
25+
location='top')

lib/matplotlib/colorbar.py

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
of the colorbar, as that also determines the *orientation*; passing
4343
incompatible values for *location* and *orientation* raises an exception.
4444
45+
ticklocation : {'auto', 'left', 'right', 'top', 'bottom'}
46+
The location of the colorbar ticks. The *ticklocation* location must match
47+
*orientation*. For example, a horizontal colorbar can only have ticks at
48+
the top or the bottom.
49+
4550
fraction : float, default: 0.15
4651
Fraction of original axes to use for colorbar.
4752
@@ -246,13 +251,18 @@ class Colorbar:
246251
alpha : float
247252
The colorbar transparency between 0 (transparent) and 1 (opaque).
248253
249-
orientation : {'vertical', 'horizontal'}
254+
orientation : None or {'vertical', 'horizontal'}
255+
If None, use the value determined by *location*. If both
256+
*orientation* and *location* are None then defaults to 'vertical'.
250257
251258
ticklocation : {'auto', 'left', 'right', 'top', 'bottom'}
252259
253260
drawedges : bool
254261
255262
filled : bool
263+
264+
location : None or {'left', 'right', 'top', 'bottom'}
265+
256266
%(_colormap_kw_doc)s
257267
"""
258268

@@ -264,7 +274,7 @@ def __init__(self, ax, mappable=None, *, cmap=None,
264274
alpha=None,
265275
values=None,
266276
boundaries=None,
267-
orientation='vertical',
277+
orientation=None,
268278
ticklocation='auto',
269279
extend=None,
270280
spacing='uniform', # uniform or proportional
@@ -275,6 +285,7 @@ def __init__(self, ax, mappable=None, *, cmap=None,
275285
extendfrac=None,
276286
extendrect=False,
277287
label='',
288+
location=None,
278289
):
279290

280291
if mappable is None:
@@ -305,14 +316,23 @@ def __init__(self, ax, mappable=None, *, cmap=None,
305316
mappable.colorbar_cid = mappable.callbacks.connect(
306317
'changed', self.update_normal)
307318

319+
location_orientation = _get_orientation_from_location(location)
320+
308321
_api.check_in_list(
309-
['vertical', 'horizontal'], orientation=orientation)
322+
[None, 'vertical', 'horizontal'], orientation=orientation)
310323
_api.check_in_list(
311324
['auto', 'left', 'right', 'top', 'bottom'],
312325
ticklocation=ticklocation)
313326
_api.check_in_list(
314327
['uniform', 'proportional'], spacing=spacing)
315328

329+
if location_orientation is not None and orientation is not None:
330+
if location_orientation != orientation:
331+
raise TypeError(
332+
"location and orientation are mutually exclusive")
333+
else:
334+
orientation = orientation or location_orientation or "vertical"
335+
316336
self.ax = ax
317337
self.ax._axes_locator = _ColorbarAxesLocator(self)
318338

@@ -365,7 +385,8 @@ def __init__(self, ax, mappable=None, *, cmap=None,
365385
self.__scale = None # linear, log10 for now. Hopefully more?
366386

367387
if ticklocation == 'auto':
368-
ticklocation = 'bottom' if orientation == 'horizontal' else 'right'
388+
ticklocation = _get_ticklocation_from_orientation(
389+
orientation) if location is None else location
369390
self.ticklocation = ticklocation
370391

371392
self.set_label(label)
@@ -1330,25 +1351,36 @@ def drag_pan(self, button, key, x, y):
13301351

13311352
def _normalize_location_orientation(location, orientation):
13321353
if location is None:
1333-
location = _api.check_getitem(
1334-
{None: "right", "vertical": "right", "horizontal": "bottom"},
1335-
orientation=orientation)
1354+
location = _get_ticklocation_from_orientation(orientation)
13361355
loc_settings = _api.check_getitem({
1337-
"left": {"location": "left", "orientation": "vertical",
1338-
"anchor": (1.0, 0.5), "panchor": (0.0, 0.5), "pad": 0.10},
1339-
"right": {"location": "right", "orientation": "vertical",
1340-
"anchor": (0.0, 0.5), "panchor": (1.0, 0.5), "pad": 0.05},
1341-
"top": {"location": "top", "orientation": "horizontal",
1342-
"anchor": (0.5, 0.0), "panchor": (0.5, 1.0), "pad": 0.05},
1343-
"bottom": {"location": "bottom", "orientation": "horizontal",
1344-
"anchor": (0.5, 1.0), "panchor": (0.5, 0.0), "pad": 0.15},
1356+
"left": {"location": "left", "anchor": (1.0, 0.5),
1357+
"panchor": (0.0, 0.5), "pad": 0.10},
1358+
"right": {"location": "right", "anchor": (0.0, 0.5),
1359+
"panchor": (1.0, 0.5), "pad": 0.05},
1360+
"top": {"location": "top", "anchor": (0.5, 0.0),
1361+
"panchor": (0.5, 1.0), "pad": 0.05},
1362+
"bottom": {"location": "bottom", "anchor": (0.5, 1.0),
1363+
"panchor": (0.5, 0.0), "pad": 0.15},
13451364
}, location=location)
1365+
loc_settings["orientation"] = _get_orientation_from_location(location)
13461366
if orientation is not None and orientation != loc_settings["orientation"]:
13471367
# Allow the user to pass both if they are consistent.
13481368
raise TypeError("location and orientation are mutually exclusive")
13491369
return loc_settings
13501370

13511371

1372+
def _get_orientation_from_location(location):
1373+
return _api.check_getitem(
1374+
{None: None, "left": "vertical", "right": "vertical",
1375+
"top": "horizontal", "bottom": "horizontal"}, location=location)
1376+
1377+
1378+
def _get_ticklocation_from_orientation(orientation):
1379+
return _api.check_getitem(
1380+
{None: "right", "vertical": "right", "horizontal": "bottom"},
1381+
orientation=orientation)
1382+
1383+
13521384
@_docstring.interpd
13531385
def make_axes(parents, location=None, orientation=None, fraction=0.15,
13541386
shrink=1.0, aspect=20, **kwargs):

lib/matplotlib/tests/test_colorbar.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,3 +1149,34 @@ def test_title_text_loc():
11491149
# colorbar axes, including its extend triangles....
11501150
assert (cb.ax.title.get_window_extent(fig.canvas.get_renderer()).ymax >
11511151
cb.ax.spines['outline'].get_window_extent().ymax)
1152+
1153+
1154+
@check_figures_equal(extensions=["png"])
1155+
def test_passing_location(fig_ref, fig_test):
1156+
ax_ref = fig_ref.add_subplot()
1157+
im = ax_ref.imshow([[0, 1], [2, 3]])
1158+
ax_ref.figure.colorbar(im, cax=ax_ref.inset_axes([0, 1.05, 1, 0.05]),
1159+
orientation="horizontal", ticklocation="top")
1160+
ax_test = fig_test.add_subplot()
1161+
im = ax_test.imshow([[0, 1], [2, 3]])
1162+
ax_test.figure.colorbar(im, cax=ax_test.inset_axes([0, 1.05, 1, 0.05]),
1163+
location="top")
1164+
1165+
1166+
@pytest.mark.parametrize("kwargs,error,message", [
1167+
({'location': 'top', 'orientation': 'vertical'}, TypeError,
1168+
"location and orientation are mutually exclusive"),
1169+
({'location': 'top', 'orientation': 'vertical', 'cax': True}, TypeError,
1170+
"location and orientation are mutually exclusive"), # Different to above
1171+
({'ticklocation': 'top', 'orientation': 'vertical', 'cax': True},
1172+
ValueError, "'top' is not a valid value for position"),
1173+
({'location': 'top', 'extendfrac': (0, None)}, ValueError,
1174+
"invalid value for extendfrac"),
1175+
])
1176+
def test_colorbar_errors(kwargs, error, message):
1177+
fig, ax = plt.subplots()
1178+
im = ax.imshow([[0, 1], [2, 3]])
1179+
if kwargs.get('cax', None) is True:
1180+
kwargs['cax'] = ax.inset_axes([0, 1.05, 1, 0.05])
1181+
with pytest.raises(error, match=message):
1182+
fig.colorbar(im, **kwargs)

0 commit comments

Comments
 (0)