From fc91e617ec25a4fa4ce4aa2ba3dc5d28562e5090 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Tue, 11 Oct 2022 12:18:32 -0500 Subject: [PATCH 1/9] Set figure options dynamically Previously, for the figure options, things were hard coded for the X and Y axes but were not implemented for the Z axis. With this commit, all the options in the Figure options dialogue box are generated dynamically based on the axes present in the figure. This removes all the hard coded part and should make it more modular to make further changes. --- .../backends/qt_editor/figureoptions.py | 104 ++++++++++-------- 1 file changed, 58 insertions(+), 46 deletions(-) diff --git a/lib/matplotlib/backends/qt_editor/figureoptions.py b/lib/matplotlib/backends/qt_editor/figureoptions.py index 67e00006910f..3ccd5adab0cf 100644 --- a/lib/matplotlib/backends/qt_editor/figureoptions.py +++ b/lib/matplotlib/backends/qt_editor/figureoptions.py @@ -5,6 +5,7 @@ """Module that provides a GUI-based editor for Matplotlib's figure options.""" +from itertools import chain from matplotlib import cbook, cm, colors as mcolors, markers, image as mimage from matplotlib.backends.qt_compat import QtGui from matplotlib.backends.qt_editor import _formlayout @@ -38,30 +39,41 @@ def convert_limits(lim, converter): # Cast to builtin floats as they have nicer reprs. return map(float, lim) - xconverter = axes.xaxis.converter - xmin, xmax = convert_limits(axes.get_xlim(), xconverter) - yconverter = axes.yaxis.converter - ymin, ymax = convert_limits(axes.get_ylim(), yconverter) - general = [('Title', axes.get_title()), - sep, - (None, "X-Axis"), - ('Left', xmin), ('Right', xmax), - ('Label', axes.get_xlabel()), - ('Scale', [axes.get_xscale(), - 'linear', 'log', 'symlog', 'logit']), - sep, - (None, "Y-Axis"), - ('Bottom', ymin), ('Top', ymax), - ('Label', axes.get_ylabel()), - ('Scale', [axes.get_yscale(), - 'linear', 'log', 'symlog', 'logit']), - sep, - ('(Re-)Generate automatic legend', False), - ] + axis_map = axes._axis_map + axis_converter = { + axis: getattr(getattr(axes, f'{axis}axis'), 'converter') + for axis in axis_map.keys() + } + axis_limits = { + axis: tuple(convert_limits( + getattr(axes, f'get_{axis}lim')(), axis_converter[axis] + )) + for axis in axis_map.keys() + } + general = [ + ('Title', axes.get_title()), + sep, + ] + axes_info = [ + ( + (None, f"{axis.upper()}-Axis"), + ('Min', axis_limits[axis][0]), + ('Max', axis_limits[axis][1]), + ('Label', getattr(axes, f"get_{axis}label")()), + ('Scale', [getattr(axes, f"get_{axis}scale")(), + 'linear', 'log', 'symlog', 'logit']), + sep, + ) + for axis in axis_map.keys() + ] + general.extend(chain.from_iterable(axes_info)) + general.append(('(Re-)Generate automatic legend', False)) # Save the unit data - xunits = axes.xaxis.get_units() - yunits = axes.yaxis.get_units() + axis_units = { + axis: getattr(getattr(axes, f"{axis}axis"), "get_units")() + for axis in axis_map.keys() + } # Get / Curves labeled_lines = [] @@ -165,8 +177,10 @@ def prepare_data(d, init): def apply_callback(data): """A callback to apply changes.""" - orig_xlim = axes.get_xlim() - orig_ylim = axes.get_ylim() + orig_limits = { + axis: getattr(axes, f"get_{axis}lim")() + for axis in axis_map.keys() + } general = data.pop(0) curves = data.pop(0) if has_curve else [] @@ -174,28 +188,24 @@ def apply_callback(data): if data: raise ValueError("Unexpected field") - # Set / General - (title, xmin, xmax, xlabel, xscale, ymin, ymax, ylabel, yscale, - generate_legend) = general + title = general.pop(0) + axes.set_title(title) + generate_legend = general.pop() - if axes.get_xscale() != xscale: - axes.set_xscale(xscale) - if axes.get_yscale() != yscale: - axes.set_yscale(yscale) + for i, axis in enumerate(axis_map.keys()): + ax = getattr(axes, f"{axis}axis") + axmin = general[4*i] + axmax = general[4*i + 1] + axlabel = general[4*i + 2] + axscale = general[4*i + 3] + if getattr(axes, f"get_{axis}scale")() != axscale: + getattr(axes, f"set_{axis}scale")(axscale) - axes.set_title(title) - axes.set_xlim(xmin, xmax) - axes.set_xlabel(xlabel) - axes.set_ylim(ymin, ymax) - axes.set_ylabel(ylabel) - - # Restore the unit data - axes.xaxis.converter = xconverter - axes.yaxis.converter = yconverter - axes.xaxis.set_units(xunits) - axes.yaxis.set_units(yunits) - axes.xaxis._update_axisinfo() - axes.yaxis._update_axisinfo() + getattr(axes, f"set_{axis}lim")(axmin, axmax) + getattr(axes, f"set_{axis}label")(axlabel) + setattr(ax, 'converter', axis_converter[axis]) + getattr(ax, 'set_units')(axis_units[axis]) + ax._update_axisinfo() # Set / Curves for index, curve in enumerate(curves): @@ -242,8 +252,10 @@ def apply_callback(data): # Redraw figure = axes.get_figure() figure.canvas.draw() - if not (axes.get_xlim() == orig_xlim and axes.get_ylim() == orig_ylim): - figure.canvas.toolbar.push_current() + for axis in axis_map.keys(): + if getattr(axes, f"get_{axis}lim")() != orig_limits[axis]: + figure.canvas.toolbar.push_current() + break _formlayout.fedit( datalist, title="Figure options", parent=parent, From 0f7b47291a111007473304833a65cca8a1a477f6 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Tue, 11 Oct 2022 21:56:56 -0500 Subject: [PATCH 2/9] Reduce getattr calls and use axis_map dictionary The value in `axis_map` is the axes object for that axis so we can use that directly to get the object instead of using `getattr`s by looping over the items of the dictionary. This also removes the need to have an `axis_converter` dictionary. --- .../backends/qt_editor/figureoptions.py | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/lib/matplotlib/backends/qt_editor/figureoptions.py b/lib/matplotlib/backends/qt_editor/figureoptions.py index 3ccd5adab0cf..7238faa5e200 100644 --- a/lib/matplotlib/backends/qt_editor/figureoptions.py +++ b/lib/matplotlib/backends/qt_editor/figureoptions.py @@ -40,15 +40,11 @@ def convert_limits(lim, converter): return map(float, lim) axis_map = axes._axis_map - axis_converter = { - axis: getattr(getattr(axes, f'{axis}axis'), 'converter') - for axis in axis_map.keys() - } axis_limits = { - axis: tuple(convert_limits( - getattr(axes, f'get_{axis}lim')(), axis_converter[axis] + axname: tuple(convert_limits( + getattr(axes, f'get_{axname}lim')(), axis.converter )) - for axis in axis_map.keys() + for axname, axis in axis_map.items() } general = [ ('Title', axes.get_title()), @@ -192,19 +188,18 @@ def apply_callback(data): axes.set_title(title) generate_legend = general.pop() - for i, axis in enumerate(axis_map.keys()): - ax = getattr(axes, f"{axis}axis") + for i, (axname, ax) in enumerate(axis_map.items()): axmin = general[4*i] axmax = general[4*i + 1] axlabel = general[4*i + 2] axscale = general[4*i + 3] - if getattr(axes, f"get_{axis}scale")() != axscale: - getattr(axes, f"set_{axis}scale")(axscale) + if getattr(axes, f"get_{axname}scale")() != axscale: + getattr(axes, f"set_{axname}scale")(axscale) - getattr(axes, f"set_{axis}lim")(axmin, axmax) - getattr(axes, f"set_{axis}label")(axlabel) - setattr(ax, 'converter', axis_converter[axis]) - getattr(ax, 'set_units')(axis_units[axis]) + getattr(axes, f"set_{axname}lim")(axmin, axmax) + getattr(axes, f"set_{axname}label")(axlabel) + setattr(ax, 'converter', ax.converter) + getattr(ax, 'set_units')(axis_units[axname]) ax._update_axisinfo() # Set / Curves From 7e5029e69de0622ba08fdf329f90ac28e7b302c0 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Wed, 12 Oct 2022 11:08:14 -0500 Subject: [PATCH 3/9] Change variable names to match coding style --- .../backends/qt_editor/figureoptions.py | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/matplotlib/backends/qt_editor/figureoptions.py b/lib/matplotlib/backends/qt_editor/figureoptions.py index 7238faa5e200..cf066ab7cc39 100644 --- a/lib/matplotlib/backends/qt_editor/figureoptions.py +++ b/lib/matplotlib/backends/qt_editor/figureoptions.py @@ -41,10 +41,10 @@ def convert_limits(lim, converter): axis_map = axes._axis_map axis_limits = { - axname: tuple(convert_limits( - getattr(axes, f'get_{axname}lim')(), axis.converter + name: tuple(convert_limits( + getattr(axes, f'get_{name}lim')(), axis.converter )) - for axname, axis in axis_map.items() + for name, axis in axis_map.items() } general = [ ('Title', axes.get_title()), @@ -52,23 +52,23 @@ def convert_limits(lim, converter): ] axes_info = [ ( - (None, f"{axis.upper()}-Axis"), - ('Min', axis_limits[axis][0]), - ('Max', axis_limits[axis][1]), - ('Label', getattr(axes, f"get_{axis}label")()), - ('Scale', [getattr(axes, f"get_{axis}scale")(), + (None, f"{name.upper()}-Axis"), + ('Min', axis_limits[name][0]), + ('Max', axis_limits[name][1]), + ('Label', axis.get_label().get_text()), + ('Scale', [axis.get_scale(), 'linear', 'log', 'symlog', 'logit']), sep, ) - for axis in axis_map.keys() + for name, axis in axis_map.items() ] general.extend(chain.from_iterable(axes_info)) general.append(('(Re-)Generate automatic legend', False)) # Save the unit data axis_units = { - axis: getattr(getattr(axes, f"{axis}axis"), "get_units")() - for axis in axis_map.keys() + name: axis.get_units() + for name, axis in axis_map.items() } # Get / Curves @@ -174,8 +174,8 @@ def prepare_data(d, init): def apply_callback(data): """A callback to apply changes.""" orig_limits = { - axis: getattr(axes, f"get_{axis}lim")() - for axis in axis_map.keys() + name: getattr(axes, f"get_{name}lim")() + for name in axis_map.keys() } general = data.pop(0) @@ -188,19 +188,19 @@ def apply_callback(data): axes.set_title(title) generate_legend = general.pop() - for i, (axname, ax) in enumerate(axis_map.items()): - axmin = general[4*i] - axmax = general[4*i + 1] - axlabel = general[4*i + 2] - axscale = general[4*i + 3] - if getattr(axes, f"get_{axname}scale")() != axscale: - getattr(axes, f"set_{axname}scale")(axscale) + for i, (name, axis) in enumerate(axis_map.items()): + axis_min = general[4*i] + axis_max = general[4*i + 1] + axis_label = general[4*i + 2] + axis_scale = general[4*i + 3] + if getattr(axes, f"get_{name}scale")() != axis_scale: + getattr(axes, f"set_{name}scale")(axis_scale) - getattr(axes, f"set_{axname}lim")(axmin, axmax) - getattr(axes, f"set_{axname}label")(axlabel) - setattr(ax, 'converter', ax.converter) - getattr(ax, 'set_units')(axis_units[axname]) - ax._update_axisinfo() + getattr(axes, f"set_{name}lim")(axis_min, axis_max) + axis.set_label_text(axis_label) + setattr(axis, 'converter', axis.converter) + axis.set_units(axis_units[name]) + axis._update_axisinfo() # Set / Curves for index, curve in enumerate(curves): @@ -247,8 +247,8 @@ def apply_callback(data): # Redraw figure = axes.get_figure() figure.canvas.draw() - for axis in axis_map.keys(): - if getattr(axes, f"get_{axis}lim")() != orig_limits[axis]: + for name in axis_map.keys(): + if getattr(axes, f"get_{name}lim")() != orig_limits[name]: figure.canvas.toolbar.push_current() break From a505dee0ec0da907c94f2410b7905ed515492e38 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Wed, 12 Oct 2022 21:41:02 -0500 Subject: [PATCH 4/9] Add some changes for variable names --- .../backends/qt_editor/figureoptions.py | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/matplotlib/backends/qt_editor/figureoptions.py b/lib/matplotlib/backends/qt_editor/figureoptions.py index cf066ab7cc39..6726f5dacedc 100644 --- a/lib/matplotlib/backends/qt_editor/figureoptions.py +++ b/lib/matplotlib/backends/qt_editor/figureoptions.py @@ -49,21 +49,20 @@ def convert_limits(lim, converter): general = [ ('Title', axes.get_title()), sep, + *chain.from_iterable([ + ( + (None, f"{name.upper()}-Axis"), + ('Min', axis_limits[name][0]), + ('Max', axis_limits[name][1]), + ('Label', axis.get_label().get_text()), + ('Scale', [axis.get_scale(), + 'linear', 'log', 'symlog', 'logit']), + sep, + ) + for name, axis in axis_map.items() + ]), + ('(Re-)Generate automatic legend', False), ] - axes_info = [ - ( - (None, f"{name.upper()}-Axis"), - ('Min', axis_limits[name][0]), - ('Max', axis_limits[name][1]), - ('Label', axis.get_label().get_text()), - ('Scale', [axis.get_scale(), - 'linear', 'log', 'symlog', 'logit']), - sep, - ) - for name, axis in axis_map.items() - ] - general.extend(chain.from_iterable(axes_info)) - general.append(('(Re-)Generate automatic legend', False)) # Save the unit data axis_units = { @@ -175,7 +174,7 @@ def apply_callback(data): """A callback to apply changes.""" orig_limits = { name: getattr(axes, f"get_{name}lim")() - for name in axis_map.keys() + for name in axis_map } general = data.pop(0) From 6e214194ecc14821b429b4706e44a30ba2499936 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Fri, 14 Oct 2022 14:03:20 -0500 Subject: [PATCH 5/9] Save converter before any changes and reset --- lib/matplotlib/backends/qt_editor/figureoptions.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/qt_editor/figureoptions.py b/lib/matplotlib/backends/qt_editor/figureoptions.py index 6726f5dacedc..3b8d252294a9 100644 --- a/lib/matplotlib/backends/qt_editor/figureoptions.py +++ b/lib/matplotlib/backends/qt_editor/figureoptions.py @@ -40,6 +40,10 @@ def convert_limits(lim, converter): return map(float, lim) axis_map = axes._axis_map + axis_converter = { + name: axis.converter + for name, axis in axis_map.items() + } axis_limits = { name: tuple(convert_limits( getattr(axes, f'get_{name}lim')(), axis.converter @@ -197,7 +201,7 @@ def apply_callback(data): getattr(axes, f"set_{name}lim")(axis_min, axis_max) axis.set_label_text(axis_label) - setattr(axis, 'converter', axis.converter) + axis.converter = axis_converter[name] axis.set_units(axis_units[name]) axis._update_axisinfo() @@ -246,7 +250,7 @@ def apply_callback(data): # Redraw figure = axes.get_figure() figure.canvas.draw() - for name in axis_map.keys(): + for name in axis_map: if getattr(axes, f"get_{name}lim")() != orig_limits[name]: figure.canvas.toolbar.push_current() break From 9b0fd09c79a3e4fd4a21de69e69488b6ac7d04b8 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Wed, 19 Oct 2022 21:28:33 -0500 Subject: [PATCH 6/9] Move related unit and converter code together Add other suggestions mentioned in the PR. --- lib/matplotlib/backends/qt_editor/figureoptions.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/backends/qt_editor/figureoptions.py b/lib/matplotlib/backends/qt_editor/figureoptions.py index 3b8d252294a9..dfe29992aa05 100644 --- a/lib/matplotlib/backends/qt_editor/figureoptions.py +++ b/lib/matplotlib/backends/qt_editor/figureoptions.py @@ -40,10 +40,6 @@ def convert_limits(lim, converter): return map(float, lim) axis_map = axes._axis_map - axis_converter = { - name: axis.converter - for name, axis in axis_map.items() - } axis_limits = { name: tuple(convert_limits( getattr(axes, f'get_{name}lim')(), axis.converter @@ -68,7 +64,11 @@ def convert_limits(lim, converter): ('(Re-)Generate automatic legend', False), ] - # Save the unit data + # Save the converter and unit data + axis_converter = { + name: axis.converter + for name, axis in axis_map.items() + } axis_units = { name: axis.get_units() for name, axis in axis_map.items() @@ -196,11 +196,13 @@ def apply_callback(data): axis_max = general[4*i + 1] axis_label = general[4*i + 2] axis_scale = general[4*i + 3] - if getattr(axes, f"get_{name}scale")() != axis_scale: + if axis.get_scale() != axis_scale: getattr(axes, f"set_{name}scale")(axis_scale) getattr(axes, f"set_{name}lim")(axis_min, axis_max) axis.set_label_text(axis_label) + + # Restore the unit data axis.converter = axis_converter[name] axis.set_units(axis_units[name]) axis._update_axisinfo() From 53b65e41e61b9e9c7c7ab9bb466eb78ddf91ac04 Mon Sep 17 00:00:00 2001 From: Chahak Mehta <201501422@daiict.ac.in> Date: Thu, 20 Oct 2022 11:41:37 -0500 Subject: [PATCH 7/9] Update lib/matplotlib/backends/qt_editor/figureoptions.py Change axis name conversion to `.title` instead of `.upper` to handle more general axis names. Co-authored-by: Elliott Sales de Andrade --- lib/matplotlib/backends/qt_editor/figureoptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/qt_editor/figureoptions.py b/lib/matplotlib/backends/qt_editor/figureoptions.py index dfe29992aa05..c881271ae44e 100644 --- a/lib/matplotlib/backends/qt_editor/figureoptions.py +++ b/lib/matplotlib/backends/qt_editor/figureoptions.py @@ -51,7 +51,7 @@ def convert_limits(lim, converter): sep, *chain.from_iterable([ ( - (None, f"{name.upper()}-Axis"), + (None, f"{name.title()}-Axis"), ('Min', axis_limits[name][0]), ('Max', axis_limits[name][1]), ('Label', axis.get_label().get_text()), From 372a8372dd2dc6cb24fa12424ec42a53c0b53587 Mon Sep 17 00:00:00 2001 From: Chahak Mehta <201501422@daiict.ac.in> Date: Thu, 20 Oct 2022 11:53:10 -0500 Subject: [PATCH 8/9] Update lib/matplotlib/backends/qt_editor/figureoptions.py Use private method of `axis` instead of using `getattr` on `axes` to set limits. Co-authored-by: Elliott Sales de Andrade --- lib/matplotlib/backends/qt_editor/figureoptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/qt_editor/figureoptions.py b/lib/matplotlib/backends/qt_editor/figureoptions.py index c881271ae44e..8556d1d781f0 100644 --- a/lib/matplotlib/backends/qt_editor/figureoptions.py +++ b/lib/matplotlib/backends/qt_editor/figureoptions.py @@ -199,7 +199,7 @@ def apply_callback(data): if axis.get_scale() != axis_scale: getattr(axes, f"set_{name}scale")(axis_scale) - getattr(axes, f"set_{name}lim")(axis_min, axis_max) + axis._set_lim(axis_min, axis_max, auto=False) axis.set_label_text(axis_label) # Restore the unit data From 3fc108012bafe8ddfeded411893b2be8b9b58ed7 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Thu, 20 Oct 2022 12:05:48 -0500 Subject: [PATCH 9/9] Remove redundant `_update_axisinfo` call `set_units` calls the `_update_axisinfo` function internally, so the call here was redundant. --- lib/matplotlib/backends/qt_editor/figureoptions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/matplotlib/backends/qt_editor/figureoptions.py b/lib/matplotlib/backends/qt_editor/figureoptions.py index 8556d1d781f0..2a9510980106 100644 --- a/lib/matplotlib/backends/qt_editor/figureoptions.py +++ b/lib/matplotlib/backends/qt_editor/figureoptions.py @@ -205,7 +205,6 @@ def apply_callback(data): # Restore the unit data axis.converter = axis_converter[name] axis.set_units(axis_units[name]) - axis._update_axisinfo() # Set / Curves for index, curve in enumerate(curves):