From 643c74b96459bb26bd1d366241197a646bafb72b Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sun, 21 Jul 2013 01:53:58 -0500 Subject: [PATCH 01/35] Add easy style sheet selection. --- doc/conf.py | 4 +- examples/style_sheets/plot_dark_background.py | 24 ++++ examples/style_sheets/plot_ggplot.py | 51 +++++++++ examples/style_sheets/plot_grayscale.py | 34 ++++++ lib/matplotlib/style/__init__.py | 2 + lib/matplotlib/style/core.py | 106 ++++++++++++++++++ .../style/stylelib/dark_background.mplrc | 23 ++++ lib/matplotlib/style/stylelib/ggplot.mplrc | 39 +++++++ lib/matplotlib/style/stylelib/grayscale.mplrc | 29 +++++ 9 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 examples/style_sheets/plot_dark_background.py create mode 100644 examples/style_sheets/plot_ggplot.py create mode 100644 examples/style_sheets/plot_grayscale.py create mode 100644 lib/matplotlib/style/__init__.py create mode 100644 lib/matplotlib/style/core.py create mode 100644 lib/matplotlib/style/stylelib/dark_background.mplrc create mode 100644 lib/matplotlib/style/stylelib/ggplot.mplrc create mode 100644 lib/matplotlib/style/stylelib/grayscale.mplrc diff --git a/doc/conf.py b/doc/conf.py index d9712743abfc..e62952541a75 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -28,7 +28,8 @@ extensions = ['matplotlib.sphinxext.mathmpl', 'sphinxext.math_symbol_table', 'sphinx.ext.autodoc', 'matplotlib.sphinxext.only_directives', 'sphinx.ext.doctest', 'sphinx.ext.autosummary', - 'matplotlib.sphinxext.plot_directive', 'sphinx.ext.inheritance_diagram', + 'matplotlib.sphinxext.plot_directive', + 'sphinx.ext.inheritance_diagram', 'sphinxext.gen_gallery', 'sphinxext.gen_rst', 'matplotlib.sphinxext.ipython_console_highlighting', 'sphinxext.github', @@ -117,6 +118,7 @@ ('text_labels_and_annotations', 'Text, labels, and annotations'), ('ticks_and_spines', 'Ticks and spines'), ('subplots_axes_and_figures', 'Subplots, axes, and figures'), + ('style_sheets', 'Style sheets'), ('specialty_plots', 'Specialty plots'), ('showcase', 'Showcase'), ('api', 'API'), diff --git a/examples/style_sheets/plot_dark_background.py b/examples/style_sheets/plot_dark_background.py new file mode 100644 index 000000000000..acb94d4eafa9 --- /dev/null +++ b/examples/style_sheets/plot_dark_background.py @@ -0,0 +1,24 @@ +""" +This example demonstrates the "dark_background" style, which uses white for +elements that are typically black (text, borders, etc). Note, however, that not +all plot elements default to colors defined by an rc parameter. + +""" +import numpy as np +import matplotlib.pyplot as plt + +from matplotlib import style +style.use('dark_background') + + +L = 6 +x = np.linspace(0, L) +ncolors = len(plt.rcParams['axes.color_cycle']) +shift = np.linspace(0, L, ncolors, endpoint=False) +for s in shift: + plt.plot(x, np.sin(x + s), 'o-') +plt.xlabel('x-axis') +plt.ylabel('y-axis') +plt.title('title') + +plt.show() diff --git a/examples/style_sheets/plot_ggplot.py b/examples/style_sheets/plot_ggplot.py new file mode 100644 index 000000000000..726ecb9ce462 --- /dev/null +++ b/examples/style_sheets/plot_ggplot.py @@ -0,0 +1,51 @@ +""" +This example demonstrates the "ggplot" style, which adjusts the style to +emulate ggplot_ (a popular plotting package for R_). + +These settings were shamelessly stolen from [1]_ (with permission). + +.. [1] http://www.huyng.com/posts/sane-color-scheme-for-matplotlib/ + +.. _ggplot: http://had.co.nz/ggplot/ +.. _R: http://www.r-project.org/ + +""" +import numpy as np +import matplotlib.pyplot as plt +from matplotlib import style + +style.use('ggplot') + +fig, axes = plt.subplots(ncols=2, nrows=2) +ax1, ax2, ax3, ax4 = axes.ravel() + +# scatter plot (Note: `plt.scatter` doesn't use default colors) +x, y = np.random.normal(size=(2, 200)) +ax1.plot(x, y, 'o') + +# sinusoidal lines with colors from default color cycle +L = 2*np.pi +x = np.linspace(0, L) +ncolors = len(plt.rcParams['axes.color_cycle']) +shift = np.linspace(0, L, ncolors, endpoint=False) +for s in shift: + ax2.plot(x, np.sin(x + s), '-') +ax2.margins(0) + +# bar graphs +x = np.arange(5) +y1, y2 = np.random.randint(1, 25, size=(2, 5)) +width = 0.25 +ax3.bar(x, y1, width) +ax3.bar(x+width, y2, width, color=plt.rcParams['axes.color_cycle'][2]) +ax3.set_xticks(x+width) +ax3.set_xticklabels(['a', 'b', 'c', 'd', 'e']) + +# circles with colors from default color cycle +for i, color in enumerate(plt.rcParams['axes.color_cycle']): + xy = np.random.normal(size=2) + ax4.add_patch(plt.Circle(xy, radius=0.3, color=color)) +ax4.axis('equal') +ax4.margins(0) + +plt.show() diff --git a/examples/style_sheets/plot_grayscale.py b/examples/style_sheets/plot_grayscale.py new file mode 100644 index 000000000000..a6d04dc48970 --- /dev/null +++ b/examples/style_sheets/plot_grayscale.py @@ -0,0 +1,34 @@ +""" +This example demonstrates the "grayscale" style sheet, which changes all colors +that are defined as rc parameters to grayscale. Note, however, that not all +plot elements default to colors defined by an rc parameter. + +""" +import numpy as np +import matplotlib.pyplot as plt + +from matplotlib import style + + +def color_cycle_example(ax): + L = 6 + x = np.linspace(0, L) + ncolors = len(plt.rcParams['axes.color_cycle']) + shift = np.linspace(0, L, ncolors, endpoint=False) + for s in shift: + ax.plot(x, np.sin(x + s), 'o-') + +def image_and_patch_example(ax): + ax.imshow(np.random.random(size=(20, 20)), interpolation='none') + c = plt.Circle((5, 5), radius=5, label='patch') + ax.add_patch(c) + + +style.use('grayscale') + +fig, (ax1, ax2) = plt.subplots(ncols=2) + +color_cycle_example(ax1) +image_and_patch_example(ax2) + +plt.show() diff --git a/lib/matplotlib/style/__init__.py b/lib/matplotlib/style/__init__.py new file mode 100644 index 000000000000..c98e84e2ac93 --- /dev/null +++ b/lib/matplotlib/style/__init__.py @@ -0,0 +1,2 @@ +from core import * + diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py new file mode 100644 index 000000000000..92d916777190 --- /dev/null +++ b/lib/matplotlib/style/core.py @@ -0,0 +1,106 @@ +""" +Core functions and attributes for the matplotlib style library: + +``use`` + Select style sheet to override the current matplotlib settings. +``available`` + List available style sheets. +``library`` + A dictionary of style names and matplotlib settings. +""" +import os +import re + +import numpy as np +import matplotlib.pyplot as plt +import matplotlib as mpl + + +__all__ = ['use', 'available', 'library'] + + +_here = os.path.abspath(os.path.dirname(__file__)) +BASE_LIBRARY_PATH = os.path.join(_here, 'stylelib') +# Users may want multiple library paths, so store a list of paths. +USER_LIBRARY_PATHS = [os.path.join('~', '.matplotlib', 'stylelib')] +STYLE_FILE_PATTERN = re.compile('([A-Za-z._-]+).mplrc$') + + +def use(name): + """Use matplotlib rc parameters from a pre-defined name or from a file. + + Parameters + ---------- + name : str or list of str + Name of style. For list of available styles see `style.available`. + If given a list, each style is applied from first to last in the list. + """ + if np.isscalar(name): + name = [name] + for s in name: + plt.rcParams.update(library[s]) + + +def load_base_library(): + """Load style library defined in this package.""" + library = dict() + library.update(read_style_directory(BASE_LIBRARY_PATH)) + return library + + +def iter_user_libraries(): + for stylelib_path in USER_LIBRARY_PATHS: + stylelib_path = os.path.expanduser(stylelib_path) + if os.path.exists(stylelib_path) and os.path.isdir(stylelib_path): + yield stylelib_path + + +def update_user_library(library): + """Update style library with user-defined rc files""" + for stylelib_path in iter_user_libraries(): + styles = read_style_directory(stylelib_path) + update_nested_dict(library, styles) + return library + + +def iter_style_files(style_dir): + """Yield file path and name of styles in the given directory.""" + for path in os.listdir(style_dir): + filename = os.path.basename(path) + match = STYLE_FILE_PATTERN.match(filename) + if match: + path = os.path.abspath(os.path.join(style_dir, path)) + yield path, match.groups()[0] + + +def read_style_directory(style_dir): + """Return dictionary of styles defined in `style_dir`.""" + styles = dict() + for path, name in iter_style_files(style_dir): + styles[name] = mpl.rc_params_from_file(path) + return styles + + +def update_nested_dict(main_dict, new_dict): + """Update nested dict (only level of nesting) with new values. + + Unlike dict.update, this assumes that the values of the parent dict are + dicts (or dict-like), so you shouldn't replace the nested dict if it + already exists. Instead you should update the sub-dict. + """ + # update named styles specified by user + for name, rc_dict in new_dict.iteritems(): + if name in main_dict: + # FIXME: This is currently broken because rc_params_from_file fills + # in all settings so the update overwrites all values. + main_dict[name].update(rc_dict) + else: + main_dict[name] = rc_dict + return main_dict + + +# Load style library +# ================== +_base_library = load_base_library() +library = update_user_library(_base_library) +available = library.keys() diff --git a/lib/matplotlib/style/stylelib/dark_background.mplrc b/lib/matplotlib/style/stylelib/dark_background.mplrc new file mode 100644 index 000000000000..c3557c1f12b4 --- /dev/null +++ b/lib/matplotlib/style/stylelib/dark_background.mplrc @@ -0,0 +1,23 @@ +# Set black background default line colors to white. + +lines.color: white +patch.edgecolor: white + +text.color: white + +axes.facecolor: black +axes.edgecolor: white +axes.labelcolor: white +axes.color_cycle: 8dd3c7, feffb3, bfbbd9, fa8174, 81b1d2, fdb462, b3de69, bc82bd, ccebc4, ffed6f + +xtick.color: white +ytick.color: white + +grid.color: white + +figure.facecolor: black +figure.edgecolor: black + +savefig.facecolor: black +savefig.edgecolor: black + diff --git a/lib/matplotlib/style/stylelib/ggplot.mplrc b/lib/matplotlib/style/stylelib/ggplot.mplrc new file mode 100644 index 000000000000..54949aa6d7b5 --- /dev/null +++ b/lib/matplotlib/style/stylelib/ggplot.mplrc @@ -0,0 +1,39 @@ +# from http://www.huyng.com/posts/sane-color-scheme-for-matplotlib/ + +patch.linewidth: 0.5 +patch.facecolor: 348ABD # blue +patch.edgecolor: EEEEEE +patch.antialiased: True + +font.size: 10.0 + +axes.facecolor: E5E5E5 +axes.edgecolor: white +axes.linewidth: 1 +axes.grid: True +axes.titlesize: x-large +axes.labelsize: large +axes.labelcolor: 555555 +axes.axisbelow: True # grid/ticks are below elements (eg lines, text) + +axes.color_cycle: E24A33, 348ABD, 988ED5, 777777, FBC15E, 8EBA42, FFB5B8 + # E24A33 : red + # 348ABD : blue + # 988ED5 : purple + # 777777 : gray + # FBC15E : yellow + # 8EBA42 : green + # FFB5B8 : pink + +xtick.color: 555555 +xtick.direction: out + +ytick.color: 555555 +ytick.direction: out + +grid.color: white +grid.linestyle: - # solid line + +figure.facecolor: white +figure.edgecolor: 0.50 + diff --git a/lib/matplotlib/style/stylelib/grayscale.mplrc b/lib/matplotlib/style/stylelib/grayscale.mplrc new file mode 100644 index 000000000000..2012b1b95d36 --- /dev/null +++ b/lib/matplotlib/style/stylelib/grayscale.mplrc @@ -0,0 +1,29 @@ +# Set all colors to grayscale +# Note: strings of float values are interpreted by matplotlib as gray values. + + +lines.color: black +patch.facecolor: gray +patch.edgecolor: black + +text.color: black + +axes.facecolor: white +axes.edgecolor: black +axes.labelcolor: black +# black to light gray +axes.color_cycle: 0.00, 0.40, 0.60, 0.70 + +xtick.color: black +ytick.color: black + +grid.color: black + +figure.facecolor: 0.75 +figure.edgecolor: white + +image.cmap: gray + +savefig.facecolor: white +savefig.edgecolor: white + From 3270aa4a96eb5f28fd8d5a01e737fe4e715d5373 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sun, 21 Jul 2013 10:47:21 -0500 Subject: [PATCH 02/35] Add rc_params_in_file to return partially-filled RcParams This allows style sheets to update the current settings instead of overwriting them. --- lib/matplotlib/__init__.py | 78 +++++++++++++++++++++++++----------- lib/matplotlib/style/core.py | 2 +- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 7207a5ae7442..26e6db8bf96e 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -878,9 +878,14 @@ def rc_params(fail_on_error=False): return rc_params_from_file(fname, fail_on_error) -def rc_params_from_file(fname, fail_on_error=False): - """Return a :class:`matplotlib.RcParams` instance from the - contents of the given filename. +_error_details_fmt = 'line #%d\n\t"%s"\n\tin file "%s"' + + +def rc_params_in_file(fname, fail_on_error=False): + """Return :class:`matplotlib.RcParams` from the contents of the given file. + + Unlike `rc_params_from_file`, the configuration class only contains the + parameters specified in the file (i.e. default values are not filled in). """ cnt = 0 rc_temp = {} @@ -891,8 +896,8 @@ def rc_params_from_file(fname, fail_on_error=False): if not strippedline: continue tup = strippedline.split(':', 1) if len(tup) != 2: - warnings.warn('Illegal line #%d\n\t%s\n\tin file "%s"' % \ - (cnt, line, fname)) + error_details = _error_details_fmt % (cnt, line, fname) + warnings.warn('Illegal %s' % error_details) continue key, val = tup key = key.strip() @@ -902,34 +907,35 @@ def rc_params_from_file(fname, fail_on_error=False): (fname, cnt)) rc_temp[key] = (val, line, cnt) - ret = RcParams([(key, default) for key, (default, _) in \ - six.iteritems(defaultParams)]) + config = RcParams() for key in ('verbose.level', 'verbose.fileo'): if key in rc_temp: val, line, cnt = rc_temp.pop(key) if fail_on_error: - ret[key] = val # try to convert to proper type or raise + config[key] = val # try to convert to proper type or raise else: - try: ret[key] = val # try to convert to proper type or skip + try: + config[key] = val # try to convert to proper type or skip except Exception as msg: - warnings.warn('Bad val "%s" on line #%d\n\t"%s"\n\tin file \ -"%s"\n\t%s' % (val, cnt, line, fname, msg)) - - verbose.set_level(ret['verbose.level']) - verbose.set_fileo(ret['verbose.fileo']) + error_details = _error_details_fmt % (cnt, line, fname) + warnings.warn('Bad val "%s" on %s\n\t%s' % + (val, error_details, msg)) for key, (val, line, cnt) in six.iteritems(rc_temp): if key in defaultParams: if fail_on_error: - ret[key] = val # try to convert to proper type or raise + config[key] = val # try to convert to proper type or raise else: - try: ret[key] = val # try to convert to proper type or skip + try: + config[key] = val # try to convert to proper type or skip except Exception as msg: - warnings.warn('Bad val "%s" on line #%d\n\t"%s"\n\tin file \ -"%s"\n\t%s' % (val, cnt, line, fname, msg)) + error_details = _error_details_fmt % (cnt, line, fname) + warnings.warn('Bad val "%s" on %s\n\t%s' % + (val, error_details, msg)) elif key in _deprecated_ignore_map: - warnings.warn('%s is deprecated. Update your matplotlibrc to use %s instead.'% (key, _deprecated_ignore_map[key])) + warnings.warn('%s is deprecated. Update your matplotlibrc to use ' + '%s instead.'% (key, _deprecated_ignore_map[key])) else: print(""" @@ -939,21 +945,45 @@ def rc_params_from_file(fname, fail_on_error=False): http://matplotlib.sf.net/_static/matplotlibrc or from the matplotlib source distribution""" % (key, cnt, fname), file=sys.stderr) - if ret['datapath'] is None: - ret['datapath'] = get_data_path() + return config + + + + +def rc_params_from_file(fname, fail_on_error=False): + """Return :class:`matplotlib.RcParams` from the contents of the given file. + + Parameters + ---------- + fname : str + Name of file parsed for matplotlib settings. + fail_on_error : bool + If True, raise an error when the parser fails to convert a parameter. + """ + + config = RcParams([(key, default) + for key, (default, _) in six.iteritems(defaultParams)]) + + config.update(rc_params_in_file(fname, fail_on_error)) + + verbose.set_level(config['verbose.level']) + verbose.set_fileo(config['verbose.fileo']) + + if config['datapath'] is None: + config['datapath'] = get_data_path() - if not ret['text.latex.preamble'] == ['']: + if not config['text.latex.preamble'] == ['']: verbose.report(""" ***************************************************************** You have the following UNSUPPORTED LaTeX preamble customizations: %s Please do not ask for support with these customizations active. ***************************************************************** -"""% '\n'.join(ret['text.latex.preamble']), 'helpful') +"""% '\n'.join(config['text.latex.preamble']), 'helpful') verbose.report('loaded rc file %s'%fname) - return ret + return config # this is the instance used by the matplotlib classes diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 92d916777190..fc6220f372a7 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -77,7 +77,7 @@ def read_style_directory(style_dir): """Return dictionary of styles defined in `style_dir`.""" styles = dict() for path, name in iter_style_files(style_dir): - styles[name] = mpl.rc_params_from_file(path) + styles[name] = mpl.rc_params_in_file(path) return styles From d83a03ce6ca93a151259864fed991363c71a8203 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sun, 21 Jul 2013 10:53:16 -0500 Subject: [PATCH 03/35] Rename style files to `*.style` --- lib/matplotlib/style/core.py | 2 +- .../stylelib/{dark_background.mplrc => dark_background.style} | 0 lib/matplotlib/style/stylelib/{ggplot.mplrc => ggplot.style} | 0 .../style/stylelib/{grayscale.mplrc => grayscale.style} | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename lib/matplotlib/style/stylelib/{dark_background.mplrc => dark_background.style} (100%) rename lib/matplotlib/style/stylelib/{ggplot.mplrc => ggplot.style} (100%) rename lib/matplotlib/style/stylelib/{grayscale.mplrc => grayscale.style} (100%) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index fc6220f372a7..1a5b28615e73 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -23,7 +23,7 @@ BASE_LIBRARY_PATH = os.path.join(_here, 'stylelib') # Users may want multiple library paths, so store a list of paths. USER_LIBRARY_PATHS = [os.path.join('~', '.matplotlib', 'stylelib')] -STYLE_FILE_PATTERN = re.compile('([A-Za-z._-]+).mplrc$') +STYLE_FILE_PATTERN = re.compile('([A-Za-z._-]+).style$') def use(name): diff --git a/lib/matplotlib/style/stylelib/dark_background.mplrc b/lib/matplotlib/style/stylelib/dark_background.style similarity index 100% rename from lib/matplotlib/style/stylelib/dark_background.mplrc rename to lib/matplotlib/style/stylelib/dark_background.style diff --git a/lib/matplotlib/style/stylelib/ggplot.mplrc b/lib/matplotlib/style/stylelib/ggplot.style similarity index 100% rename from lib/matplotlib/style/stylelib/ggplot.mplrc rename to lib/matplotlib/style/stylelib/ggplot.style diff --git a/lib/matplotlib/style/stylelib/grayscale.mplrc b/lib/matplotlib/style/stylelib/grayscale.style similarity index 100% rename from lib/matplotlib/style/stylelib/grayscale.mplrc rename to lib/matplotlib/style/stylelib/grayscale.style From c8cc486d6fcc5020c17c46cd25bd8162f0ace0b5 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sun, 21 Jul 2013 15:03:25 -0500 Subject: [PATCH 04/35] Allow style.use to open URLs --- lib/matplotlib/__init__.py | 37 ++++++++++++++++++++++++++++++------ lib/matplotlib/style/core.py | 31 +++++++++++++++++++++--------- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 26e6db8bf96e..9bb6b03d51e7 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -155,7 +155,16 @@ def _forward_ilshift(self, other): return self pyparsing.Forward.__ilshift__ = _forward_ilshift -import os, re, shutil, warnings +try: + from urllib.request import urlopen +except ImportError: + from urllib2 import urlopen + +import os +import re +import tempfile +import warnings +import contextlib import distutils.sysconfig # cbook must import matplotlib only within function @@ -174,11 +183,8 @@ def _forward_ilshift(self, other): sys.argv = ['modpython'] -import sys, os, tempfile - from matplotlib.rcsetup import (defaultParams, - validate_backend, - validate_toolbar) + validate_backend) major, minor1, minor2, s, tmp = sys.version_info _python24 = (major == 2 and minor1 >= 4) or major >= 3 @@ -878,6 +884,25 @@ def rc_params(fail_on_error=False): return rc_params_from_file(fname, fail_on_error) +URL_REGEX = re.compile(r'http://|https://|ftp://|file://|file:\\') + + +def is_url(filename): + """Return True if string is an http or ftp path.""" + return URL_REGEX.match(filename) is not None + + +@contextlib.contextmanager +def _open_file_or_url(fname): + if is_url(fname): + f = urlopen(fname) + yield f + f.close() + else: + with open(fname) as f: + yield f + + _error_details_fmt = 'line #%d\n\t"%s"\n\tin file "%s"' @@ -889,7 +914,7 @@ def rc_params_in_file(fname, fail_on_error=False): """ cnt = 0 rc_temp = {} - with open(fname) as fd: + with _open_file_or_url(fname) as fd: for line in fd: cnt += 1 strippedline = line.split('#', 1)[0].strip() diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 1a5b28615e73..375b88c2aa98 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -23,7 +23,12 @@ BASE_LIBRARY_PATH = os.path.join(_here, 'stylelib') # Users may want multiple library paths, so store a list of paths. USER_LIBRARY_PATHS = [os.path.join('~', '.matplotlib', 'stylelib')] -STYLE_FILE_PATTERN = re.compile('([A-Za-z._-]+).style$') +STYLE_FILE_PATTERN = re.compile('([\S]+).style$') + + +def is_style_file(filename): + """Return True if the filename looks like a style file.""" + return STYLE_FILE_PATTERN.match(filename) is not None def use(name): @@ -32,13 +37,23 @@ def use(name): Parameters ---------- name : str or list of str - Name of style. For list of available styles see `style.available`. - If given a list, each style is applied from first to last in the list. + Name of style or path/URL to a style file. For a list of available + style names, see `style.available`. If given a list, each style is + applied from first to last in the list. """ if np.isscalar(name): name = [name] - for s in name: - plt.rcParams.update(library[s]) + + for style in name: + if is_style_file(style): + settings = mpl.rc_params_in_file(style) + plt.rcParams.update(settings) + elif style not in library: + msg = ("'%s' not found in the style library. " + "See `style.available` for list of available styles.") + raise ValueError(msg % style) + else: + plt.rcParams.update(library[style]) def load_base_library(): @@ -67,8 +82,8 @@ def iter_style_files(style_dir): """Yield file path and name of styles in the given directory.""" for path in os.listdir(style_dir): filename = os.path.basename(path) - match = STYLE_FILE_PATTERN.match(filename) - if match: + if is_style_file(filename): + match = STYLE_FILE_PATTERN.match(filename) path = os.path.abspath(os.path.join(style_dir, path)) yield path, match.groups()[0] @@ -91,8 +106,6 @@ def update_nested_dict(main_dict, new_dict): # update named styles specified by user for name, rc_dict in new_dict.iteritems(): if name in main_dict: - # FIXME: This is currently broken because rc_params_from_file fills - # in all settings so the update overwrites all values. main_dict[name].update(rc_dict) else: main_dict[name] = rc_dict From 455b54ca9cbe28ffbda3a8effc80d27121d8dcd7 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sun, 21 Jul 2013 16:10:14 -0500 Subject: [PATCH 05/35] Remove pyplot import --- lib/matplotlib/style/core.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 375b88c2aa98..c50dcdb1be44 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -12,7 +12,6 @@ import re import numpy as np -import matplotlib.pyplot as plt import matplotlib as mpl @@ -47,13 +46,13 @@ def use(name): for style in name: if is_style_file(style): settings = mpl.rc_params_in_file(style) - plt.rcParams.update(settings) + mpl.rcParams.update(settings) elif style not in library: msg = ("'%s' not found in the style library. " "See `style.available` for list of available styles.") raise ValueError(msg % style) else: - plt.rcParams.update(library[style]) + mpl.rcParams.update(library[style]) def load_base_library(): From 7769b2940768ee81ca2ed031f51ef6b3ed61e3e8 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Mon, 22 Jul 2013 23:16:55 -0500 Subject: [PATCH 06/35] Add style context manager and tests --- lib/matplotlib/style/core.py | 44 ++++++++++++++++-- lib/matplotlib/style/tests/test_core.py | 61 +++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 lib/matplotlib/style/tests/test_core.py diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index c50dcdb1be44..689305c022cb 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -3,6 +3,8 @@ ``use`` Select style sheet to override the current matplotlib settings. +``context`` + Context manager to use a style sheet temporarily. ``available`` List available style sheets. ``library`` @@ -10,19 +12,21 @@ """ import os import re +import contextlib import numpy as np import matplotlib as mpl -__all__ = ['use', 'available', 'library'] +__all__ = ['use', 'context', 'available', 'library', 'reload_library'] _here = os.path.abspath(os.path.dirname(__file__)) BASE_LIBRARY_PATH = os.path.join(_here, 'stylelib') # Users may want multiple library paths, so store a list of paths. USER_LIBRARY_PATHS = [os.path.join('~', '.matplotlib', 'stylelib')] -STYLE_FILE_PATTERN = re.compile('([\S]+).style$') +STYLE_EXTENSION = 'style' +STYLE_FILE_PATTERN = re.compile('([\S]+).%s$' % STYLE_EXTENSION) def is_style_file(filename): @@ -31,7 +35,7 @@ def is_style_file(filename): def use(name): - """Use matplotlib rc parameters from a pre-defined name or from a file. + """Use matplotlib style settings from a known style sheet or from a file. Parameters ---------- @@ -55,6 +59,28 @@ def use(name): mpl.rcParams.update(library[style]) +@contextlib.contextmanager +def context(name, after_reset=False): + """Context manager for using style settings temporarily. + + Parameters + ---------- + name : str or list of str + Name of style or path/URL to a style file. For a list of available + style names, see `style.available`. If given a list, each style is + applied from first to last in the list. + after_reset : bool + If True, apply style after resetting settings to their defaults; + otherwise, apply style on top of the current settings. + """ + initial_settings = mpl.rcParams.copy() + if after_reset: + mpl.rcdefaults() + use(name) + yield + mpl.rcParams.update(initial_settings) + + def load_base_library(): """Load style library defined in this package.""" library = dict() @@ -114,5 +140,13 @@ def update_nested_dict(main_dict, new_dict): # Load style library # ================== _base_library = load_base_library() -library = update_user_library(_base_library) -available = library.keys() + +library = None +available = [] + +def reload_library(): + """Reload style library.""" + global library, available + library = update_user_library(_base_library) + available[:] = library.keys() +reload_library() diff --git a/lib/matplotlib/style/tests/test_core.py b/lib/matplotlib/style/tests/test_core.py new file mode 100644 index 000000000000..9300b22f2fff --- /dev/null +++ b/lib/matplotlib/style/tests/test_core.py @@ -0,0 +1,61 @@ +import os +import shutil +import tempfile +from contextlib import contextmanager + +import matplotlib as mpl +from matplotlib import style +from matplotlib.style.core import USER_LIBRARY_PATHS, STYLE_EXTENSION + + +PARAM = 'image.cmap' +VALUE = 'pink' +DUMMY_SETTINGS = {PARAM: VALUE} + + +@contextmanager +def temp_style(style_name, settings=None): + """Context manager to create a style sheet in a temporary directory.""" + settings = DUMMY_SETTINGS + temp_file = '%s.%s' % (style_name, STYLE_EXTENSION) + + # Write style settings to file in the temp directory. + tempdir = tempfile.mkdtemp() + with open(os.path.join(tempdir, temp_file), 'w') as f: + for k, v in settings.iteritems(): + f.write('%s: %s' % (k, v)) + + # Add temp directory to style path and reload so we can access this style. + USER_LIBRARY_PATHS.append(tempdir) + style.reload_library() + + try: + yield + finally: + shutil.rmtree(tempdir) + + +def test_available(): + with temp_style('_test_', DUMMY_SETTINGS): + assert '_test_' in style.available + + +def test_use(): + mpl.rcParams[PARAM] = 'gray' + with temp_style('test', DUMMY_SETTINGS): + style.use('test') + assert mpl.rcParams[PARAM] == VALUE + + +def test_context(): + mpl.rcParams[PARAM] = 'gray' + with temp_style('test', DUMMY_SETTINGS): + with style.context('test'): + assert mpl.rcParams[PARAM] == VALUE + # Check that this value is reset after the exiting the context. + assert mpl.rcParams[PARAM] == 'gray' + + +if __name__ == '__main__': + from numpy import testing + testing.run_module_suite() From 391408928823efa6da45a8b03015d21eaf3d6804 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Mon, 22 Jul 2013 23:37:08 -0500 Subject: [PATCH 07/35] Change style extension to *.mplstyle --- lib/matplotlib/style/core.py | 2 +- .../{dark_background.style => dark_background.mplstyle} | 0 lib/matplotlib/style/stylelib/{ggplot.style => ggplot.mplstyle} | 0 .../style/stylelib/{grayscale.style => grayscale.mplstyle} | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename lib/matplotlib/style/stylelib/{dark_background.style => dark_background.mplstyle} (100%) rename lib/matplotlib/style/stylelib/{ggplot.style => ggplot.mplstyle} (100%) rename lib/matplotlib/style/stylelib/{grayscale.style => grayscale.mplstyle} (100%) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 689305c022cb..24610eb9be74 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -25,7 +25,7 @@ BASE_LIBRARY_PATH = os.path.join(_here, 'stylelib') # Users may want multiple library paths, so store a list of paths. USER_LIBRARY_PATHS = [os.path.join('~', '.matplotlib', 'stylelib')] -STYLE_EXTENSION = 'style' +STYLE_EXTENSION = 'mplstyle' STYLE_FILE_PATTERN = re.compile('([\S]+).%s$' % STYLE_EXTENSION) diff --git a/lib/matplotlib/style/stylelib/dark_background.style b/lib/matplotlib/style/stylelib/dark_background.mplstyle similarity index 100% rename from lib/matplotlib/style/stylelib/dark_background.style rename to lib/matplotlib/style/stylelib/dark_background.mplstyle diff --git a/lib/matplotlib/style/stylelib/ggplot.style b/lib/matplotlib/style/stylelib/ggplot.mplstyle similarity index 100% rename from lib/matplotlib/style/stylelib/ggplot.style rename to lib/matplotlib/style/stylelib/ggplot.mplstyle diff --git a/lib/matplotlib/style/stylelib/grayscale.style b/lib/matplotlib/style/stylelib/grayscale.mplstyle similarity index 100% rename from lib/matplotlib/style/stylelib/grayscale.style rename to lib/matplotlib/style/stylelib/grayscale.mplstyle From c3fae2e27698d3b6d7dda5f312a021010fc811dd Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Tue, 23 Jul 2013 00:21:30 -0500 Subject: [PATCH 08/35] Got a little crazy with the whitespace --- lib/matplotlib/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 9bb6b03d51e7..ddb2398db699 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -973,8 +973,6 @@ def rc_params_in_file(fname, fail_on_error=False): return config - - def rc_params_from_file(fname, fail_on_error=False): """Return :class:`matplotlib.RcParams` from the contents of the given file. From a3de2313b7a01aa5fbe667f4017e55f6e10ac594 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Wed, 24 Jul 2013 22:23:43 -0500 Subject: [PATCH 09/35] Add style package and data to setupext.py --- setupext.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setupext.py b/setupext.py index fba77d28244a..1aedcc0c721b 100644 --- a/setupext.py +++ b/setupext.py @@ -549,6 +549,7 @@ def get_packages(self): 'matplotlib.projections', 'matplotlib.axes', 'matplotlib.sphinxext', + 'matplotlib.style', 'matplotlib.testing', 'matplotlib.testing.jpl_units', 'matplotlib.tri', @@ -581,7 +582,8 @@ def get_package_data(self): 'backends/web_backend/jquery/css/themes/base/*.*', 'backends/web_backend/jquery/css/themes/base/images/*', 'backends/web_backend/css/*.*', - 'backends/Matplotlib.nib/*' + 'backends/Matplotlib.nib/*', + 'style/stylelib/*.mplstyle', ]} From d56f73e64385b226e70ab85bf68c839640346072 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Wed, 18 Sep 2013 22:47:10 -0500 Subject: [PATCH 10/35] Move test so that it actually runs. --- lib/matplotlib/__init__.py | 1 + lib/matplotlib/{style/tests/test_core.py => tests/test_style.py} | 0 2 files changed, 1 insertion(+) rename lib/matplotlib/{style/tests/test_core.py => tests/test_style.py} (100%) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index ddb2398db699..ced3871a5208 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1310,6 +1310,7 @@ def tk_window_focus(): 'matplotlib.tests.test_simplification', 'matplotlib.tests.test_spines', 'matplotlib.tests.test_streamplot', + 'matplotlib.tests.test_style', 'matplotlib.tests.test_subplots', 'matplotlib.tests.test_table', 'matplotlib.tests.test_text', diff --git a/lib/matplotlib/style/tests/test_core.py b/lib/matplotlib/tests/test_style.py similarity index 100% rename from lib/matplotlib/style/tests/test_core.py rename to lib/matplotlib/tests/test_style.py From ec6ce6bbeca84530c23744a239d0a6ef1f88a8cb Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Wed, 18 Sep 2013 22:52:40 -0500 Subject: [PATCH 11/35] Use explicit string check --- lib/matplotlib/style/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 24610eb9be74..3df6ede90321 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -14,8 +14,8 @@ import re import contextlib -import numpy as np import matplotlib as mpl +from matplotlib import cbook __all__ = ['use', 'context', 'available', 'library', 'reload_library'] @@ -44,7 +44,7 @@ def use(name): style names, see `style.available`. If given a list, each style is applied from first to last in the list. """ - if np.isscalar(name): + if cbook.is_string_like(name): name = [name] for style in name: From 5fdc037c06715ee75ab2c489936896632d3823f0 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Wed, 18 Sep 2013 23:15:12 -0500 Subject: [PATCH 12/35] Hide rc_params_in_file from parent namespace --- lib/matplotlib/__init__.py | 17 ++++++++++++----- lib/matplotlib/style/core.py | 5 +++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index ced3871a5208..91286f7bd1f8 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -906,7 +906,7 @@ def _open_file_or_url(fname): _error_details_fmt = 'line #%d\n\t"%s"\n\tin file "%s"' -def rc_params_in_file(fname, fail_on_error=False): +def _rc_params_in_file(fname, fail_on_error=False): """Return :class:`matplotlib.RcParams` from the contents of the given file. Unlike `rc_params_from_file`, the configuration class only contains the @@ -973,7 +973,7 @@ def rc_params_in_file(fname, fail_on_error=False): return config -def rc_params_from_file(fname, fail_on_error=False): +def rc_params_from_file(fname, fail_on_error=False, use_default_template=True): """Return :class:`matplotlib.RcParams` from the contents of the given file. Parameters @@ -982,12 +982,19 @@ def rc_params_from_file(fname, fail_on_error=False): Name of file parsed for matplotlib settings. fail_on_error : bool If True, raise an error when the parser fails to convert a parameter. + use_default_template : bool + If True, initialize with default parameters before updating with those + in the given file. If False, the configuration class only contains the + parameters specified in the file. (Useful for updating dicts.) """ + config_from_file = _rc_params_in_file(fname, fail_on_error) - config = RcParams([(key, default) - for key, (default, _) in six.iteritems(defaultParams)]) + if not use_default_template: + return config_from_file - config.update(rc_params_in_file(fname, fail_on_error)) + iter_params = six.iteritems(defaultParams) + config = RcParams([(key, default) for key, (default, _) in iter_params]) + config.update(config_from_file) verbose.set_level(config['verbose.level']) verbose.set_fileo(config['verbose.fileo']) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 3df6ede90321..686900e0ad90 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -16,6 +16,7 @@ import matplotlib as mpl from matplotlib import cbook +from matplotlib import rc_params_from_file __all__ = ['use', 'context', 'available', 'library', 'reload_library'] @@ -49,7 +50,7 @@ def use(name): for style in name: if is_style_file(style): - settings = mpl.rc_params_in_file(style) + settings = rc_params_from_file(style, use_default_template=False) mpl.rcParams.update(settings) elif style not in library: msg = ("'%s' not found in the style library. " @@ -117,7 +118,7 @@ def read_style_directory(style_dir): """Return dictionary of styles defined in `style_dir`.""" styles = dict() for path, name in iter_style_files(style_dir): - styles[name] = mpl.rc_params_in_file(path) + styles[name] = rc_params_from_file(path, use_default_template=False) return styles From 0c7437c122beaeb9c17b86e96b79a88cb85df143 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Wed, 18 Sep 2013 23:19:47 -0500 Subject: [PATCH 13/35] Remove usage of import * --- lib/matplotlib/style/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/style/__init__.py b/lib/matplotlib/style/__init__.py index c98e84e2ac93..9a9eae7d55db 100644 --- a/lib/matplotlib/style/__init__.py +++ b/lib/matplotlib/style/__init__.py @@ -1,2 +1 @@ -from core import * - +from core import use, context, available, library, reload_library From 200d2e0ea8c83ff7afc0bc1d81c580e235876c5c Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Wed, 18 Sep 2013 23:21:44 -0500 Subject: [PATCH 14/35] Clarify docstring --- lib/matplotlib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 91286f7bd1f8..794fc6604d44 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -888,7 +888,7 @@ def rc_params(fail_on_error=False): def is_url(filename): - """Return True if string is an http or ftp path.""" + """Return True if string is an http, ftp, or file URL path.""" return URL_REGEX.match(filename) is not None From 7392ce677b15cc41697ba2f25642e2191642c5be Mon Sep 17 00:00:00 2001 From: Thomas A Caswell <tcaswell@gmail.com> Date: Thu, 26 Sep 2013 22:20:29 -0500 Subject: [PATCH 15/35] added `matplotlib.style` to pyplot import list --- lib/matplotlib/pyplot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 8ff3714ff585..0d0c03ce13f3 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -25,6 +25,7 @@ import matplotlib import matplotlib.colorbar +import matplotlib.style from matplotlib import _pylab_helpers, interactive from matplotlib.cbook import dedent, silent_list, is_string_like, is_numlike from matplotlib.cbook import _string_to_bool From ea63c99b71ab7bcfaf862d75111a2c805c07efde Mon Sep 17 00:00:00 2001 From: Adrian Price-Whelan <adrian.prw@gmail.com> Date: Tue, 17 Sep 2013 01:50:13 -0400 Subject: [PATCH 16/35] fix url rc specification smoothed over conflicts add test for URL stylesheet whoops, remove extraneous regex I added... move test to proper location arg, don't need urllib2 either...didn't realize functionality was in core mpl --- lib/matplotlib/style/core.py | 20 +++++++++++--------- lib/matplotlib/tests/test_style.py | 5 +++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 686900e0ad90..39c4d49d841a 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -29,7 +29,6 @@ STYLE_EXTENSION = 'mplstyle' STYLE_FILE_PATTERN = re.compile('([\S]+).%s$' % STYLE_EXTENSION) - def is_style_file(filename): """Return True if the filename looks like a style file.""" return STYLE_FILE_PATTERN.match(filename) is not None @@ -49,15 +48,18 @@ def use(name): name = [name] for style in name: - if is_style_file(style): - settings = rc_params_from_file(style, use_default_template=False) - mpl.rcParams.update(settings) - elif style not in library: - msg = ("'%s' not found in the style library. " - "See `style.available` for list of available styles.") - raise ValueError(msg % style) - else: + if style in library: mpl.rcParams.update(library[style]) + else: + try: + settings = mpl.rc_params_in_file(style) + mpl.rcParams.update(settings) + except: + msg = ("'%s' not found in the style library and input is " + "not a valid URL. See `style.available` for list of " + "available styles.") + raise ValueError(msg % style) + @contextlib.contextmanager diff --git a/lib/matplotlib/tests/test_style.py b/lib/matplotlib/tests/test_style.py index 9300b22f2fff..233091ea36d0 100644 --- a/lib/matplotlib/tests/test_style.py +++ b/lib/matplotlib/tests/test_style.py @@ -46,6 +46,11 @@ def test_use(): style.use('test') assert mpl.rcParams[PARAM] == VALUE +def test_use_url(): + with temp_style('test', DUMMY_SETTINGS): + style.use('https://gist.github.com/adrn/6590261/raw') + + assert mpl.rcParams['axes.facecolor'] == "adeade" def test_context(): mpl.rcParams[PARAM] = 'gray' From eaa23ee059ad4fc8e5a2d639a1d0ba746b8a8816 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell <tcaswell@gmail.com> Date: Thu, 26 Sep 2013 22:40:36 -0500 Subject: [PATCH 17/35] fixed divergent naming scheme --- lib/matplotlib/style/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 39c4d49d841a..5e589c8c5f75 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -52,7 +52,7 @@ def use(name): mpl.rcParams.update(library[style]) else: try: - settings = mpl.rc_params_in_file(style) + settings = mpl._rc_params_in_file(style) mpl.rcParams.update(settings) except: msg = ("'%s' not found in the style library and input is " From 5f80ca155abdb9cfb2a7b6e5692e5039f7d6269a Mon Sep 17 00:00:00 2001 From: Thomas A Caswell <tcaswell@gmail.com> Date: Thu, 26 Sep 2013 22:53:18 -0500 Subject: [PATCH 18/35] pep8 clean up --- lib/matplotlib/style/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 5e589c8c5f75..870bb34cd704 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -29,6 +29,7 @@ STYLE_EXTENSION = 'mplstyle' STYLE_FILE_PATTERN = re.compile('([\S]+).%s$' % STYLE_EXTENSION) + def is_style_file(filename): """Return True if the filename looks like a style file.""" return STYLE_FILE_PATTERN.match(filename) is not None @@ -61,7 +62,6 @@ def use(name): raise ValueError(msg % style) - @contextlib.contextmanager def context(name, after_reset=False): """Context manager for using style settings temporarily. @@ -147,6 +147,7 @@ def update_nested_dict(main_dict, new_dict): library = None available = [] + def reload_library(): """Reload style library.""" global library, available From f5ecf5e70b996de7d25902ada2b1ed2e9218a965 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sat, 28 Sep 2013 22:18:02 -0500 Subject: [PATCH 19/35] Add docs for style package --- doc/users/beginner.rst | 1 + doc/users/style_sheets.rst | 90 ++++++++++++++++++++++++++++++++++++++ doc/users/whats_new.rst | 17 +++++++ 3 files changed, 108 insertions(+) create mode 100644 doc/users/style_sheets.rst diff --git a/doc/users/beginner.rst b/doc/users/beginner.rst index d1eb6e9deca5..c895415c4ffd 100644 --- a/doc/users/beginner.rst +++ b/doc/users/beginner.rst @@ -13,6 +13,7 @@ Beginner's Guide :maxdepth: 2 pyplot_tutorial.rst + style_sheets.rst navigation_toolbar.rst index_text.rst image_tutorial.rst diff --git a/doc/users/style_sheets.rst b/doc/users/style_sheets.rst new file mode 100644 index 000000000000..92a8d3743d2e --- /dev/null +++ b/doc/users/style_sheets.rst @@ -0,0 +1,90 @@ +.. _style-sheets + +*********************************** +Customizing plots with style sheets +*********************************** + + +The ``style`` package adds support for easy-to-switch plotting "styles" with +the same parameters as a matplotlibrc_ file. + +There are a number of pre-defined styles provided by matplotlib. For +example, there's a pre-defined style called "ggplot", which emulates the +aesthetics of ggplot_ (a popular plotting package for R_). To use this style, +just add:: + + >>> from matplotlib import style + >>> style.use('ggplot') + +To list all available styles, use:: + + >>> print style.available + + +Defining your own style +======================= + +You can create custom styles and use them by calling ``style.use`` with the +path or URL to the style sheet. Alternatively, if you add your +``<style-name>.mplstyle`` file to ``~/.matplotlib/stylelib`` (you may need to +create this directory), you can reuse your custom style sheet with a call to +``style.use(<style-name>)``. Note that a custom style sheet in +``~/.matplotlib/stylelib`` will override a style sheet defined by matplotlib if +the styles have the same name. + +For example, you might want to create +``~/.matplotlib/stylelib/presentation.mplstyle`` with the following:: + + axes.titlesize : 24 + axes.labelsize : 20 + lines.linewidth : 3 + lines.markersize : 10 + xtick.labelsize : 16 + ytick.labelsize : 16 + +Then, when you want to adapt a plot designed for a paper to one that looks +good in a presentation, you can just add:: + + >>> from matplotlib import style + >>> style.use('presentation') + + +Composing styles +================ + +Style sheets are designed to be composed together. So you can have a style +sheet that customizes colors and a separate style sheet that alters element +sizes for presentations. These styles can easily be combined by passing +a list of styles:: + + >>> from matplotlib import style + >>> style.use(['dark_background', 'presentation']) + +Note that styles further to the right will overwrite values that are already +defined by styles on the right. + + +Temporary styling +================= + +If you only want to use a style for a specific block of code but don't want +to change the global styling, the style package provides a context manager +for limiting your changes to a specific scope. To isolate the your styling +changes, you can write something like the following:: + + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from matplotlib import style + >>> + >>> with style.context(('dark_background')): + >>> plt.plot(np.sin(np.linspace(0, 2*np.pi)), 'r-o') + >>> + >>> # Some plotting code with the default style + >>> + >>> plt.show() + + +.. _matplotlibrc: http://matplotlib.sourceforge.net/users/customizing.html +.. _ggplot: http://had.co.nz/ggplot/ +.. _R: http://www.r-project.org/ diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index 5bd6969afecf..d072c3807f1c 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -98,6 +98,23 @@ an offset will be determined such that the tick labels are meaningful. If `False` then the full number will be formatted in all conditions. +``style`` package added +``````````````````````` +You can now easily switch between different styles using the new ``style`` +package:: + + >>> from matplotlib import style + >>> style.use('dark_background') + +Subsequent plots will use updated colors, sizes, etc. To list all available +styles, use:: + + >>> print style.available + +You can add your own custom ``<style name>.mplstyle`` files to +``~/.matplotlib/stylelib`` or call ``use`` with a URL pointing to a file with +``matplotlibrc`` settings. + .. _whats-new-1-3: new in matplotlib-1.3 From a8ef5bfab1614e2db383438be49ec815007f0882 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sat, 28 Sep 2013 23:02:09 -0500 Subject: [PATCH 20/35] Import style package for easy use. --- lib/matplotlib/pyplot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 0d0c03ce13f3..a67fc8580b1e 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -25,7 +25,7 @@ import matplotlib import matplotlib.colorbar -import matplotlib.style +from matplotlib import style from matplotlib import _pylab_helpers, interactive from matplotlib.cbook import dedent, silent_list, is_string_like, is_numlike from matplotlib.cbook import _string_to_bool From dc291e09113181d503554e138184f9d80c897e53 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sat, 28 Sep 2013 23:07:22 -0500 Subject: [PATCH 21/35] Use style package from pyplot --- doc/users/style_sheets.rst | 17 ++++++++--------- examples/style_sheets/plot_dark_background.py | 3 +-- examples/style_sheets/plot_ggplot.py | 3 +-- examples/style_sheets/plot_grayscale.py | 4 +--- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/doc/users/style_sheets.rst b/doc/users/style_sheets.rst index 92a8d3743d2e..563ac542c8ec 100644 --- a/doc/users/style_sheets.rst +++ b/doc/users/style_sheets.rst @@ -13,12 +13,12 @@ example, there's a pre-defined style called "ggplot", which emulates the aesthetics of ggplot_ (a popular plotting package for R_). To use this style, just add:: - >>> from matplotlib import style - >>> style.use('ggplot') + >>> import matplotlib.pyplot as plt + >>> plt.style.use('ggplot') To list all available styles, use:: - >>> print style.available + >>> print plt.style.available Defining your own style @@ -45,8 +45,8 @@ For example, you might want to create Then, when you want to adapt a plot designed for a paper to one that looks good in a presentation, you can just add:: - >>> from matplotlib import style - >>> style.use('presentation') + >>> import matplotlib.pyplot as plt + >>> plt.style.use('presentation') Composing styles @@ -57,8 +57,8 @@ sheet that customizes colors and a separate style sheet that alters element sizes for presentations. These styles can easily be combined by passing a list of styles:: - >>> from matplotlib import style - >>> style.use(['dark_background', 'presentation']) + >>> import matplotlib.pyplot as plt + >>> plt.style.use(['dark_background', 'presentation']) Note that styles further to the right will overwrite values that are already defined by styles on the right. @@ -75,9 +75,8 @@ changes, you can write something like the following:: >>> import numpy as np >>> import matplotlib.pyplot as plt - >>> from matplotlib import style >>> - >>> with style.context(('dark_background')): + >>> with plt.style.context(('dark_background')): >>> plt.plot(np.sin(np.linspace(0, 2*np.pi)), 'r-o') >>> >>> # Some plotting code with the default style diff --git a/examples/style_sheets/plot_dark_background.py b/examples/style_sheets/plot_dark_background.py index acb94d4eafa9..c2f76b95e6f5 100644 --- a/examples/style_sheets/plot_dark_background.py +++ b/examples/style_sheets/plot_dark_background.py @@ -7,9 +7,8 @@ import numpy as np import matplotlib.pyplot as plt -from matplotlib import style -style.use('dark_background') +plt.style.use('dark_background') L = 6 x = np.linspace(0, L) diff --git a/examples/style_sheets/plot_ggplot.py b/examples/style_sheets/plot_ggplot.py index 726ecb9ce462..0542ea35b2bf 100644 --- a/examples/style_sheets/plot_ggplot.py +++ b/examples/style_sheets/plot_ggplot.py @@ -12,9 +12,8 @@ """ import numpy as np import matplotlib.pyplot as plt -from matplotlib import style -style.use('ggplot') +plt.style.use('ggplot') fig, axes = plt.subplots(ncols=2, nrows=2) ax1, ax2, ax3, ax4 = axes.ravel() diff --git a/examples/style_sheets/plot_grayscale.py b/examples/style_sheets/plot_grayscale.py index a6d04dc48970..e233960099d3 100644 --- a/examples/style_sheets/plot_grayscale.py +++ b/examples/style_sheets/plot_grayscale.py @@ -7,8 +7,6 @@ import numpy as np import matplotlib.pyplot as plt -from matplotlib import style - def color_cycle_example(ax): L = 6 @@ -24,7 +22,7 @@ def image_and_patch_example(ax): ax.add_patch(c) -style.use('grayscale') +plt.style.use('grayscale') fig, (ax1, ax2) = plt.subplots(ncols=2) From c5b5bb49d489533b84f873234bbcd6384f98e6f0 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sat, 28 Sep 2013 23:17:53 -0500 Subject: [PATCH 22/35] Fix test --- lib/matplotlib/style/core.py | 10 ++++++---- lib/matplotlib/tests/test_style.py | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 870bb34cd704..4d412d82ce2f 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -53,12 +53,14 @@ def use(name): mpl.rcParams.update(library[style]) else: try: - settings = mpl._rc_params_in_file(style) - mpl.rcParams.update(settings) + rc = rc_params_from_file(style, use_default_template=False) + print rc + print type(rc) + mpl.rcParams.update(rc) except: msg = ("'%s' not found in the style library and input is " - "not a valid URL. See `style.available` for list of " - "available styles.") + "not a valid URL or path. See `style.available` for " + "list of available styles.") raise ValueError(msg % style) diff --git a/lib/matplotlib/tests/test_style.py b/lib/matplotlib/tests/test_style.py index 233091ea36d0..24031b0f2ab3 100644 --- a/lib/matplotlib/tests/test_style.py +++ b/lib/matplotlib/tests/test_style.py @@ -50,7 +50,7 @@ def test_use_url(): with temp_style('test', DUMMY_SETTINGS): style.use('https://gist.github.com/adrn/6590261/raw') - assert mpl.rcParams['axes.facecolor'] == "adeade" + assert mpl.rcParams['axes.facecolor'] == "#adeade" def test_context(): mpl.rcParams[PARAM] = 'gray' From 7ac26ee0b5ce5c7ccbad4e53061bc99849ff23bd Mon Sep 17 00:00:00 2001 From: Thomas A Caswell <tcaswell@gmail.com> Date: Fri, 18 Oct 2013 11:32:48 -0500 Subject: [PATCH 23/35] pep8 --- lib/matplotlib/tests/test_style.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/matplotlib/tests/test_style.py b/lib/matplotlib/tests/test_style.py index 24031b0f2ab3..9a7ee9ac5f8e 100644 --- a/lib/matplotlib/tests/test_style.py +++ b/lib/matplotlib/tests/test_style.py @@ -46,12 +46,14 @@ def test_use(): style.use('test') assert mpl.rcParams[PARAM] == VALUE + def test_use_url(): with temp_style('test', DUMMY_SETTINGS): style.use('https://gist.github.com/adrn/6590261/raw') assert mpl.rcParams['axes.facecolor'] == "#adeade" + def test_context(): mpl.rcParams[PARAM] = 'gray' with temp_style('test', DUMMY_SETTINGS): From 46a725b984c0649d335a0f6019392b29d2a065cf Mon Sep 17 00:00:00 2001 From: Michael Droettboom <mdboom@gmail.com> Date: Mon, 30 Sep 2013 11:53:26 -0400 Subject: [PATCH 24/35] Clear style settings between tests Conflicts: lib/matplotlib/tests/test_style.py Conflicts: lib/matplotlib/tests/test_style.py --- lib/matplotlib/tests/test_style.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/tests/test_style.py b/lib/matplotlib/tests/test_style.py index 9a7ee9ac5f8e..75c59ed20953 100644 --- a/lib/matplotlib/tests/test_style.py +++ b/lib/matplotlib/tests/test_style.py @@ -33,6 +33,7 @@ def temp_style(style_name, settings=None): yield finally: shutil.rmtree(tempdir) + style.reload_library() def test_available(): @@ -43,15 +44,14 @@ def test_available(): def test_use(): mpl.rcParams[PARAM] = 'gray' with temp_style('test', DUMMY_SETTINGS): - style.use('test') - assert mpl.rcParams[PARAM] == VALUE + with style.context('test'): + assert mpl.rcParams[PARAM] == VALUE def test_use_url(): with temp_style('test', DUMMY_SETTINGS): - style.use('https://gist.github.com/adrn/6590261/raw') - - assert mpl.rcParams['axes.facecolor'] == "#adeade" + with style.context('https://gist.github.com/adrn/6590261/raw'): + assert mpl.rcParams['axes.facecolor'] == "#adeade" def test_context(): From d372a3545659e75753019ee9526c9e55f9e03c04 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Fri, 18 Oct 2013 22:47:44 -0500 Subject: [PATCH 25/35] Add note that style sheets are experimental. --- doc/users/whats_new.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index d072c3807f1c..886e60c16f0e 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -115,6 +115,9 @@ You can add your own custom ``<style name>.mplstyle`` files to ``~/.matplotlib/stylelib`` or call ``use`` with a URL pointing to a file with ``matplotlibrc`` settings. +*Note that this is an experimental feature*, and the interface may change as +users test out this new feature. + .. _whats-new-1-3: new in matplotlib-1.3 From e714c778e8dac04fec7debd63164ab7bbf24cfe8 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell <tcaswell@gmail.com> Date: Sat, 26 Oct 2013 23:18:48 -0500 Subject: [PATCH 26/35] added python3 emulation code + six + fixed up print calls --- lib/matplotlib/style/core.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 4d412d82ce2f..1db7e519b292 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -1,3 +1,9 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +import six +from six.moves import reduce, xrange, zip + """ Core functions and attributes for the matplotlib style library: @@ -54,8 +60,8 @@ def use(name): else: try: rc = rc_params_from_file(style, use_default_template=False) - print rc - print type(rc) + print(rc) + print(type(rc)) mpl.rcParams.update(rc) except: msg = ("'%s' not found in the style library and input is " From 512b77c011bf281b56632ddaa4064e841b38833d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell <tcaswell@gmail.com> Date: Thu, 31 Oct 2013 08:48:45 -0500 Subject: [PATCH 27/35] removed unneeded print statements removed some of the unneeded imports --- lib/matplotlib/style/core.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index 1db7e519b292..d2dca91e1d11 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -2,7 +2,6 @@ unicode_literals) import six -from six.moves import reduce, xrange, zip """ Core functions and attributes for the matplotlib style library: @@ -60,8 +59,6 @@ def use(name): else: try: rc = rc_params_from_file(style, use_default_template=False) - print(rc) - print(type(rc)) mpl.rcParams.update(rc) except: msg = ("'%s' not found in the style library and input is " From 17282c88937bf0be1feb49640657cfa1189e0dc0 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Tue, 12 Nov 2013 21:08:00 -0600 Subject: [PATCH 28/35] Attempt to fix python 3 test errors on Travis CI --- lib/matplotlib/tests/test_style.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/matplotlib/tests/test_style.py b/lib/matplotlib/tests/test_style.py index 75c59ed20953..224c5960df60 100644 --- a/lib/matplotlib/tests/test_style.py +++ b/lib/matplotlib/tests/test_style.py @@ -61,8 +61,3 @@ def test_context(): assert mpl.rcParams[PARAM] == VALUE # Check that this value is reset after the exiting the context. assert mpl.rcParams[PARAM] == 'gray' - - -if __name__ == '__main__': - from numpy import testing - testing.run_module_suite() From a6142fc19beb074de34a2f1f1f2ee10830c0a04b Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Wed, 13 Nov 2013 21:48:07 -0600 Subject: [PATCH 29/35] Remove test from list to test Travis CI failure. --- lib/matplotlib/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 794fc6604d44..4a5ea5f755d8 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1317,7 +1317,6 @@ def tk_window_focus(): 'matplotlib.tests.test_simplification', 'matplotlib.tests.test_spines', 'matplotlib.tests.test_streamplot', - 'matplotlib.tests.test_style', 'matplotlib.tests.test_subplots', 'matplotlib.tests.test_table', 'matplotlib.tests.test_text', From 19e7bed4dcc7fec7af18e05aff6209ed4ae52747 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Wed, 13 Nov 2013 21:58:29 -0600 Subject: [PATCH 30/35] Remove test file to test Travis CI failure --- lib/matplotlib/tests/test_style.py | 63 ------------------------------ 1 file changed, 63 deletions(-) delete mode 100644 lib/matplotlib/tests/test_style.py diff --git a/lib/matplotlib/tests/test_style.py b/lib/matplotlib/tests/test_style.py deleted file mode 100644 index 224c5960df60..000000000000 --- a/lib/matplotlib/tests/test_style.py +++ /dev/null @@ -1,63 +0,0 @@ -import os -import shutil -import tempfile -from contextlib import contextmanager - -import matplotlib as mpl -from matplotlib import style -from matplotlib.style.core import USER_LIBRARY_PATHS, STYLE_EXTENSION - - -PARAM = 'image.cmap' -VALUE = 'pink' -DUMMY_SETTINGS = {PARAM: VALUE} - - -@contextmanager -def temp_style(style_name, settings=None): - """Context manager to create a style sheet in a temporary directory.""" - settings = DUMMY_SETTINGS - temp_file = '%s.%s' % (style_name, STYLE_EXTENSION) - - # Write style settings to file in the temp directory. - tempdir = tempfile.mkdtemp() - with open(os.path.join(tempdir, temp_file), 'w') as f: - for k, v in settings.iteritems(): - f.write('%s: %s' % (k, v)) - - # Add temp directory to style path and reload so we can access this style. - USER_LIBRARY_PATHS.append(tempdir) - style.reload_library() - - try: - yield - finally: - shutil.rmtree(tempdir) - style.reload_library() - - -def test_available(): - with temp_style('_test_', DUMMY_SETTINGS): - assert '_test_' in style.available - - -def test_use(): - mpl.rcParams[PARAM] = 'gray' - with temp_style('test', DUMMY_SETTINGS): - with style.context('test'): - assert mpl.rcParams[PARAM] == VALUE - - -def test_use_url(): - with temp_style('test', DUMMY_SETTINGS): - with style.context('https://gist.github.com/adrn/6590261/raw'): - assert mpl.rcParams['axes.facecolor'] == "#adeade" - - -def test_context(): - mpl.rcParams[PARAM] = 'gray' - with temp_style('test', DUMMY_SETTINGS): - with style.context('test'): - assert mpl.rcParams[PARAM] == VALUE - # Check that this value is reset after the exiting the context. - assert mpl.rcParams[PARAM] == 'gray' From c60449881b6a46d7f2e1aceb47e8c5f1c1928206 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Wed, 13 Nov 2013 22:15:40 -0600 Subject: [PATCH 31/35] Revert commits used to test Travis CI test failures. End result of the experiment: The failure isn't related to the tests added by this PR. --- lib/matplotlib/__init__.py | 1 + lib/matplotlib/tests/test_style.py | 68 ++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 lib/matplotlib/tests/test_style.py diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 4a5ea5f755d8..794fc6604d44 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1317,6 +1317,7 @@ def tk_window_focus(): 'matplotlib.tests.test_simplification', 'matplotlib.tests.test_spines', 'matplotlib.tests.test_streamplot', + 'matplotlib.tests.test_style', 'matplotlib.tests.test_subplots', 'matplotlib.tests.test_table', 'matplotlib.tests.test_text', diff --git a/lib/matplotlib/tests/test_style.py b/lib/matplotlib/tests/test_style.py new file mode 100644 index 000000000000..75c59ed20953 --- /dev/null +++ b/lib/matplotlib/tests/test_style.py @@ -0,0 +1,68 @@ +import os +import shutil +import tempfile +from contextlib import contextmanager + +import matplotlib as mpl +from matplotlib import style +from matplotlib.style.core import USER_LIBRARY_PATHS, STYLE_EXTENSION + + +PARAM = 'image.cmap' +VALUE = 'pink' +DUMMY_SETTINGS = {PARAM: VALUE} + + +@contextmanager +def temp_style(style_name, settings=None): + """Context manager to create a style sheet in a temporary directory.""" + settings = DUMMY_SETTINGS + temp_file = '%s.%s' % (style_name, STYLE_EXTENSION) + + # Write style settings to file in the temp directory. + tempdir = tempfile.mkdtemp() + with open(os.path.join(tempdir, temp_file), 'w') as f: + for k, v in settings.iteritems(): + f.write('%s: %s' % (k, v)) + + # Add temp directory to style path and reload so we can access this style. + USER_LIBRARY_PATHS.append(tempdir) + style.reload_library() + + try: + yield + finally: + shutil.rmtree(tempdir) + style.reload_library() + + +def test_available(): + with temp_style('_test_', DUMMY_SETTINGS): + assert '_test_' in style.available + + +def test_use(): + mpl.rcParams[PARAM] = 'gray' + with temp_style('test', DUMMY_SETTINGS): + with style.context('test'): + assert mpl.rcParams[PARAM] == VALUE + + +def test_use_url(): + with temp_style('test', DUMMY_SETTINGS): + with style.context('https://gist.github.com/adrn/6590261/raw'): + assert mpl.rcParams['axes.facecolor'] == "#adeade" + + +def test_context(): + mpl.rcParams[PARAM] = 'gray' + with temp_style('test', DUMMY_SETTINGS): + with style.context('test'): + assert mpl.rcParams[PARAM] == VALUE + # Check that this value is reset after the exiting the context. + assert mpl.rcParams[PARAM] == 'gray' + + +if __name__ == '__main__': + from numpy import testing + testing.run_module_suite() From 0b098e2aa939f00482602ca2ae4b20eb2b5cfba3 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sun, 17 Nov 2013 11:14:00 -0600 Subject: [PATCH 32/35] Fix import for python 3 --- lib/matplotlib/style/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/style/__init__.py b/lib/matplotlib/style/__init__.py index 9a9eae7d55db..cb0592f41e78 100644 --- a/lib/matplotlib/style/__init__.py +++ b/lib/matplotlib/style/__init__.py @@ -1 +1,3 @@ -from core import use, context, available, library, reload_library +from __future__ import absolute_import + +from .core import use, context, available, library, reload_library From 7e2bffb3b24e6c07ceb2d5a22be8c5b6ff155120 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sun, 17 Nov 2013 11:56:40 -0600 Subject: [PATCH 33/35] Use iteritems from `six` module --- lib/matplotlib/style/core.py | 2 +- lib/matplotlib/tests/test_style.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/style/core.py b/lib/matplotlib/style/core.py index d2dca91e1d11..845d5472b294 100644 --- a/lib/matplotlib/style/core.py +++ b/lib/matplotlib/style/core.py @@ -137,7 +137,7 @@ def update_nested_dict(main_dict, new_dict): already exists. Instead you should update the sub-dict. """ # update named styles specified by user - for name, rc_dict in new_dict.iteritems(): + for name, rc_dict in six.iteritems(new_dict): if name in main_dict: main_dict[name].update(rc_dict) else: diff --git a/lib/matplotlib/tests/test_style.py b/lib/matplotlib/tests/test_style.py index 75c59ed20953..d8e71f5cc4d2 100644 --- a/lib/matplotlib/tests/test_style.py +++ b/lib/matplotlib/tests/test_style.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) + import os import shutil import tempfile @@ -7,6 +10,8 @@ from matplotlib import style from matplotlib.style.core import USER_LIBRARY_PATHS, STYLE_EXTENSION +import six + PARAM = 'image.cmap' VALUE = 'pink' @@ -22,7 +27,7 @@ def temp_style(style_name, settings=None): # Write style settings to file in the temp directory. tempdir = tempfile.mkdtemp() with open(os.path.join(tempdir, temp_file), 'w') as f: - for k, v in settings.iteritems(): + for k, v in six.iteritems(settings): f.write('%s: %s' % (k, v)) # Add temp directory to style path and reload so we can access this style. From 1d87f347ddb89830dcd51884b746fa4d8f8dfb38 Mon Sep 17 00:00:00 2001 From: Tony S Yu <tsyu80@gmail.com> Date: Sun, 17 Nov 2013 13:59:03 -0600 Subject: [PATCH 34/35] Add compatibility layer for Python 3's urlopen --- lib/matplotlib/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 794fc6604d44..9986bb0a37a8 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -892,11 +892,17 @@ def is_url(filename): return URL_REGEX.match(filename) is not None +def _url_lines(f): + # Compatibility for urlopen in python 3, which yields bytes. + for line in f: + yield line.decode(encoding='utf8') + + @contextlib.contextmanager def _open_file_or_url(fname): if is_url(fname): f = urlopen(fname) - yield f + yield _url_lines(f) f.close() else: with open(fname) as f: From 79f83c996ff611040796605ba76b3fcd109c9914 Mon Sep 17 00:00:00 2001 From: Michael Droettboom <mdboom@gmail.com> Date: Mon, 18 Nov 2013 07:28:24 -0500 Subject: [PATCH 35/35] Fix _fix_url on Python 2.6 --- lib/matplotlib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 9986bb0a37a8..b61f183b767b 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -895,7 +895,7 @@ def is_url(filename): def _url_lines(f): # Compatibility for urlopen in python 3, which yields bytes. for line in f: - yield line.decode(encoding='utf8') + yield line.decode('utf8') @contextlib.contextmanager