Skip to content

Add easy style sheet selection #2236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
Nov 18, 2013
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
643c74b
Add easy style sheet selection.
tonysyu Jul 21, 2013
3270aa4
Add rc_params_in_file to return partially-filled RcParams
tonysyu Jul 21, 2013
d83a03c
Rename style files to `*.style`
tonysyu Jul 21, 2013
c8cc486
Allow style.use to open URLs
tonysyu Jul 21, 2013
455b54c
Remove pyplot import
tonysyu Jul 21, 2013
7769b29
Add style context manager and tests
tonysyu Jul 23, 2013
3914089
Change style extension to *.mplstyle
tonysyu Jul 23, 2013
c3fae2e
Got a little crazy with the whitespace
tonysyu Jul 23, 2013
a3de231
Add style package and data to setupext.py
tonysyu Jul 25, 2013
d56f73e
Move test so that it actually runs.
tonysyu Sep 19, 2013
ec6ce6b
Use explicit string check
tonysyu Sep 19, 2013
5fdc037
Hide rc_params_in_file from parent namespace
tonysyu Sep 19, 2013
0c7437c
Remove usage of import *
tonysyu Sep 19, 2013
200d2e0
Clarify docstring
tonysyu Sep 19, 2013
7392ce6
added `matplotlib.style` to pyplot import list
tacaswell Sep 27, 2013
ea63c99
fix url rc specification
adrn Sep 17, 2013
eaa23ee
fixed divergent naming scheme
tacaswell Sep 27, 2013
5f80ca1
pep8 clean up
tacaswell Sep 27, 2013
f5ecf5e
Add docs for style package
tonysyu Sep 29, 2013
a8ef5bf
Import style package for easy use.
tonysyu Sep 29, 2013
dc291e0
Use style package from pyplot
tonysyu Sep 29, 2013
c5b5bb4
Fix test
tonysyu Sep 29, 2013
7ac26ee
pep8
tacaswell Oct 18, 2013
46a725b
Clear style settings between tests
mdboom Sep 30, 2013
d372a35
Add note that style sheets are experimental.
tonysyu Oct 19, 2013
e714c77
added python3 emulation code + six + fixed up print calls
tacaswell Oct 27, 2013
512b77c
removed unneeded print statements
tacaswell Oct 31, 2013
17282c8
Attempt to fix python 3 test errors on Travis CI
tonysyu Nov 13, 2013
a6142fc
Remove test from list to test Travis CI failure.
tonysyu Nov 14, 2013
19e7bed
Remove test file to test Travis CI failure
tonysyu Nov 14, 2013
c604498
Revert commits used to test Travis CI test failures.
tonysyu Nov 14, 2013
0b098e2
Fix import for python 3
tonysyu Nov 17, 2013
7e2bffb
Use iteritems from `six` module
tonysyu Nov 17, 2013
1d87f34
Add compatibility layer for Python 3's urlopen
tonysyu Nov 17, 2013
79f83c9
Fix _fix_url on Python 2.6
mdboom Nov 18, 2013
246c348
Merge pull request #6 from mdboom/style/py26-fixes
tonysyu Nov 18, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add easy style sheet selection.
  • Loading branch information
tonysyu committed Nov 17, 2013
commit 643c74b96459bb26bd1d366241197a646bafb72b
4 changes: 3 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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'),
Expand Down
24 changes: 24 additions & 0 deletions examples/style_sheets/plot_dark_background.py
Original file line number Diff line number Diff line change
@@ -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()
51 changes: 51 additions & 0 deletions examples/style_sheets/plot_ggplot.py
Original file line number Diff line number Diff line change
@@ -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()
34 changes: 34 additions & 0 deletions examples/style_sheets/plot_grayscale.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 2 additions & 0 deletions lib/matplotlib/style/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from core import *
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason that this file doesn't just contain all the contents of core, instead of wildcard importing them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a matter of style, I guess. I prefer __init__.py modules to be fairly lightweight, but if that's frowned upon, I'm fine with changing it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, aren't we doing the dot-style imports now? I also still don't like the import *.


106 changes: 106 additions & 0 deletions lib/matplotlib/style/core.py
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope you don't really need pyplot for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Fixed.

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):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have cbook.is_scalar()

name = [name]
for s in name:
plt.rcParams.update(library[s])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would benefit from a more helpful error message if s not in library



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()
23 changes: 23 additions & 0 deletions lib/matplotlib/style/stylelib/dark_background.mplrc
Original file line number Diff line number Diff line change
@@ -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

39 changes: 39 additions & 0 deletions lib/matplotlib/style/stylelib/ggplot.mplrc
Original file line number Diff line number Diff line change
@@ -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

29 changes: 29 additions & 0 deletions lib/matplotlib/style/stylelib/grayscale.mplrc
Original file line number Diff line number Diff line change
@@ -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