From 1e383499c7e43b7383316f84238cc4599db2bd78 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 20 Mar 2022 16:21:42 -0400 Subject: [PATCH 01/12] test, first commit --- README2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 README2 diff --git a/README2 b/README2 new file mode 100644 index 000000000000..5f12aa4163dd --- /dev/null +++ b/README2 @@ -0,0 +1 @@ +ana and caroline From 3183036f5df2c7fbfa9dc57c764608b7f9a093d3 Mon Sep 17 00:00:00 2001 From: bromberc Date: Sun, 20 Mar 2022 16:27:12 -0400 Subject: [PATCH 02/12] first --- README2 | 1 + 1 file changed, 1 insertion(+) diff --git a/README2 b/README2 index 5f12aa4163dd..dd4136e69f94 100644 --- a/README2 +++ b/README2 @@ -1 +1,2 @@ ana and caroline +test git push From fff5a42d3ed4c9f52bf4f80e6c2a395a71eab615 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 27 Mar 2022 15:12:23 -0400 Subject: [PATCH 03/12] update test --- lib/matplotlib/figure.py | 55 ++++++++++++++++++++++++++++++++++++++++ test_align_titles.py | 18 +++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 test_align_titles.py diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 974e46d6e4b3..24824cc89749 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -174,6 +174,7 @@ def __init__(self, **kwargs): # see self.align_xlabels and self.align_ylabels and # axis._get_tick_boxes_siblings self._align_label_groups = {"x": cbook.Grouper(), "y": cbook.Grouper()} + self._align_title_groups = {"title": cbook.Grouper()} self.figure = self # list of child gridspecs for this figure @@ -1241,6 +1242,60 @@ def align_xlabels(self, axs=None): # grouper for groups of xlabels to align self._align_label_groups['x'].join(ax, axc) + def align_titles(self, axs=None): + """ + Align the titles of subplots in the same subplot column if title + alignment is being done automatically (i.e. the title position is + not manually set). + + Alignment persists for draw events after this is called. + + Parameters + ---------- + axs : list of `~matplotlib.axes.Axes` + Optional list of (or ndarray) `~matplotlib.axes.Axes` + to align the titles. + Default is to align all Axes on the figure. + + See Also + -------- + matplotlib.figure.Figure.align_xlabels + matplotlib.figure.Figure.align_ylabels + matplotlib.figure.Figure.align_labels + + Notes + ----- + This assumes that ``axs`` are from the same `.GridSpec`, so that + their `.SubplotSpec` positions correspond to figure positions. + + Examples + -------- + Example with titles:: + + fig, axs = subplots(1, 2, + subplot_kw={"xlabel": "x", "ylabel": "y", "title": "t"}) + axs[0].imshow(zeros((3, 5))) + axs[1].imshow(zeros((5, 3))) + fig.align_labels() + fig.align_titles() + """ + if axs is None: + axs = self.axes + axs = np.ravel(axs) + axs = [ax for ax in axs if hasattr(ax, 'get_subplotspec')] + locs = ['left', 'center', 'right'] + for ax in axs: + for loc in locs: + if ax.get_title(loc=loc): + _log.debug(' Working on: %s', ax.get_title(loc=loc)) + rowspan = ax.get_subplotspec().rowspan + for axc in axs: + for loc in locs: + rowspanc = axc.get_subplotspec().rowspan + if rowspan.start == rowspanc.start or \ + rowspan.stop == rowspanc.stop: + self._align_title_groups['title'].join(ax, axc) + def align_ylabels(self, axs=None): """ Align the ylabels of subplots in the same subplot column if label diff --git a/test_align_titles.py b/test_align_titles.py new file mode 100644 index 000000000000..ec2957af27ea --- /dev/null +++ b/test_align_titles.py @@ -0,0 +1,18 @@ +import matplotlib as plt +fig, axs = plt.subplots(2, 2, + subplot_kw={"xlabel": "x", "ylabel": "", "title": "t"}) +print(axs.shape) +axs[0][0].imshow(plt.zeros((3, 5))) +axs[0][1].imshow(plt.zeros((5, 3))) +axs[1][0].imshow(plt.zeros((1, 2))) +axs[1][1].imshow(plt.zeros((2, 1))) +axs[0][0].set_title('t2') +rowspan1 = axs[0][0].get_subplotspec().rowspan +print(rowspan1, rowspan1.start, rowspan1.stop) +rowspan2 = axs[1][1].get_subplotspec().rowspan +print(rowspan2, rowspan2.start, rowspan2.stop) + +fig.align_labels() +fig.align_titles() +plt.show() +print("DONE") From e0a20ccbf6207f6edfdb7688dae756af5bb4d9cc Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 29 Mar 2022 20:25:11 -0400 Subject: [PATCH 04/12] copy a bbox function to axesBase from figure --- lib/matplotlib/axes/_base.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 3be469189a3b..a7e382eaa27a 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2941,6 +2941,32 @@ def handle_single_axis( scaley, self._shared_axes["y"], 'y', self.yaxis, self._ymargin, y_stickies, self.set_ybound) + def _get_titles_siblings(self, renderer): + """ + Get the bounding boxes for this `.axis` and its siblings + as set by `.Figure.align_titles`. + + By default it just gets bboxes for self. + """ + # Get the Grouper keeping track of title groups for this figure. + axis_names = [ + name for name, axis in self.axes._axis_map.items() + if name in self.figure._align_label_groups and axis is self] + if len(axis_names) != 1: + return [], [] + axis_name, = axis_names + grouper = self.figure._align_label_groups[axis_name] + bboxes = [] + bboxes2 = [] + # If we want to align labels from other Axes: + for ax in grouper.get_siblings(self.axes): + axis = getattr(ax, f"{axis_name}axis") + ticks_to_draw = axis._update_ticks() + tlb, tlb2 = axis._get_ticklabel_bboxes(ticks_to_draw, renderer) + bboxes.extend(tlb) + bboxes2.extend(tlb2) + return bboxes, bboxes2 + def _update_title_position(self, renderer): """ Update the title position based on the bounding box enclosing @@ -3003,6 +3029,7 @@ def _update_title_position(self, renderer): title.set_position((x, y)) ymax = max(title.get_position()[1] for title in titles) + # TODO: and for titles in sibling axes according to grouper for title in titles: # now line up all the titles at the highest baseline. x, _ = title.get_position() From 8f71fd1bcf2b4098b5942731502e06d717602844 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 3 Apr 2022 16:25:24 -0400 Subject: [PATCH 05/12] add broken code? commented out --- lib/matplotlib/axes/_base.py | 39 +++++++++++------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index a7e382eaa27a..3e336fe5dfd0 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2941,32 +2941,6 @@ def handle_single_axis( scaley, self._shared_axes["y"], 'y', self.yaxis, self._ymargin, y_stickies, self.set_ybound) - def _get_titles_siblings(self, renderer): - """ - Get the bounding boxes for this `.axis` and its siblings - as set by `.Figure.align_titles`. - - By default it just gets bboxes for self. - """ - # Get the Grouper keeping track of title groups for this figure. - axis_names = [ - name for name, axis in self.axes._axis_map.items() - if name in self.figure._align_label_groups and axis is self] - if len(axis_names) != 1: - return [], [] - axis_name, = axis_names - grouper = self.figure._align_label_groups[axis_name] - bboxes = [] - bboxes2 = [] - # If we want to align labels from other Axes: - for ax in grouper.get_siblings(self.axes): - axis = getattr(ax, f"{axis_name}axis") - ticks_to_draw = axis._update_ticks() - tlb, tlb2 = axis._get_ticklabel_bboxes(ticks_to_draw, renderer) - bboxes.extend(tlb) - bboxes2.extend(tlb2) - return bboxes, bboxes2 - def _update_title_position(self, renderer): """ Update the title position based on the bounding box enclosing @@ -2976,7 +2950,18 @@ def _update_title_position(self, renderer): _log.debug('title position was updated manually, not adjusting') return - titles = (self.title, self._left_title, self._right_title) + titles = [] + + # Get grouped axes for alignment if align_titles was previously called + # grouped_axs = self.figure._align_title_groups.get_siblings(self) + # if(grouped_axs is not None): + # titles = [] + # for ax in grouped_axs: + # titles.extend([ax.title, ax._left_title, ax._right_title]) + # else: + # titles = [self.title, self._left_title, self._right_title] + + titles = [self.title, self._left_title, self._right_title] for title in titles: x, _ = title.get_position() From f5de111d6bb22ad5c7b155f780bca498f6f939c2 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 3 Apr 2022 19:08:56 -0400 Subject: [PATCH 06/12] align titles works --- lib/matplotlib/axes/_base.py | 23 +++++++++++++---------- lib/matplotlib/figure.py | 4 ++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 3e336fe5dfd0..bb4478b412ea 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2952,15 +2952,6 @@ def _update_title_position(self, renderer): titles = [] - # Get grouped axes for alignment if align_titles was previously called - # grouped_axs = self.figure._align_title_groups.get_siblings(self) - # if(grouped_axs is not None): - # titles = [] - # for ax in grouped_axs: - # titles.extend([ax.title, ax._left_title, ax._right_title]) - # else: - # titles = [self.title, self._left_title, self._right_title] - titles = [self.title, self._left_title, self._right_title] for title in titles: @@ -3014,12 +3005,24 @@ def _update_title_position(self, renderer): title.set_position((x, y)) ymax = max(title.get_position()[1] for title in titles) - # TODO: and for titles in sibling axes according to grouper for title in titles: # now line up all the titles at the highest baseline. x, _ = title.get_position() title.set_position((x, ymax)) + # Assign + grouped_axs = self.figure._align_title_groups.get_siblings(self) + ymax = None + ax_max = None + for ax in grouped_axs: + if ymax is None or ax.bbox.ymax > ymax: + ymax = ax.bbox.ymax + ax_max = ax + # print("SELF FIRST: ", self.bbox.xmax) + # print("OTHER: ", ax_max.bbox.xmax) + self.bbox = ax_max.bbox + # print("SELF AFTER: ", self.bbox.xmax) + # Drawing @martist.allow_rasterization def draw(self, renderer): diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 24824cc89749..1dce61634957 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -174,7 +174,7 @@ def __init__(self, **kwargs): # see self.align_xlabels and self.align_ylabels and # axis._get_tick_boxes_siblings self._align_label_groups = {"x": cbook.Grouper(), "y": cbook.Grouper()} - self._align_title_groups = {"title": cbook.Grouper()} + self._align_title_groups = cbook.Grouper() self.figure = self # list of child gridspecs for this figure @@ -1294,7 +1294,7 @@ def align_titles(self, axs=None): rowspanc = axc.get_subplotspec().rowspan if rowspan.start == rowspanc.start or \ rowspan.stop == rowspanc.stop: - self._align_title_groups['title'].join(ax, axc) + self._align_title_groups.join(ax, axc) def align_ylabels(self, axs=None): """ From 8b27ae368985bf0773c17492148c85da04b85c59 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 4 Apr 2022 15:04:24 -0400 Subject: [PATCH 07/12] finish code and add tests --- lib/matplotlib/axes/_base.py | 13 +++++------- lib/matplotlib/tests/test_figure.py | 33 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index bb4478b412ea..b484f990b82c 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -3004,24 +3004,21 @@ def _update_title_position(self, renderer): (0., 2 * top - title.get_window_extent(renderer).ymin)) title.set_position((x, y)) + grouped_axs = self.figure._align_title_groups.get_siblings(self) ymax = max(title.get_position()[1] for title in titles) for title in titles: # now line up all the titles at the highest baseline. x, _ = title.get_position() title.set_position((x, ymax)) - # Assign - grouped_axs = self.figure._align_title_groups.get_siblings(self) - ymax = None + # Align bboxes of grouped axes to highest in group + bb_ymax = None ax_max = None for ax in grouped_axs: - if ymax is None or ax.bbox.ymax > ymax: - ymax = ax.bbox.ymax + if bb_ymax is None or ax.bbox.ymax > bb_ymax: + bb_ymax = ax.bbox.ymax ax_max = ax - # print("SELF FIRST: ", self.bbox.xmax) - # print("OTHER: ", ax_max.bbox.xmax) self.bbox = ax_max.bbox - # print("SELF AFTER: ", self.bbox.xmax) # Drawing @martist.allow_rasterization diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index 9f68f63e5236..8cd7d7b7d5b6 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -100,6 +100,39 @@ def test_align_labels_stray_axes(): np.testing.assert_allclose(yn[::2], yn[1::2]) +# TODO add image comparison +@image_comparison(['figure_align_titles'], extensions=['png', 'svg'], + tol=0 if platform.machine() == 'x86_64' else 0.01) +def test_align_titles(): + fig, axs = plt.subplots(2, 2, + subplot_kw={"xlabel": "x", "ylabel": "", + "title": "Title"}) + axs[0][0].imshow(plt.np.zeros((5, 3))) + axs[0][1].imshow(plt.np.zeros((3, 5))) + axs[1][0].imshow(plt.np.zeros((2, 1))) + axs[1][1].imshow(plt.np.zeros((1, 2))) + + axs[0][1].set_title('Title2', loc="left") + axs[0][1].set_title() + + fig.align_titles() + + +## TODO add image comparison +@image_comparison(['figure_align_titles_some'], extensions=['png', 'svg'], + tol=0 if platform.machine() == 'x86_64' else 0.01) +def test_align_titles_param(): + fig, axs = plt.subplots(2, 2, + subplot_kw={"xlabel": "x", "ylabel": "", + "title": "t"}) + axs[0][0].imshow(plt.np.zeros((3, 5))) + axs[0][1].imshow(plt.np.zeros((5, 3))) + axs[1][0].imshow(plt.np.zeros((2, 1))) + axs[1][1].imshow(plt.np.zeros((1, 2))) + + fig.align_titles([axs[0][0], axs[0][1]]) + + def test_figure_label(): # pyplot figure creation, selection, and closing with label/number/instance plt.close('all') From a3d8ccd29523251ec0226865f7406393cb20ec0d Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 6 Apr 2022 15:33:00 -0400 Subject: [PATCH 08/12] add demo --- .../align_titles_demo.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 examples/subplots_axes_and_figures/align_titles_demo.py diff --git a/examples/subplots_axes_and_figures/align_titles_demo.py b/examples/subplots_axes_and_figures/align_titles_demo.py new file mode 100644 index 000000000000..42c8fe819fbe --- /dev/null +++ b/examples/subplots_axes_and_figures/align_titles_demo.py @@ -0,0 +1,26 @@ +""" +=============== +Aligning Labels +=============== + +Aligning titles using `.Figure.align_titles`. + +Note that the title "Title 1" would normally be much closer to the +figure's axis. +""" +import matplotlib.pyplot as plt +fig, axs = plt.subplots(2, 2, + subplot_kw={"xlabel": "x", "ylabel": "", "title": "t"}) +print(axs.shape) +axs[0][0].imshow(plt.np.zeros((3, 5))) +axs[0][1].imshow(plt.np.zeros((5, 3))) +axs[1][0].imshow(plt.np.zeros((1, 2))) +axs[1][1].imshow(plt.np.zeros((2, 1))) +axs[0][0].set_title('t2') +rowspan1 = axs[0][0].get_subplotspec().rowspan +print(rowspan1, rowspan1.start, rowspan1.stop) +rowspan2 = axs[1][1].get_subplotspec().rowspan +print(rowspan2, rowspan2.start, rowspan2.stop) + +fig.align_titles() +plt.show() From 681abfe7322139756a10d00c549e7b9cfe1cb078 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Apr 2022 15:30:38 -0400 Subject: [PATCH 09/12] remove readme2 --- README2 | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 README2 diff --git a/README2 b/README2 deleted file mode 100644 index dd4136e69f94..000000000000 --- a/README2 +++ /dev/null @@ -1,2 +0,0 @@ -ana and caroline -test git push From f23d9e01bdc091b4270ff9fcb79e977f54a470c2 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Apr 2022 15:45:49 -0400 Subject: [PATCH 10/12] pr changes addressed --- lib/matplotlib/axes/_base.py | 5 ++--- lib/matplotlib/figure.py | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index b484f990b82c..dddd0cf1930d 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2950,8 +2950,6 @@ def _update_title_position(self, renderer): _log.debug('title position was updated manually, not adjusting') return - titles = [] - titles = [self.title, self._left_title, self._right_title] for title in titles: @@ -3004,7 +3002,6 @@ def _update_title_position(self, renderer): (0., 2 * top - title.get_window_extent(renderer).ymin)) title.set_position((x, y)) - grouped_axs = self.figure._align_title_groups.get_siblings(self) ymax = max(title.get_position()[1] for title in titles) for title in titles: # now line up all the titles at the highest baseline. @@ -3012,6 +3009,8 @@ def _update_title_position(self, renderer): title.set_position((x, ymax)) # Align bboxes of grouped axes to highest in group + grouped_axs = self.figure._align_label_groups['title'] \ + .get_siblings(self) bb_ymax = None ax_max = None for ax in grouped_axs: diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 1dce61634957..025c651edd22 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -173,8 +173,8 @@ def __init__(self, **kwargs): # groupers to keep track of x and y labels we want to align. # see self.align_xlabels and self.align_ylabels and # axis._get_tick_boxes_siblings - self._align_label_groups = {"x": cbook.Grouper(), "y": cbook.Grouper()} - self._align_title_groups = cbook.Grouper() + self._align_label_groups = {"x": cbook.Grouper(), "y": cbook.Grouper(), + "title": cbook.Grouper()} self.figure = self # list of child gridspecs for this figure @@ -1294,7 +1294,7 @@ def align_titles(self, axs=None): rowspanc = axc.get_subplotspec().rowspan if rowspan.start == rowspanc.start or \ rowspan.stop == rowspanc.stop: - self._align_title_groups.join(ax, axc) + self._align_label_groups['title'].join(ax, axc) def align_ylabels(self, axs=None): """ @@ -2903,6 +2903,7 @@ def draw(self, renderer): artists = self._get_draw_artists(renderer) try: + renderer.open_group('figure', gid=self.get_gid()) if self.axes and self.get_layout_engine() is not None: try: From 83e89a16dc02be93f441e8ccfc36dca1a51c0331 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 20 Mar 2022 16:21:42 -0400 Subject: [PATCH 11/12] add code and tests update test first copy a bbox function to axesBase from figure add broken code? commented out align titles works finish code and add tests add demo remove readme2 --- .../align_titles_demo.py | 26 +++++++++ lib/matplotlib/axes/_base.py | 14 ++++- lib/matplotlib/figure.py | 55 +++++++++++++++++++ lib/matplotlib/tests/test_figure.py | 33 +++++++++++ test_align_titles.py | 18 ++++++ 5 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 examples/subplots_axes_and_figures/align_titles_demo.py create mode 100644 test_align_titles.py diff --git a/examples/subplots_axes_and_figures/align_titles_demo.py b/examples/subplots_axes_and_figures/align_titles_demo.py new file mode 100644 index 000000000000..42c8fe819fbe --- /dev/null +++ b/examples/subplots_axes_and_figures/align_titles_demo.py @@ -0,0 +1,26 @@ +""" +=============== +Aligning Labels +=============== + +Aligning titles using `.Figure.align_titles`. + +Note that the title "Title 1" would normally be much closer to the +figure's axis. +""" +import matplotlib.pyplot as plt +fig, axs = plt.subplots(2, 2, + subplot_kw={"xlabel": "x", "ylabel": "", "title": "t"}) +print(axs.shape) +axs[0][0].imshow(plt.np.zeros((3, 5))) +axs[0][1].imshow(plt.np.zeros((5, 3))) +axs[1][0].imshow(plt.np.zeros((1, 2))) +axs[1][1].imshow(plt.np.zeros((2, 1))) +axs[0][0].set_title('t2') +rowspan1 = axs[0][0].get_subplotspec().rowspan +print(rowspan1, rowspan1.start, rowspan1.stop) +rowspan2 = axs[1][1].get_subplotspec().rowspan +print(rowspan2, rowspan2.start, rowspan2.stop) + +fig.align_titles() +plt.show() diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 3be469189a3b..b484f990b82c 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2950,7 +2950,9 @@ def _update_title_position(self, renderer): _log.debug('title position was updated manually, not adjusting') return - titles = (self.title, self._left_title, self._right_title) + titles = [] + + titles = [self.title, self._left_title, self._right_title] for title in titles: x, _ = title.get_position() @@ -3002,12 +3004,22 @@ def _update_title_position(self, renderer): (0., 2 * top - title.get_window_extent(renderer).ymin)) title.set_position((x, y)) + grouped_axs = self.figure._align_title_groups.get_siblings(self) ymax = max(title.get_position()[1] for title in titles) for title in titles: # now line up all the titles at the highest baseline. x, _ = title.get_position() title.set_position((x, ymax)) + # Align bboxes of grouped axes to highest in group + bb_ymax = None + ax_max = None + for ax in grouped_axs: + if bb_ymax is None or ax.bbox.ymax > bb_ymax: + bb_ymax = ax.bbox.ymax + ax_max = ax + self.bbox = ax_max.bbox + # Drawing @martist.allow_rasterization def draw(self, renderer): diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 974e46d6e4b3..1dce61634957 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -174,6 +174,7 @@ def __init__(self, **kwargs): # see self.align_xlabels and self.align_ylabels and # axis._get_tick_boxes_siblings self._align_label_groups = {"x": cbook.Grouper(), "y": cbook.Grouper()} + self._align_title_groups = cbook.Grouper() self.figure = self # list of child gridspecs for this figure @@ -1241,6 +1242,60 @@ def align_xlabels(self, axs=None): # grouper for groups of xlabels to align self._align_label_groups['x'].join(ax, axc) + def align_titles(self, axs=None): + """ + Align the titles of subplots in the same subplot column if title + alignment is being done automatically (i.e. the title position is + not manually set). + + Alignment persists for draw events after this is called. + + Parameters + ---------- + axs : list of `~matplotlib.axes.Axes` + Optional list of (or ndarray) `~matplotlib.axes.Axes` + to align the titles. + Default is to align all Axes on the figure. + + See Also + -------- + matplotlib.figure.Figure.align_xlabels + matplotlib.figure.Figure.align_ylabels + matplotlib.figure.Figure.align_labels + + Notes + ----- + This assumes that ``axs`` are from the same `.GridSpec`, so that + their `.SubplotSpec` positions correspond to figure positions. + + Examples + -------- + Example with titles:: + + fig, axs = subplots(1, 2, + subplot_kw={"xlabel": "x", "ylabel": "y", "title": "t"}) + axs[0].imshow(zeros((3, 5))) + axs[1].imshow(zeros((5, 3))) + fig.align_labels() + fig.align_titles() + """ + if axs is None: + axs = self.axes + axs = np.ravel(axs) + axs = [ax for ax in axs if hasattr(ax, 'get_subplotspec')] + locs = ['left', 'center', 'right'] + for ax in axs: + for loc in locs: + if ax.get_title(loc=loc): + _log.debug(' Working on: %s', ax.get_title(loc=loc)) + rowspan = ax.get_subplotspec().rowspan + for axc in axs: + for loc in locs: + rowspanc = axc.get_subplotspec().rowspan + if rowspan.start == rowspanc.start or \ + rowspan.stop == rowspanc.stop: + self._align_title_groups.join(ax, axc) + def align_ylabels(self, axs=None): """ Align the ylabels of subplots in the same subplot column if label diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index 9f68f63e5236..8cd7d7b7d5b6 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -100,6 +100,39 @@ def test_align_labels_stray_axes(): np.testing.assert_allclose(yn[::2], yn[1::2]) +# TODO add image comparison +@image_comparison(['figure_align_titles'], extensions=['png', 'svg'], + tol=0 if platform.machine() == 'x86_64' else 0.01) +def test_align_titles(): + fig, axs = plt.subplots(2, 2, + subplot_kw={"xlabel": "x", "ylabel": "", + "title": "Title"}) + axs[0][0].imshow(plt.np.zeros((5, 3))) + axs[0][1].imshow(plt.np.zeros((3, 5))) + axs[1][0].imshow(plt.np.zeros((2, 1))) + axs[1][1].imshow(plt.np.zeros((1, 2))) + + axs[0][1].set_title('Title2', loc="left") + axs[0][1].set_title() + + fig.align_titles() + + +## TODO add image comparison +@image_comparison(['figure_align_titles_some'], extensions=['png', 'svg'], + tol=0 if platform.machine() == 'x86_64' else 0.01) +def test_align_titles_param(): + fig, axs = plt.subplots(2, 2, + subplot_kw={"xlabel": "x", "ylabel": "", + "title": "t"}) + axs[0][0].imshow(plt.np.zeros((3, 5))) + axs[0][1].imshow(plt.np.zeros((5, 3))) + axs[1][0].imshow(plt.np.zeros((2, 1))) + axs[1][1].imshow(plt.np.zeros((1, 2))) + + fig.align_titles([axs[0][0], axs[0][1]]) + + def test_figure_label(): # pyplot figure creation, selection, and closing with label/number/instance plt.close('all') diff --git a/test_align_titles.py b/test_align_titles.py new file mode 100644 index 000000000000..ec2957af27ea --- /dev/null +++ b/test_align_titles.py @@ -0,0 +1,18 @@ +import matplotlib as plt +fig, axs = plt.subplots(2, 2, + subplot_kw={"xlabel": "x", "ylabel": "", "title": "t"}) +print(axs.shape) +axs[0][0].imshow(plt.zeros((3, 5))) +axs[0][1].imshow(plt.zeros((5, 3))) +axs[1][0].imshow(plt.zeros((1, 2))) +axs[1][1].imshow(plt.zeros((2, 1))) +axs[0][0].set_title('t2') +rowspan1 = axs[0][0].get_subplotspec().rowspan +print(rowspan1, rowspan1.start, rowspan1.stop) +rowspan2 = axs[1][1].get_subplotspec().rowspan +print(rowspan2, rowspan2.start, rowspan2.stop) + +fig.align_labels() +fig.align_titles() +plt.show() +print("DONE") From e3c50f895a4e6ea74072b5cbe6691527c7ffe617 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Apr 2022 15:45:49 -0400 Subject: [PATCH 12/12] pr changes addressed test cases --- lib/matplotlib/axes/_base.py | 5 ++--- lib/matplotlib/figure.py | 7 ++++--- lib/matplotlib/tests/test_figure.py | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index b484f990b82c..dddd0cf1930d 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2950,8 +2950,6 @@ def _update_title_position(self, renderer): _log.debug('title position was updated manually, not adjusting') return - titles = [] - titles = [self.title, self._left_title, self._right_title] for title in titles: @@ -3004,7 +3002,6 @@ def _update_title_position(self, renderer): (0., 2 * top - title.get_window_extent(renderer).ymin)) title.set_position((x, y)) - grouped_axs = self.figure._align_title_groups.get_siblings(self) ymax = max(title.get_position()[1] for title in titles) for title in titles: # now line up all the titles at the highest baseline. @@ -3012,6 +3009,8 @@ def _update_title_position(self, renderer): title.set_position((x, ymax)) # Align bboxes of grouped axes to highest in group + grouped_axs = self.figure._align_label_groups['title'] \ + .get_siblings(self) bb_ymax = None ax_max = None for ax in grouped_axs: diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 1dce61634957..025c651edd22 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -173,8 +173,8 @@ def __init__(self, **kwargs): # groupers to keep track of x and y labels we want to align. # see self.align_xlabels and self.align_ylabels and # axis._get_tick_boxes_siblings - self._align_label_groups = {"x": cbook.Grouper(), "y": cbook.Grouper()} - self._align_title_groups = cbook.Grouper() + self._align_label_groups = {"x": cbook.Grouper(), "y": cbook.Grouper(), + "title": cbook.Grouper()} self.figure = self # list of child gridspecs for this figure @@ -1294,7 +1294,7 @@ def align_titles(self, axs=None): rowspanc = axc.get_subplotspec().rowspan if rowspan.start == rowspanc.start or \ rowspan.stop == rowspanc.stop: - self._align_title_groups.join(ax, axc) + self._align_label_groups['title'].join(ax, axc) def align_ylabels(self, axs=None): """ @@ -2903,6 +2903,7 @@ def draw(self, renderer): artists = self._get_draw_artists(renderer) try: + renderer.open_group('figure', gid=self.get_gid()) if self.axes and self.get_layout_engine() is not None: try: diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index 8cd7d7b7d5b6..fb0fed6b311f 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -113,13 +113,12 @@ def test_align_titles(): axs[1][1].imshow(plt.np.zeros((1, 2))) axs[0][1].set_title('Title2', loc="left") - axs[0][1].set_title() fig.align_titles() ## TODO add image comparison -@image_comparison(['figure_align_titles_some'], extensions=['png', 'svg'], +@image_comparison(['figure_align_titles_param'], extensions=['png', 'svg'], tol=0 if platform.machine() == 'x86_64' else 0.01) def test_align_titles_param(): fig, axs = plt.subplots(2, 2, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy