From 2cd3401d0678780a6c40ccd8e49dfcf464ebbb75 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 24 Feb 2016 23:06:29 -0500 Subject: [PATCH 01/19] PRF: change draw -> draw_idle closes #6058 --- lib/matplotlib/backends/backend_qt5.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py index 64e8accb958..67ebecc1870 100644 --- a/lib/matplotlib/backends/backend_qt5.py +++ b/lib/matplotlib/backends/backend_qt5.py @@ -768,7 +768,7 @@ def funcleft(self, val): self.targetfig.subplots_adjust(left=val) self.leftvalue.setText("%.2f" % val) if self.drawon: - self.targetfig.canvas.draw() + self.targetfig.canvas.draw_idle() def funcright(self, val): if val == self.sliderleft.value(): @@ -777,7 +777,7 @@ def funcright(self, val): self.targetfig.subplots_adjust(right=val) self.rightvalue.setText("%.2f" % val) if self.drawon: - self.targetfig.canvas.draw() + self.targetfig.canvas.draw_idle() def funcbottom(self, val): if val == self.slidertop.value(): @@ -786,7 +786,7 @@ def funcbottom(self, val): self.targetfig.subplots_adjust(bottom=val) self.bottomvalue.setText("%.2f" % val) if self.drawon: - self.targetfig.canvas.draw() + self.targetfig.canvas.draw_idle() def functop(self, val): if val == self.sliderbottom.value(): @@ -795,31 +795,31 @@ def functop(self, val): self.targetfig.subplots_adjust(top=val) self.topvalue.setText("%.2f" % val) if self.drawon: - self.targetfig.canvas.draw() + self.targetfig.canvas.draw_idle() def funcwspace(self, val): val /= 1000. self.targetfig.subplots_adjust(wspace=val) self.wspacevalue.setText("%.2f" % val) if self.drawon: - self.targetfig.canvas.draw() + self.targetfig.canvas.draw_idle() def funchspace(self, val): val /= 1000. self.targetfig.subplots_adjust(hspace=val) self.hspacevalue.setText("%.2f" % val) if self.drawon: - self.targetfig.canvas.draw() + self.targetfig.canvas.draw_idle() def functight(self): self.targetfig.tight_layout() self._setSliderPositions() - self.targetfig.canvas.draw() + self.targetfig.canvas.draw_idle() def reset(self): self.targetfig.subplots_adjust(**self.defaults) self._setSliderPositions() - self.targetfig.canvas.draw() + self.targetfig.canvas.draw_idle() def error_msg_qt(msg, parent=None): From 3a2d8d2ec6b640f7026f2f59c280e8598c7d77cc Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 6 Mar 2016 21:24:18 -0500 Subject: [PATCH 02/19] MNT: cleanup initialization of subplot The 'left' and 'right' sliders are both created at 0 and then updated to the values read out of the figure. There is logic in the callback functions to separate singular inputs and a second set of callback functions to ensure that the min/max stay in the right order. What was happening is: - both left and right are 0 - set left to the value from the figure - constraint callback fires and sets the minimum of right to be equal to the current value of left. Qt helpfully adjust the value of the slider from 0 -> the left value (as 0 is now no longer an allowed value) - the callback to adjust the figure fires, notes that the left and right values are the same and makes the input value a bit smaller - propagates that value back to the figure. The solution is to set the values of each slider before connecting up the callback. closes #6121 --- lib/matplotlib/backends/backend_qt5.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py index 67ebecc1870..d89f3b90ad0 100644 --- a/lib/matplotlib/backends/backend_qt5.py +++ b/lib/matplotlib/backends/backend_qt5.py @@ -747,13 +747,17 @@ def __init__(self, targetfig, parent): self.defaults = {} for attr in ('left', 'bottom', 'right', 'top', 'wspace', 'hspace', ): - self.defaults[attr] = getattr(self.targetfig.subplotpars, attr) + val = getattr(self.targetfig.subplotpars, attr) + self.defaults[attr] = val slider = getattr(self, 'slider' + attr) + txt = getattr(self, attr + 'value') slider.setMinimum(0) slider.setMaximum(1000) slider.setSingleStep(5) + # do this before hooking up the callbacks + slider.setSliderPosition(int(val * 1000)) + txt.setText("%.2f" % val) slider.valueChanged.connect(getattr(self, 'func' + attr)) - self._setSliderPositions() def _setSliderPositions(self): From be40bd5310ca95d6d946e608fb8ca1a9c26317b0 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 24 Feb 2016 22:43:30 -0500 Subject: [PATCH 03/19] FIX: prevent an infinite loop closes #6057 --- lib/matplotlib/ticker.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 34d9676f743..8f50071f375 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1503,6 +1503,7 @@ def __init__(self, base=10.0, subs=[1.0], numdecs=4, numticks=15): """ self.base(base) self.subs(subs) + # this needs to be validated > 1 with traitlets self.numticks = numticks self.numdecs = numdecs @@ -1575,6 +1576,9 @@ def tick_values(self, vmin, vmax): subs = self._subs stride = 1 + if not self.numticks > 1: + raise RuntimeError('The number of ticks must be greater than 1 ' + 'for LogLocator.') while numdec / stride + 1 > self.numticks: stride += 1 From 82ae8ef215080028dc6148aef204a019ab84751c Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 11 May 2016 13:44:09 +0100 Subject: [PATCH 04/19] Block pyparsing 2.1.2 which is broken on python3.4 --- .travis.yml | 2 +- setupext.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 38e3b01156c..0bb47343e08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,7 +72,7 @@ install: pip install --upgrade setuptools # Install only from travis wheelhouse - if [ -z "$PRE" ]; then - wheelhouse_pip_install python-dateutil $NUMPY $PANDAS pyparsing!=2.0.4 pillow sphinx!=1.3.0; + wheelhouse_pip_install python-dateutil $NUMPY $PANDAS pyparsing!=2.1.2 pillow sphinx!=1.3.0; else pip install $PRE python-dateutil $NUMPY pyparsing!=2.0.4 pillow sphinx!=1.3.0; fi diff --git a/setupext.py b/setupext.py index 5890512e749..ff6cbca5401 100755 --- a/setupext.py +++ b/setupext.py @@ -1239,6 +1239,7 @@ def check(self): class Pyparsing(SetupPackage): name = "pyparsing" # pyparsing 2.0.4 has broken python 3 support. + # pyparsing 2.1.2 is broken in python3.4/3.3. def is_ok(self): # pyparsing 2.0.0 bug, but it may be patched in distributions try: @@ -1273,10 +1274,11 @@ def check(self): return "using pyparsing version %s" % pyparsing.__version__ def get_install_requires(self): + versionstring = 'pyparsing>=1.5.6,!=2.0.4,!=2.1.2' if self.is_ok(): - return ['pyparsing>=1.5.6,!=2.0.4'] + return [versionstring] else: - return ['pyparsing>=1.5.6,!=2.0.0,!=2.0.4'] + return [versionstring + ',!=2.0.0'] class BackendAgg(OptionalBackendPackage): From bc447a9b228b3d8601cc3ddd15c063756689f374 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 11 May 2016 14:33:57 +0100 Subject: [PATCH 05/19] Missed one in the travis file --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0bb47343e08..991d07c2743 100644 --- a/.travis.yml +++ b/.travis.yml @@ -74,7 +74,7 @@ install: - if [ -z "$PRE" ]; then wheelhouse_pip_install python-dateutil $NUMPY $PANDAS pyparsing!=2.1.2 pillow sphinx!=1.3.0; else - pip install $PRE python-dateutil $NUMPY pyparsing!=2.0.4 pillow sphinx!=1.3.0; + pip install $PRE python-dateutil $NUMPY pyparsing!=2.1.2 pillow sphinx!=1.3.0; fi # Always install from pypi - pip install $PRE pep8 cycler==0.9.0 From c21bf6d3ce70a194ec65952e47722b875b51ccff Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 11 May 2016 23:25:52 -0400 Subject: [PATCH 06/19] FIX: apply tick.side rcparam to major&minor ticks Extends cbb3dc84fdb4e4cedf1d171fbc9bf12790e34489 to also apply to minor ticks closes #6408 --- lib/matplotlib/axes/_base.py | 3 ++- lib/matplotlib/tests/test_axes.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 65ae951b13e..2274e52bb17 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -576,7 +576,8 @@ def __init__(self, fig, rect, self.tick_params(top=rcParams['xtick.top'], bottom=rcParams['xtick.bottom'], left=rcParams['ytick.left'], - right=rcParams['ytick.right']) + right=rcParams['ytick.right'], + which='both') def __setstate__(self, state): self.__dict__ = state diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 1819d28a2e0..98b6257f238 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -4064,9 +4064,13 @@ def test_rc_tick(): # tick1On bottom/left assert xax._major_tick_kw['tick1On'] == False assert xax._major_tick_kw['tick2On'] == True + assert xax._minor_tick_kw['tick1On'] == False + assert xax._minor_tick_kw['tick2On'] == True assert yax._major_tick_kw['tick1On'] == True assert yax._major_tick_kw['tick2On'] == False + assert yax._minor_tick_kw['tick1On'] == True + assert yax._minor_tick_kw['tick2On'] == False @cleanup From 9b39f3ed6e4792f1cf8ed4031faeb752e4bce748 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 12 May 2016 17:26:22 -0400 Subject: [PATCH 07/19] Merge pull request #6397 from fariza/stop-gtk-key-event-propagation MNT: key events handler return value to True to stop propagation it gtk --- lib/matplotlib/backends/backend_gtk.py | 4 ++-- lib/matplotlib/backends/backend_gtk3.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/backends/backend_gtk.py b/lib/matplotlib/backends/backend_gtk.py index f439af2564e..7ebca484d7b 100644 --- a/lib/matplotlib/backends/backend_gtk.py +++ b/lib/matplotlib/backends/backend_gtk.py @@ -300,14 +300,14 @@ def key_press_event(self, widget, event): key = self._get_key(event) if _debug: print("hit", key) FigureCanvasBase.key_press_event(self, key, guiEvent=event) - return False # finish event propagation? + return True # stop event propagation def key_release_event(self, widget, event): if _debug: print('FigureCanvasGTK.%s' % fn_name()) key = self._get_key(event) if _debug: print("release", key) FigureCanvasBase.key_release_event(self, key, guiEvent=event) - return False # finish event propagation? + return True # stop event propagation def motion_notify_event(self, widget, event): if _debug: print('FigureCanvasGTK.%s' % fn_name()) diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index 2c4099a50e0..8872e65427b 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -251,14 +251,14 @@ def key_press_event(self, widget, event): key = self._get_key(event) if _debug: print("hit", key) FigureCanvasBase.key_press_event(self, key, guiEvent=event) - return False # finish event propagation? + return True # stop event propagation def key_release_event(self, widget, event): if _debug: print('FigureCanvasGTK3.%s' % fn_name()) key = self._get_key(event) if _debug: print("release", key) FigureCanvasBase.key_release_event(self, key, guiEvent=event) - return False # finish event propagation? + return True # stop event propagation def motion_notify_event(self, widget, event): if _debug: print('FigureCanvasGTK3.%s' % fn_name()) From c2b6769726eac657e6cb6f8abb5dd17d63350c6f Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 12 May 2016 17:32:26 -0400 Subject: [PATCH 08/19] Merge pull request #6390 from anntzer/xkcd-colors-namespace API: Use xkcd: prefix to avoid color name clashes. --- doc/users/colors.rst | 49 ++++++++++++++----------------------- lib/matplotlib/_color_data.py | 11 +++------ lib/matplotlib/tests/test_colors.py | 4 +-- 3 files changed, 24 insertions(+), 40 deletions(-) diff --git a/doc/users/colors.rst b/doc/users/colors.rst index ccea8ea5d6f..f935edd5c93 100644 --- a/doc/users/colors.rst +++ b/doc/users/colors.rst @@ -4,57 +4,47 @@ Specifying Colors ***************** -In almost all places in matplotlib where a color can be specified by the user it can be provided as: +In almost all places in matplotlib where a color can be specified by the user +it can be provided as: * ``(r, g, b)`` tuples * ``(r, g, b, a)`` tuples * hex string, ex ``#OFOFOF`` * float value between [0, 1] for gray level * One of ``{'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}`` -* valid css4/X11 color names -* valid name from the `XKCD color survey +* valid CSS4/X11 color names +* valid name from the `xkcd color survey `__ These - names are available both with and with out spaces. In the case of name clashes - the css/X11 names have priority. To ensure colors - from the XKCD mapping are used prefix the space-less name with - ``'XKCD'``. + names are prefixed with ``'xkcd:'`` (e.g., ``'xkcd:sky blue'``) to + prevent name clashes with the CSS4/X11 names. All string specifications of color are case-insensitive. Internally, mpl is moving to storing all colors as RGBA float quadruples. -Name clash between CSS4/X11 and XKCD ------------------------------------- - -The color names in the XKCD survey include spaces (unlike css4/X11 -names). Matplotlib exposes all of the XKCD colors both with and -without spaces. - -There are 95 (out of 148 colors in the css color list) conflicts -between the css4/X11 names and the XKCD names. Given that these are -the standard color names of the web, matplotlib should follow these -conventions. To accesses the XKCD colors which are shadowed by css4, -prefix the colorname with ``'XKCD'``, for example ``'blue'`` maps to -``'#0000FF'`` where as ``'XKCDblue'`` maps to ``'#0343DF'``. +There are 95 (out of 148 colors in the css color list) conflicts between the +CSS4/X11 names and the xkcd names. Given that the former are the standard +color names of the web, matplotlib should follow them. Thus, xkcd color names +are prefixed with ``'xkcd:'``, for example ``'blue'`` maps to ``'#0000FF'`` +where as ``'xkcd:blue'`` maps to ``'#0343DF'``. .. plot:: import matplotlib.pyplot as plt import matplotlib._color_data as mcd - import matplotlib.patches as mpatch - overlap = (set(mcd.CSS4_COLORS) & set(mcd.XKCD_COLORS)) + + overlap = {name for name in mcd.CSS4_COLORS + if "xkcd:" + name in mcd.XKCD_COLORS} fig = plt.figure(figsize=[4.8, 16]) ax = fig.add_axes([0, 0, 1, 1]) - j = 0 - - for n in sorted(overlap, reverse=True): + for j, n in enumerate(sorted(overlap, reverse=True)): cn = mcd.CSS4_COLORS[n] - xkcd = mcd.XKCD_COLORS[n].upper() + xkcd = mcd.XKCD_COLORS["xkcd:" + n].upper() if cn != xkcd: - print (n, cn, xkcd) + print(n, cn, xkcd) r1 = mpatch.Rectangle((0, j), 1, 1, color=cn) r2 = mpatch.Rectangle((1, j), 1, 1, color=xkcd) @@ -62,10 +52,9 @@ prefix the colorname with ``'XKCD'``, for example ``'blue'`` maps to ax.add_patch(r1) ax.add_patch(r2) ax.axhline(j, color='k') - j += 1 - ax.text(.5, j+.1, 'X11', ha='center') - ax.text(1.5, j+.1, 'XKCD', ha='center') + ax.text(.5, j + .1, 'X11', ha='center') + ax.text(1.5, j + .1, 'XKCD', ha='center') ax.set_xlim(0, 3) ax.set_ylim(0, j + 1) ax.axis('off') diff --git a/lib/matplotlib/_color_data.py b/lib/matplotlib/_color_data.py index 52351093cb3..f19c3e5d4af 100644 --- a/lib/matplotlib/_color_data.py +++ b/lib/matplotlib/_color_data.py @@ -961,14 +961,9 @@ 'green': '#15b01a', 'purple': '#7e1e9c'} -# normalize to names with no spaces and provide versions with XKCD -# prefix. -for k in list(XKCD_COLORS): - XKCD_COLORS['xkcd'+k] = XKCD_COLORS[k] - _k = k.replace(' ', '') - if _k != k: - XKCD_COLORS[_k] = XKCD_COLORS[k] - XKCD_COLORS['xkcd'+_k] = XKCD_COLORS[k] + +# Normalize name to "xkcd:" to avoid name collisions. +XKCD_COLORS = {'xkcd:' + name: value for name, value in XKCD_COLORS.items()} # https://drafts.csswg.org/css-color-4/#named-colors diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 085d4d476e7..7b59ca3d865 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -562,7 +562,7 @@ def test_xkcd(): mcolors.colorConverter.to_rgb('blue')) assert x11_blue == '#0000ff' XKCD_blue = mcolors.rgb2hex( - mcolors.colorConverter.to_rgb('XKCDblue')) + mcolors.colorConverter.to_rgb('xkcd:blue')) assert XKCD_blue == '#0343df' @@ -608,7 +608,7 @@ def test_cn(): assert red == '#ff0000' matplotlib.rcParams['axes.prop_cycle'] = cycler('color', - ['XKCDblue', 'r']) + ['xkcd:blue', 'r']) XKCD_blue = mcolors.rgb2hex(mcolors.colorConverter.to_rgb('C0')) assert XKCD_blue == '#0343df' red = mcolors.rgb2hex(mcolors.colorConverter.to_rgb('C1')) From 25bdeddd93b03ac9073379c4f40f4fd03d0e9b58 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 12 May 2016 20:05:09 -0400 Subject: [PATCH 09/19] Merge pull request #6415 from fariza/remove-gtk-linepropsdialog CLN: removing unused DialogLineprops from gtk3 --- lib/matplotlib/backends/backend_gtk3.py | 160 -------------------------------- 1 file changed, 160 deletions(-) diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index 8872e65427b..d8f2910c7f3 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -935,166 +935,6 @@ def trigger(self, sender, event, data=None): self.window.present() -class DialogLineprops(object): - """ - A GUI dialog for controlling lineprops - """ - signals = ( - 'on_combobox_lineprops_changed', - 'on_combobox_linestyle_changed', - 'on_combobox_marker_changed', - 'on_colorbutton_linestyle_color_set', - 'on_colorbutton_markerface_color_set', - 'on_dialog_lineprops_okbutton_clicked', - 'on_dialog_lineprops_cancelbutton_clicked', - ) - - linestyles = [ls for ls in lines.Line2D.lineStyles if ls.strip()] - linestyled = dict([ (s,i) for i,s in enumerate(linestyles)]) - - - markers = [m for m in lines.Line2D.markers if cbook.is_string_like(m)] - - markerd = dict([(s,i) for i,s in enumerate(markers)]) - - def __init__(self, lines): - import Gtk.glade - - datadir = matplotlib.get_data_path() - gladefile = os.path.join(datadir, 'lineprops.glade') - if not os.path.exists(gladefile): - raise IOError('Could not find gladefile lineprops.glade in %s'%datadir) - - self._inited = False - self._updateson = True # suppress updates when setting widgets manually - self.wtree = Gtk.glade.XML(gladefile, 'dialog_lineprops') - self.wtree.signal_autoconnect(dict([(s, getattr(self, s)) for s in self.signals])) - - self.dlg = self.wtree.get_widget('dialog_lineprops') - - self.lines = lines - - cbox = self.wtree.get_widget('combobox_lineprops') - cbox.set_active(0) - self.cbox_lineprops = cbox - - cbox = self.wtree.get_widget('combobox_linestyles') - for ls in self.linestyles: - cbox.append_text(ls) - cbox.set_active(0) - self.cbox_linestyles = cbox - - cbox = self.wtree.get_widget('combobox_markers') - for m in self.markers: - cbox.append_text(m) - cbox.set_active(0) - self.cbox_markers = cbox - self._lastcnt = 0 - self._inited = True - - - def show(self): - 'populate the combo box' - self._updateson = False - # flush the old - cbox = self.cbox_lineprops - for i in range(self._lastcnt-1,-1,-1): - cbox.remove_text(i) - - # add the new - for line in self.lines: - cbox.append_text(line.get_label()) - cbox.set_active(0) - - self._updateson = True - self._lastcnt = len(self.lines) - self.dlg.show() - - def get_active_line(self): - 'get the active line' - ind = self.cbox_lineprops.get_active() - line = self.lines[ind] - return line - - def get_active_linestyle(self): - 'get the active lineinestyle' - ind = self.cbox_linestyles.get_active() - ls = self.linestyles[ind] - return ls - - def get_active_marker(self): - 'get the active lineinestyle' - ind = self.cbox_markers.get_active() - m = self.markers[ind] - return m - - def _update(self): - 'update the active line props from the widgets' - if not self._inited or not self._updateson: return - line = self.get_active_line() - ls = self.get_active_linestyle() - marker = self.get_active_marker() - line.set_linestyle(ls) - line.set_marker(marker) - - button = self.wtree.get_widget('colorbutton_linestyle') - color = button.get_color() - r, g, b = [val/65535. for val in (color.red, color.green, color.blue)] - line.set_color((r,g,b)) - - button = self.wtree.get_widget('colorbutton_markerface') - color = button.get_color() - r, g, b = [val/65535. for val in (color.red, color.green, color.blue)] - line.set_markerfacecolor((r,g,b)) - - line.figure.canvas.draw() - - def on_combobox_lineprops_changed(self, item): - 'update the widgets from the active line' - if not self._inited: return - self._updateson = False - line = self.get_active_line() - - ls = line.get_linestyle() - if ls is None: ls = 'None' - self.cbox_linestyles.set_active(self.linestyled[ls]) - - marker = line.get_marker() - if marker is None: marker = 'None' - self.cbox_markers.set_active(self.markerd[marker]) - - r,g,b = colorConverter.to_rgb(line.get_color()) - color = Gdk.Color(*[int(val*65535) for val in (r,g,b)]) - button = self.wtree.get_widget('colorbutton_linestyle') - button.set_color(color) - - r,g,b = colorConverter.to_rgb(line.get_markerfacecolor()) - color = Gdk.Color(*[int(val*65535) for val in (r,g,b)]) - button = self.wtree.get_widget('colorbutton_markerface') - button.set_color(color) - self._updateson = True - - def on_combobox_linestyle_changed(self, item): - self._update() - - def on_combobox_marker_changed(self, item): - self._update() - - def on_colorbutton_linestyle_color_set(self, button): - self._update() - - def on_colorbutton_markerface_color_set(self, button): - 'called colorbutton marker clicked' - self._update() - - def on_dialog_lineprops_okbutton_clicked(self, button): - self._update() - self.dlg.hide() - - def on_dialog_lineprops_cancelbutton_clicked(self, button): - self.dlg.hide() - - # Define the file to use as the GTk icon if sys.platform == 'win32': icon_filename = 'matplotlib.png' From e785afb2047ecef42a826bbee4c5a6ed76d9d3ad Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 3 May 2016 20:25:39 -0400 Subject: [PATCH 10/19] Merge pull request #6146 from maqifrnswa/master API: ticker.LinearLocator view_limits algorithm changes closes #6142 --- doc/api/api_changes/2015-03-14-SSH.rst | 16 ++++++++++++++++ lib/matplotlib/axes/_base.py | 3 ++- lib/matplotlib/ticker.py | 5 +++-- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 doc/api/api_changes/2015-03-14-SSH.rst diff --git a/doc/api/api_changes/2015-03-14-SSH.rst b/doc/api/api_changes/2015-03-14-SSH.rst new file mode 100644 index 00000000000..902d0e766f2 --- /dev/null +++ b/doc/api/api_changes/2015-03-14-SSH.rst @@ -0,0 +1,16 @@ +``matplotlib.ticker.LinearLocator`` algorithm update +``````````````````````````` + +The ``matplotlib.ticker.LinearLocator`` is used to define the range and location +of tickmarks of a plot when the user wants a exact number of ticks. +``LinearLocator`` thus differs from the default locator ``MaxNLocator``, which +does not necessarily return the exact user requested number of ticks. + +The view range algorithm in ``matplotlib.ticker.LinearLocator`` has been +changed so that more convenient tick location are chosen. The new algorithm +returns a plot view range that is a multiple of the user requested number of +ticks. This ensures tick marks to be located at whole integers more +constistently. For example, when both y-axis of a``twinx`` plot use +``matplotlib.ticker.LinearLocator`` with the same number of ticks, the grids of +both axis will be properly aligned at convenient tick locations. + diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 2274e52bb17..e68f40a2375 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -3762,7 +3762,8 @@ def twinx(self): create a twin of Axes for generating a plot with a sharex x-axis but independent y axis. The y-axis of self will have ticks on left and the returned axes will have ticks on the - right. + right. To ensure tick marks of both axis align, see + :class:`~matplotlib.ticker.LinearLocator` .. note:: For those who are 'picking' artists while using twinx, pick diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index b22ee19ae68..df476574cd6 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1348,10 +1348,11 @@ def view_limits(self, vmin, vmax): vmax += 1 if rcParams['axes.autolimit_mode'] == 'round_numbers': - exponent, remainder = _divmod(math.log10(vmax - vmin), 1) + exponent, remainder = _divmod(math.log10(vmax - vmin), + math.log10(max([self.numticks-1, 1]))) if remainder < 0.5: exponent -= 1 - scale = 10 ** (-exponent) + scale = max([self.numticks-1, 1]) ** (-exponent) vmin = math.floor(scale * vmin) / scale vmax = math.ceil(scale * vmax) / scale From 88db38b4fe9054d5ae352bfaa06270f6c9e3f7f5 Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Fri, 13 May 2016 15:51:33 -1000 Subject: [PATCH 11/19] BUG: copy image data so it can't be modified prior to drawing Closes #6419 --- lib/matplotlib/cbook.py | 4 ++-- lib/matplotlib/image.py | 20 ++++++++-------- lib/matplotlib/tests/test_image.py | 47 +++++++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index c57d7a1f021..ecf32759fad 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -1516,8 +1516,8 @@ def issubclass_safe(x, klass): return False -def safe_masked_invalid(x): - x = np.asanyarray(x) +def safe_masked_invalid(x, copy=False): + x = np.array(x, subok=True, copy=copy) try: xm = np.ma.masked_invalid(x, copy=False) xm.shrink_mask() diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 188d991981a..9c9f7e76fd5 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -442,7 +442,7 @@ def set_data(self, A): if hasattr(A, 'getpixel'): self._A = pil_to_array(A) else: - self._A = cbook.safe_masked_invalid(A) + self._A = cbook.safe_masked_invalid(A, copy=True) if (self._A.dtype != np.uint8 and not np.can_cast(self._A.dtype, np.float)): @@ -798,9 +798,9 @@ def set_data(self, x, y, A): colormapped, or a (M,N,3) RGB array, or a (M,N,4) RGBA array. """ - x = np.asarray(x, np.float32) - y = np.asarray(y, np.float32) - A = cbook.safe_masked_invalid(A) + x = np.array(x, np.float32) + y = np.array(y, np.float32) + A = cbook.safe_masked_invalid(A, copy=True) if len(x.shape) != 1 or len(y.shape) != 1\ or A.shape[0:2] != (y.shape[0], x.shape[0]): raise TypeError("Axes don't match array shape") @@ -887,7 +887,8 @@ def __init__(self, ax, # it needs to be remade if the bbox or viewlim change, # so caching does help with zoom/pan/resize. self.update(kwargs) - self.set_data(x, y, A) + if A is not None: + self.set_data(x, y, A) def make_image(self, magnification=1.0): if self._A is None: @@ -938,15 +939,15 @@ def draw(self, renderer, *args, **kwargs): self.stale = False def set_data(self, x, y, A): - A = cbook.safe_masked_invalid(A) + A = cbook.safe_masked_invalid(A, copy=True) if x is None: x = np.arange(0, A.shape[1]+1, dtype=np.float64) else: - x = np.asarray(x, np.float64).ravel() + x = np.array(x, np.float64).ravel() if y is None: y = np.arange(0, A.shape[0]+1, dtype=np.float64) else: - y = np.asarray(y, np.float64).ravel() + y = np.array(y, np.float64).ravel() if A.shape[:2] != (y.size-1, x.size-1): print(A.shape) @@ -1044,7 +1045,8 @@ def get_extent(self): def set_data(self, A): """Set the image array.""" - cm.ScalarMappable.set_array(self, cbook.safe_masked_invalid(A)) + cm.ScalarMappable.set_array(self, + cbook.safe_masked_invalid(A, copy=True)) self.stale = True def set_array(self, A): diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 0bcb0f4157e..b844596e29b 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -12,7 +12,8 @@ from matplotlib.testing.decorators import (image_comparison, knownfailureif, cleanup) -from matplotlib.image import BboxImage, imread, NonUniformImage +from matplotlib.image import (BboxImage, imread, NonUniformImage, + AxesImage, FigureImage, PcolorImage) from matplotlib.transforms import Bbox from matplotlib import rcParams import matplotlib.pyplot as plt @@ -464,6 +465,50 @@ def test_nonuniformimage_setnorm(): @cleanup +def test_nonuniformimage_setdata(): + ax = plt.gca() + im = NonUniformImage(ax) + x = np.arange(3, dtype=np.float64) + y = np.arange(4, dtype=np.float64) + z = np.arange(12, dtype=np.float64).reshape((4, 3)) + im.set_data(x, y, z) + x[0] = y[0] = z[0, 0] = 9.9 + assert im._A[0, 0] == im._Ax[0] == im._Ay[0] == 0, 'value changed' + + +@cleanup +def test_axesimage_setdata(): + ax = plt.gca() + im = AxesImage(ax) + z = np.arange(12, dtype=np.float64).reshape((4, 3)) + im.set_data(z) + z[0, 0] = 9.9 + assert im._A[0, 0] == 0, 'value changed' + + +@cleanup +def test_figureimage_setdata(): + fig = plt.gcf() + im = FigureImage(fig) + z = np.arange(12, dtype=np.float64).reshape((4, 3)) + im.set_data(z) + z[0, 0] = 9.9 + assert im._A[0, 0] == 0, 'value changed' + + +@cleanup +def test_pcolorimage_setdata(): + ax = plt.gca() + im = PcolorImage(ax) + x = np.arange(3, dtype=np.float64) + y = np.arange(4, dtype=np.float64) + z = np.arange(6, dtype=np.float64).reshape((3, 2)) + im.set_data(x, y, z) + x[0] = y[0] = z[0, 0] = 9.9 + assert im._A[0, 0] == im._Ax[0] == im._Ay[0] == 0, 'value changed' + + +@cleanup def test_minimized_rasterized(): # This ensures that the rasterized content in the colorbars is # only as thick as the colorbar, and doesn't extend to other parts From 27b1a36cba505a97de47b62a524257f7e68ffc62 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 11 May 2016 21:44:06 -0400 Subject: [PATCH 12/19] ENH: add musecond scale to AutoDateFormatter The lack of this scale is pointed out in #6365 and was the big difference between pandas AutoDateFormatter and the stock upstream version. --- doc/users/whats_new/rcparams.rst | 40 +++++++++++++++++++++------------------- lib/matplotlib/dates.py | 8 ++++++-- lib/matplotlib/rcsetup.py | 3 ++- matplotlibrc.template | 3 ++- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/doc/users/whats_new/rcparams.rst b/doc/users/whats_new/rcparams.rst index a19cb4c3a54..5d20097637a 100644 --- a/doc/users/whats_new/rcparams.rst +++ b/doc/users/whats_new/rcparams.rst @@ -1,25 +1,27 @@ Configuration (rcParams) ------------------------ -+----------------------------+--------------------------------------------------+ -| Parameter | Description | -+============================+==================================================+ -|`date.autoformatter.year` | foramt string for 'year' scale dates | -+----------------------------+--------------------------------------------------+ -|`date.autoformatter.month` | format string for 'month' scale dates | -+----------------------------+--------------------------------------------------+ -|`date.autoformatter.day` | format string for 'day' scale dates | -+----------------------------+--------------------------------------------------+ -|`date.autoformatter.hour` | format string for 'hour' scale times | -+----------------------------+--------------------------------------------------+ -|`date.autoformatter.minute` | format string for 'minute' scale times | -+----------------------------+--------------------------------------------------+ -|`date.autoformatter.second` | format string for 'second' scale times | -+----------------------------+--------------------------------------------------+ -|`scatter.marker` | default marker for scatter plot | -+----------------------------+--------------------------------------------------+ -|`svg.hashsalt` | see note | -+----------------------------+--------------------------------------------------+ ++---------------------------------+--------------------------------------------------+ +| Parameter | Description | ++=================================+==================================================+ +|`date.autoformatter.year` | foramt string for 'year' scale dates | ++---------------------------------+--------------------------------------------------+ +|`date.autoformatter.month` | format string for 'month' scale dates | ++---------------------------------+--------------------------------------------------+ +|`date.autoformatter.day` | format string for 'day' scale dates | ++---------------------------------+--------------------------------------------------+ +|`date.autoformatter.hour` | format string for 'hour' scale times | ++---------------------------------+--------------------------------------------------+ +|`date.autoformatter.minute` | format string for 'minute' scale times | ++---------------------------------+--------------------------------------------------+ +|`date.autoformatter.second` | format string for 'second' scale times | ++---------------------------------+--------------------------------------------------+ +|`date.autoformatter.microsecond` | format string for 'microsecond' scale times | ++---------------------------------+--------------------------------------------------+ +|`scatter.marker` | default marker for scatter plot | ++---------------------------------+--------------------------------------------------+ +|`svg.hashsalt` | see note | ++---------------------------------+--------------------------------------------------+ Added ``svg.hashsalt`` key to rcParams ``````````````````````````````````````` diff --git a/lib/matplotlib/dates.py b/lib/matplotlib/dates.py index f4d6858f125..715f75fd9f9 100755 --- a/lib/matplotlib/dates.py +++ b/lib/matplotlib/dates.py @@ -634,7 +634,9 @@ class AutoDateFormatter(ticker.Formatter): 1.0: rcParams['date.autoformat.day'], 1. / HOURS_PER_DAY: rcParams['date.autoformat.hour'], 1. / (MINUTES_PER_DAY): rcParams['date.autoformat.minute'], - 1. / (SEC_PER_DAY): rcParams['date.autoformat.second']} + 1. / (SEC_PER_DAY): rcParams['date.autoformat.second'], + 1. / (MUSECONDS_PER_DAY): rcParams['date.autoformat.microsecond'], + } The algorithm picks the key in the dictionary that is >= the @@ -693,7 +695,9 @@ def __init__(self, locator, tz=None, defaultfmt='%Y-%m-%d'): 1. / (MINUTES_PER_DAY): rcParams['date.autoformatter.minute'], 1. / (SEC_PER_DAY): - rcParams['date.autoformatter.second']} + rcParams['date.autoformatter.second'], + 1. / (MUSECONDS_PER_DAY): + rcParams['date.autoformatter.microsecond']} def __call__(self, x, pos=None): locator_unit_scale = float(self._locator._get_unit()) diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index d06f054a840..6e59c262277 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -1097,7 +1097,8 @@ def validate_hist_bins(s): 'date.autoformatter.day': ['%Y-%m-%d', six.text_type], 'date.autoformatter.hour': ['%H:%M', six.text_type], 'date.autoformatter.minute': ['%H:%M:%S', six.text_type], - 'date.autoformatter.second': ['%H:%M:%S.%f', six.text_type], + 'date.autoformatter.second': ['%H:%M:%S', six.text_type], + 'date.autoformatter.microsecond': ['%H:%M:%S.%f', six.text_type], #legend properties 'legend.fancybox': [True, validate_bool], diff --git a/matplotlibrc.template b/matplotlibrc.template index f1f29599187..fddfee7d05a 100644 --- a/matplotlibrc.template +++ b/matplotlibrc.template @@ -355,7 +355,8 @@ backend : $TEMPLATE_BACKEND # date.autoformatter.day : %Y-%m-%d # date.autoformatter.hour : %H:%M # date.autoformatter.minute : %H:%M:%S -# date.autoformatter.second : %H:%M:%S.%f +# date.autoformatter.second : %H:%M:%S +# date.autoformatter.microsecond : %H:%M:%S.%f ### TICKS # see http://matplotlib.org/api/axis_api.html#matplotlib.axis.Tick From 848e77e1b77e3cfdbb26dc7483a813ad78f3ecaf Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Sat, 14 May 2016 16:53:22 -1000 Subject: [PATCH 13/19] BUG: fix contour zmin, zmax types to work with LinearLocator Closes #6270 Also removes an unused argument from the _autolev method. --- lib/matplotlib/contour.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index f5c020a8258..f816fcebbce 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -1130,7 +1130,7 @@ def changed(self): # add label colors cm.ScalarMappable.changed(self) - def _autolev(self, z, N): + def _autolev(self, N): """ Select contour levels to span the data. @@ -1166,12 +1166,12 @@ def _contour_level_args(self, z, args): self._auto = False if self.levels is None: if len(args) == 0: - lev = self._autolev(z, 7) + lev = self._autolev(7) else: level_arg = args[0] try: if type(level_arg) == int: - lev = self._autolev(z, level_arg) + lev = self._autolev(level_arg) else: lev = np.asarray(level_arg).astype(np.float64) except: @@ -1531,12 +1531,12 @@ def _contour_args(self, args, kwargs): raise TypeError("Too many arguments to %s; see help(%s)" % (fn, fn)) z = ma.masked_invalid(z, copy=False) - self.zmax = ma.maximum(z) - self.zmin = ma.minimum(z) + self.zmax = float(z.max()) + self.zmin = float(z.min()) if self.logscale and self.zmin <= 0: z = ma.masked_where(z <= 0, z) warnings.warn('Log scale: values of z <= 0 have been masked') - self.zmin = z.min() + self.zmin = float(z.min()) self._contour_level_args(z, args) return (x, y, z) From 949ecdd4857ccfb259b13d4ccdcea1c3b288ff23 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Fri, 13 May 2016 16:36:46 -0600 Subject: [PATCH 14/19] MNT: Refactor animation handling of 'bbox_inches'. Instead of a hard-coded look for either instance types or strings, make it a property of the MovieWriter instances. Set the flag to true by default for the MovieFileWriters (which all seem to work fine) and make it false by default for MovieWriters (which are currently pipe-based and break). We can also eliminate looking at strings by doing the check after we create the instance of MovieWriter (if necessary). --- lib/matplotlib/animation.py | 52 ++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index 93eed7b84b9..b85d7011e26 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -35,6 +35,7 @@ from base64 import encodestring as encodebytes import contextlib import tempfile +import warnings from matplotlib.cbook import iterable, is_string_like from matplotlib.compat import subprocess from matplotlib import verbose @@ -109,6 +110,11 @@ class MovieWriter(object): frame_format: string The format used in writing frame data, defaults to 'rgba' ''' + + # Specifies whether the size of all frames need to be identical + # i.e. whether we can use savefig.bbox = 'tight' + frame_size_can_vary = False + def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None, metadata=None): ''' @@ -283,6 +289,11 @@ def isAvailable(cls): class FileMovieWriter(MovieWriter): '`MovieWriter` subclass that handles writing to a file.' + + # In general, if frames are writen to files on disk, it's not important + # that they all be identically sized + frame_size_can_vary = True + def __init__(self, *args, **kwargs): MovieWriter.__init__(self, *args, **kwargs) self.frame_format = rcParams['animation.frame_format'] @@ -712,29 +723,6 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None, if savefig_kwargs is None: savefig_kwargs = {} - # FIXME: Using 'bbox_inches' doesn't currently work with - # writers that pipe the data to the command because this - # requires a fixed frame size (see Ryan May's reply in this - # thread: [1]). Thus we drop the 'bbox_inches' argument if it - # exists in savefig_kwargs. - # - # [1] (http://matplotlib.1069221.n5.nabble.com/ - # Animation-class-let-save-accept-kwargs-which- - # are-passed-on-to-savefig-td39627.html) - # - if 'bbox_inches' in savefig_kwargs: - if not (writer in ['ffmpeg_file', 'mencoder_file'] or - isinstance(writer, - (FFMpegFileWriter, MencoderFileWriter))): - print("Warning: discarding the 'bbox_inches' argument in " - "'savefig_kwargs' as it is only currently supported " - "with the writers 'ffmpeg_file' and 'mencoder_file' " - "(writer used: " - "'{0}').".format( - writer if isinstance(writer, six.string_types) - else writer.__class__.__name__)) - savefig_kwargs.pop('bbox_inches') - # Need to disconnect the first draw callback, since we'll be doing # draws. Otherwise, we'll end up starting the animation. if self._first_draw_id is not None: @@ -778,7 +766,6 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None, extra_args=extra_args, metadata=metadata) else: - import warnings warnings.warn("MovieWriter %s unavailable" % writer) try: @@ -792,6 +779,23 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None, verbose.report('Animation.save using %s' % type(writer), level='helpful') + + # FIXME: Using 'bbox_inches' doesn't currently work with + # writers that pipe the data to the command because this + # requires a fixed frame size (see Ryan May's reply in this + # thread: [1]). Thus we drop the 'bbox_inches' argument if it + # exists in savefig_kwargs. + # + # [1] (http://matplotlib.1069221.n5.nabble.com/ + # Animation-class-let-save-accept-kwargs-which- + # are-passed-on-to-savefig-td39627.html) + # + if 'bbox_inches' in savefig_kwargs and not writer.frame_size_can_vary: + warnings.warn("Warning: discarding the 'bbox_inches' argument in " + "'savefig_kwargs' as it not supported by " + "{0}).".format(writer.__class__.__name__)) + savefig_kwargs.pop('bbox_inches') + # Create a new sequence of frames for saved data. This is different # from new_frame_seq() to give the ability to save 'live' generated # frame information to be saved later. From 885b6fdb849e273ebda8c84d84f5bc7fb914fd29 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Fri, 13 May 2016 16:40:06 -0600 Subject: [PATCH 15/19] FIX: Animations should disable savefig.bbox as necessary. This fixes part of #6416 by resetting the 'savefig.bbox' rcParam if it is set to 'tight'. --- lib/matplotlib/animation.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index b85d7011e26..3cd24875f10 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -39,7 +39,7 @@ from matplotlib.cbook import iterable, is_string_like from matplotlib.compat import subprocess from matplotlib import verbose -from matplotlib import rcParams, rcParamsDefault +from matplotlib import rcParams, rcParamsDefault, rc_context # Process creation flag for subprocess to prevent it raising a terminal # window. See for example: @@ -801,17 +801,26 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None, # frame information to be saved later. # TODO: Right now, after closing the figure, saving a movie won't work # since GUI widgets are gone. Either need to remove extra code to - # allow for this non-existant use case or find a way to make it work. - with writer.saving(self._fig, filename, dpi): - for anim in all_anim: - # Clear the initial frame - anim._init_draw() - for data in zip(*[a.new_saved_frame_seq() - for a in all_anim]): - for anim, d in zip(all_anim, data): - # TODO: Need to see if turning off blit is really necessary - anim._draw_next_frame(d, blit=False) - writer.grab_frame(**savefig_kwargs) + # allow for this non-existent use case or find a way to make it work. + with rc_context(): + # See above about bbox_inches savefig kwarg + if (not writer.frame_size_can_vary and + rcParams['savefig.bbox'] == 'tight'): + verbose.report("Disabling savefig.bbox = 'tight', as it is " + "not supported by " + "{0}.".format(writer.__class__.__name__), + level='helpful') + rcParams['savefig.bbox'] = None + with writer.saving(self._fig, filename, dpi): + for anim in all_anim: + # Clear the initial frame + anim._init_draw() + for data in zip(*[a.new_saved_frame_seq() + for a in all_anim]): + for anim, d in zip(all_anim, data): + # TODO: See if turning off blit is really necessary + anim._draw_next_frame(d, blit=False) + writer.grab_frame(**savefig_kwargs) # Reconnect signal for first draw if necessary if reconnect_first_draw: From d28e85302d17a14bfab3ad51bc7cc4f9ee380092 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Fri, 13 May 2016 16:44:37 -0600 Subject: [PATCH 16/19] MNT: Move comment next to code it applies to: --- lib/matplotlib/animation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index 3cd24875f10..d43d7e97c3e 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -420,8 +420,6 @@ class FFMpegBase(object): @property def output_args(self): - # The %dk adds 'k' as a suffix so that ffmpeg treats our bitrate as in - # kbps args = ['-vcodec', self.codec] # For h264, the default format is yuv444p, which is not compatible # with quicktime (and others). Specifying yuv420p fixes playback on @@ -429,6 +427,8 @@ def output_args(self): # OSX). Also fixes internet explorer. This is as of 2015/10/29. if self.codec == 'h264' and '-pix_fmt' not in self.extra_args: args.extend(['-pix_fmt', 'yuv420p']) + # The %dk adds 'k' as a suffix so that ffmpeg treats our bitrate as in + # kbps if self.bitrate > 0: args.extend(['-b', '%dk' % self.bitrate]) if self.extra_args: From 229ddf64bac1cb11c30cd530d77b793f6df8c2d1 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Fri, 13 May 2016 16:45:31 -0600 Subject: [PATCH 17/19] MNT: Fix some misspellings. --- lib/matplotlib/animation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index d43d7e97c3e..b45d3ca3561 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -133,8 +133,8 @@ def __init__(self, fps=5, codec=None, bitrate=None, extra_args=None, automatically by the underlying utility. extra_args: list of strings or None A list of extra string arguments to be passed to the underlying - movie utiltiy. The default is None, which passes the additional - argurments in the 'animation.extra_args' rcParam. + movie utility. The default is None, which passes the additional + arguments in the 'animation.extra_args' rcParam. metadata: dict of string:string or None A dictionary of keys and values for metadata to include in the output file. Some keys that may be of use include: @@ -702,8 +702,8 @@ def save(self, filename, writer=None, fps=None, dpi=None, codec=None, `animation.bitrate`. *extra_args* is a list of extra string arguments to be passed to the - underlying movie utiltiy. The default is None, which passes the - additional argurments in the 'animation.extra_args' rcParam. + underlying movie utility. The default is None, which passes the + additional arguments in the 'animation.extra_args' rcParam. *metadata* is a dictionary of keys and values for metadata to include in the output file. Some keys that may be of use include: @@ -947,7 +947,7 @@ def to_html5_video(self): directly into the HTML5 video tag. This respects the rc parameters for the writer as well as the bitrate. This also makes use of the ``interval`` to control the speed, and uses the ``repeat`` - paramter to decide whether to loop. + parameter to decide whether to loop. ''' VIDEO_TAG = r'''