Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add easy style sheet selection #2236

Merged
merged 36 commits into from

11 participants

Tony S Yu David P. Sanders Chris Beaumont Eric Firing Michael Droettboom Paul Ivanov Benjamin Root Thomas A Caswell Till Stensitzki Adrian Price-Whelan Phil Elson
Tony S Yu

Add easy switching between rcParams based on the implementation in mpltools.style. Basically, just call

from matplotlib import style
style.use('ggplot')

... to switch to a style sheet that sort of mimics ggplot's style.

Notes:

  • In the current implementation, shipped style files are stored in matplotlib/style/stylelib and user files are stored in ~/.matplotlib/stylelib.
  • I chose *.mplrc as an extension for the style files, but I'm fine with changing that. Style files in the style libraries have the extension *.style *.mplstyle.
  • Ideally there would an rc parameter (or some other mechanism) to easily add search paths for style files
  • One thing I liked in the original implementation was the ability to chain style sheets, with each style sheet adding the parameters they set. The current implementation doesn't work like this because rc_params_from_file initializes the default rcParams and then updates the defaults from the file. Thus, updating using the result from rc_params_from_file will overwrite all values.
David P. Sanders

Fantastic news! - many thanks for this. :100:
Apologies for not pulling this in myself -- I am still getting to grips with the code.
(But glad that my "gentle" prodding worked ;) )

I suggest .style as the extension for the style files.

David P. Sanders

I merged your branch and installed matplotlib, but there is no style submodule available to import from matplotlib.

Tony S Yu

@dpsanders Most likely I should have added the style package to some list for installation. One of the matplotlib devs will have to enlighten me on where this would be.

BTW, it sounds like you might want to tweak your git workflow. You don't need to merge my branch to test it out (unless by merge, you meant pull). Just checkout the branch and test on there (never merge into master---only pull from the "official" master). This might be helpful:

https://gist.github.com/piscisaureus/3342247

Also, if you run python setup.py develop, then you can avoid re-installing (unless there are compiled extensions) when testing out different branches.

David P. Sanders
Chris Beaumont

+1 on this

A few outsider thoughts

  1. It would be easy to extend style.use to accept paths or urls, in addition to style names. Urls would be especially nice, since people could peruse nbviewer/github for good styles, and try them out without remembering the search path for style.use.
  2. Since rcParams is essentially a dict, It would be easy to accept json style sheets (e.g. https://raw.github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/master/styles/bmh_matplotlibrc.json)
  3. Is it already possible to configure MPL to use a style by default? If not, perhaps there should be an rcParams option like 'style.default', to specify this?
Chris Beaumont

One other thought: if I'm parsing this correctly, it looks like styles update the current rcParams settings, but don't touch options that aren't specifically in the style sheet. I guess this enables the "chaining" behavior you talk about, but it also means that the state of rcParams after calling style.use is not always obvious. For example:

use('ggplot')  # image.cmap not modified, = 'jet'
use('greyscale') # image.cmap = 'gray'
use('ggplot') # image.cmap='gray'

It might be nice to have an option to explicitly set unspecified style options to a default (the obvious choice being rcParamsDefault or rcParamsOrig).

lib/matplotlib/style/core.py
((26 lines not shown))
+STYLE_FILE_PATTERN = re.compile('([A-Za-z._-]+).style$')
+
+
+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])

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Tony S Yu

@ChrisBeaumont Thanks for the feedback!

It would be easy to extend style.use to accept paths or urls, in addition to style names.

That's a great idea. Done!

Since rcParams is essentially a dict, It would be easy to accept json style sheets

I'm going to punt on this idea. It'd be simple to use the json file to update the settings, but ideally, it would be run through the same validation mechanism as normal rc files. Unfortunately, that validation mechanism is a bit tangled with the parser at the moment. I don't think it'd be hard to fix, but I'm not jumping at the opportunity to fix it myself ;)

Is it already possible to configure MPL to use a style by default?

Yeah, I would stick to the standard ~/.matplotlib/matplotlibrc file for this type of thing.

styles update the current rcParams settings, but don't touch options that aren't specifically in the style sheet. I guess this enables the "chaining" behavior you talk about, but it also means that the state of rcParams after calling style.use is not always obvious.

True. You can just call plt.rcdefaults() to do what you're suggesting. I'm thinking about adding style.reset() as an alias, but matplotlib already has too many names for the same thing so I'm hesitant.

Chris Beaumont

Good points.

My main reason for the suggestion about an rcParams-specified default style is to pave the way to change the defaults in MPL -- if users could add a style.default : classic line to their rcParams, then they could maintain a stable set of rcParams even if the defaults change. From discussions on the mailing list, such functionality sounds like a prerequisite for any proposed change to the defaults.

lib/matplotlib/style/core.py
@@ -0,0 +1,119 @@
+"""
+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
Eric Firing Owner
efiring added a note

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

Tony S Yu
tonysyu added a note

Good point. Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Eric Firing
Owner
Michael Droettboom
Owner

Thanks, Tony. This looks quite good. I'm going to second @ChrisBeaumont's suggestion that an rcParam to set the style would be good, to make the transition easier, as suggested. It's a tricky bootstrapping problem, however. If it's applied when it is read in, because then subsequent values in the matplotlibrc file would overwrite the style. Maybe that's correct, but it has potential for surprise and introduces some order-dependence on the rc files that we don't currently have. And then there's what to do with cyclical references to styles. All resolvable problems -- just tricky to get right I suspect.

I don't like style as an extension. It's too generic, and these files are very matplotlib-specific. I'd prefer mplstyle (or something better than that).

I agree that we want two ways of applying styles: one for chaining styles over top of the existing settings, and one that resets and then applies the style. Not sure on the best way to present that.

@efiring: One of the complications with rcParams that has long irked me is that it combines style things with platform configuration. I've often felt that style belongs much closer to the plot -- not really in user-global configuration as we do now -- whereas the platform configuration stuff (which backend to use, and font formats to write out) do belong where they are. I'd love to see this separate out into two separate files (which would also solve the cyclical reference problem above, since the platform config would choose a style, but a style file could not choose another style (perhaps only inherit from another style). I don't think that would be hard to implement, but devising the right way to transition people is the hard part.

Chris Beaumont

@mdboom agreed, the bootstrapping issue seems a little awkward, with three layers to apply (default values, matplotlibrc values, default style). For compatibility, I think this order is needed to start

  1. rcParams populated with rcsetup.defualtParams
  2. matplotlibrc values applied
  3. default style applied in "implicit mode" (i.e. values not explicitly named are not overridden)

For now, the default style is an empty called 'classic'. If the default style changes, people have the option of switching back to 'classic' in matplotlibrc.

Long term, I agree it would be cleaner to pull the visual-level options out of defaultParams, and then load the default style in "explicit mode" (which applies the visual options currently in defaultParams). Likewise, matplotlibrc would not have visual-level options, so styles and matplotlibrc never conflict. The transition period would be awkward, however (you'd have to warn users that matplotlibrc values should not have visual level options, and then load them anyways after the default style is applied in explicit mode).

lib/matplotlib/style/__init__.py
@@ -0,0 +1,2 @@
+from core import *
Paul Ivanov Collaborator
ivanov added a note

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

Tony S Yu
tonysyu added a note

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.

Benjamin Root Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Paul Ivanov
Collaborator

I'm just jumping in here because it was mentioned on twitter, but will it be possible to either make a context manager for this, or re-use the with mpl.rc_context(...) manager for this?

Michael Droettboom
Owner

@ivanov: Good idea on the context manager.

Also, as a side note -- we should be able to wrap the xkcd command in this, and it wouldn't be a special one-off function any more.

Tony S Yu

@ChrisBeaumont, @mdboom I'm convinced: It would be good to make style sheets an rcParam, but in the current state it'll be difficult to validate because of circular imports.

I'm guessing that I need to add the stylelib directory to some sort of package data field in setup.py, but it's not clear to me where.

Updates:

  • I added a context manager as suggested by @ivanov
  • I switched the extension to *.mplstyle.
  • Tests!
Benjamin Root
Collaborator
Michael Droettboom
Owner

@WeatherGod: The tricky thing about my suggestion -- of separating style from other parameters -- is how best to transition to it, as @ChrisBeaumont pointed out:

The transition period would be awkward, however (you'd have to warn users that matplotlibrc values should not have visual level options, and then load them anyways after the default style is applied in explicit mode).

But maybe we should bite the bullet and do it, despite its akwardness.

Tony S Yu

I agree that it would be good to separate style parameters from config parameters. Just to be clear though, this would be work for a separate PR, correct? (As part of that, removing the bulk of the rc logic from __init__.py and the mixing of data and logic in rcsetup.py would be very desirable---but maybe quite a bit of work.)

Benjamin Root
Collaborator
Tony S Yu

I'd agree with holding off on a "style" parameter. In any case, a lot of rc-refactoring will need to be done before that.

Chris Beaumont

I understand the concern, but it's too bad -- once a style with better defaults matures, it will be a shame to have to write 'style.use('better')' at the top of every script.

I'd definitely be willing to help and/or spearhead the proposed rcParams refactoring, to keep momentum going on this -- though I may not be the most qualified person to tackle a big API change like this.

Tony S Yu

I understand the concern, but it's too bad -- once a style with better defaults matures, it will be a shame to have to write 'style.use('better')' at the top of every script

Sorry, I don't mean to suggest that we should punt on this indefinitely. I just think that smaller, more focused PRs are easier for everyone involved. (Plus, I don't think I want to lead the refactor :)

I'd definitely be willing to help and/or spearhead the proposed rcParams refactoring, to keep momentum going on this -- though I may not be the most qualified person to tackle a big API change like this.

I think this could be split into two PRs: One just to refactor rcsetup.py and the rc parts of __init__.py into something a bit cleaner but with no API changes, and then the second part would be separating styles from config params. That's how I would do it at least.

Thomas A Caswell
Owner

I understand the concern, but it's too bad -- once a style with better defaults matures, it will be a shame to have to write 'style.use('better')' at the top of every script.

This is far preferable than having to go back and touch every existing script that is broken by the 'better' defaults.

I am in favor of treating anything more than near trivial tweaks to defaults as a major api break (like bump to 2.0). If a change requires re-generating a test image, you are breaking the api, and I suspect that this will require a majority of the test images to be re-generated.

That said, the ability easily save and set the styling is first order good, in part as protection against the defaults changing.

[edit: gah I know grammer, I swear]

David P. Sanders

As far as I can see, the style module is still unusable from matplotlib master.
Does anybody have a fix to register it -- @mdboom ?

Chris Beaumont

@dpsanders @tonysyu you probably want to add matplotlib.style to the list in setupext.Matplotlib.get_packages, and style/stylelib/* to get_package_data in the same class (though I don't know if styles belong in mpl-data instead)

Chris Beaumont

@tacaswell I don't think that is the issue being discussed here. Instead, this is about enabling a kind of opt-in behavior where someone can specify a default style in their own matplotlibrc. That would make it easier for MPL to develop alternative sets of rcParams that people could use by default if they want, without breaking test images or legacy user code.

David P. Sanders

@pelson Great, thanks -- I tried that, but got the following error from python setup.py build:

error: package directory 'lib/matplotlib/stylematplotlib/testing' does not exist

Any ideas?

Chris Beaumont

add a comma :)

David P. Sanders
Tony S Yu

@ChrisBeaumont Thanks for the tip on configuring the setup. This seems to install properly on my system.

David P. Sanders

I checked out @tonysyu 's branch with @ChrisBeaumont 's modifications, but still no matplotlib.style submodule available. Not sure what I'm doing wrong. (Well, I am sure: nothing... :P) Is there an installation log file or something I can look at?

Chris Beaumont

@tonysu It doesn't look like your tests are being run by Travis -- I think you want to move them to lib/matplotlib/tests, as that's where everything else seems to be. It looks like you should also update the default_test_modules list in __init__.py.

Chris Beaumont

@dpsanders let's talk about this off-line, until we diagnose if this is a problem with the PR or some one-off thing

David P. Sanders

Apologies, I was being caught out by the fact that style does not appear in dir(matplotlib) nor in autocompletion.
It is indeed there and works perfectly -- style.use( "ggplot") looks stunning!

How can this dir / autocomplete bug be fixed?

Apart from that, ready to merge!

Tony S Yu tonysyu referenced this pull request in tonysyu/mpltools
Closed

support for ipython notebook styles #12

Till Stensitzki

any reason this is not merged?

Michael Droettboom
Owner

Well, re-reading this, I think we got bogged down by the fact that rcParams aren't all really relevant to style... Maybe we should get this merged and play with it and see how things really play out in practice.

@ChrisBeaumont, @tonysyu: Any thoughts?

@tonysyu: Would you mind rebasing?

Chris Beaumont

+1 on merging this, and tackling the questions about refactoring rcParams in a separate issue

Chris Beaumont

Also see my previous comment about making sure the tests are discoverable

Tony S Yu

Argh! Sorry, I recently reformatted my computer, and I haven't been able to get matplotlib building correctly on my system. (The versions of libpng and freetype on my system don't have development headers, and I haven't had the time to make sure that I can rebuild them without affecting any other packages.) I can push a rebased version, but I won't actually be able to test anything on my current setup. Any chance someone can test a rebased PR and make any necessary changes?

@ChrisBeaumont Thanks for the checking on the tests. Before cleaning out my system, I tried to make sure the tests were discoverable without simply moving it to the test directory, but maybe I should just go ahead and move the test...

Adrian Price-Whelan

Am not sure the URL functionality is working in this?

Also, I moved the tests to the correct spot -- see the PR I just submitted to your branch.

lib/matplotlib/__init__.py
@@ -917,21 +948,43 @@ 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):
Phil Elson Collaborator
pelson added a note

The difference between rc_params_from_file and rc_params_in_file are so minimal I'd be tempted to factor this into a keyword - something like use_default_template=True/False?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Phil Elson
Collaborator

I'm late to this party, but I think this is a really good change. :+1:

I'm not the biggest fan of the rcParams system to control styling (which is the current state) from a configuration file - instead I agree with many of the other opinions proffered here that the style should be separate from the actual configuration.

I don't think that should be bundled into this PR, but I do think that we should look at ensuring that style is defined in the source code (explicitly linking to "style files" is fine) rather than allowing "global" default style changes, ideally before a v1.4 release...

Nice stuff @tonysyu!

Michael Droettboom
Owner

Yeah -- I completely concur with @pelson. And also that we shouldn't let that hold up this specific PR.

Phil Elson
Collaborator

I don't think style is documented in the API docs. Would you mind adding the appropriate file. I'm also keen to reduce the number of functions in lib/matplotlib/__init__.py so a keyword would be valuable. Other than that I'm :+1:

Phil Elson
Collaborator

There has been a lot of great feedback on this PR so I'd like to get a roll call from > 50% that they are +1 before we merge this (any one else please feel free to chime in with your :+1: / :-1: .

Chris Beaumont

I think @adrn Has a PR against @tonysyu's repo that adds support for gists. That could be considered here, or in a new PR. I'm fine with either

Phil Elson
Collaborator

or in a new PR

Definately. Love the gist idea though. Can I put you down as a :+1: @ChrisBeaumont?

lib/matplotlib/__init__.py
@@ -856,21 +862,45 @@ 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.
+URL_REGEX = re.compile(r'http://|https://|ftp://|file://|file:\\')
+
+
+def is_url(filename):
+ """Return True if string is an http or ftp path."""
Benjamin Root Collaborator

This docstring leaves out the possibility of file:// protocall.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/matplotlib/__init__.py
((7 lines not shown))
+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:
Benjamin Root Collaborator

Just wondering, would there be any newline issues if someone creates a style file on windows and it is used on a linux machine?

Michael Droettboom Owner
mdboom added a note

The file is opened in text mode, so it should do universal newline handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Chris Beaumont

Yes +1. Thanks @tonysyu, this is great

Michael Droettboom
Owner

:+1: from me, modulo addressing @WeatherGod's comments -- I'm fine if @adrn's gist thing doesn't go in before merging this -- but I wouldn't want it to get lost.

lib/matplotlib/style/core.py
((32 lines not shown))
+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):
+ """Use matplotlib style settings from a known style sheet or from a file.
+
+ 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.
+ """
+ if np.isscalar(name):
Benjamin Root Collaborator

we have cbook.is_scalar()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Phil Elson
Collaborator

@tonysyu - if you could address @WeatherGod and my own outstanding comments I think we can get this merged in the next 24hrs.

Benjamin Root
Collaborator

From an organizational perspective, I don't see why the tests are off into their own location. I would have thought to find the tests with all the other tests. I am also not a fan of core.py, but that is just a personal preference.

As for a few more things that are definitely needed: Add a "what's new" entry. Also, I would like to see an actual page describing these "Style Sheets" in the user's guide. Right now, we just have a couple of examples and the auto-generated API docs.

Michael Droettboom
Owner

@WeatherGod: I had missed that the tests were over there. It's not bad organizationally, but given the way our test harness works, they won't be run. They should be moved to matplotlib/tests/test_style.py and an entry added for them in __init__.py:default_test_modules.

Adrian Price-Whelan

FYI In my PR on @tonysyu's branch, I moved the tests...

Phil Elson
Collaborator

FYI In my PR on @tonysyu's branch, I moved the tests...

:smile: I like it.

Thomas A Caswell
Owner

Looks good to me, but I just skimmed the code.

My main concern as that the current default style does not change without ample warning and there is an easy way to roll back to the old style (which this provides) if it does.

Tony S Yu

Hi all: Thanks for the renewed interest in this PR. I made a few code updates as suggested here, but I've conspicuously left out updates for the API docs and "what's new". Unfortunately, I'll be quite busy for the next couple of weeks, so I won't be able to work on those right now, but I'm happy to pick it back up later (either here or in a separate PR). That said, I certainly wouldn't complain if someone wanted to hijack this PR and write those docs for me ;)

Phil Elson
Collaborator

@adrn - the gauntlet has been laid down - do you want to pick-up the baton with your own PR extending this work and submit a PR to matplotlib master?

Adrian Price-Whelan

I'll give this a go this weekend and see where I get.

Thomas A Caswell
Owner

To make this more confusing I wanted to use this (it took me a while to figure out rc_from_file pulls in the default) and ended up with my own branch that is re-based on current master and merges in @adrn 's work (but smashed down into one commit, sorry) a naming conflict resolution and some house keeping.

Lives at https://github.com/tacaswell/matplotlib/tree/stylesheets

Tony S Yu

@tacaswell Thanks for the fixes. Would you like to open up a PR against my branch. I should have some time to finish this up now.

I'm still a bit iffy about requiring an extension for local files but not for remote files, but if that really simplifies URL support, then I'm fine with it.

Thomas A Caswell
Owner

There is a PR against your branch from a branch where I cherry-picked the 4 commits over.

I think a PR from my first branch would be very messy because I re-based on-to master.

The changes from @adrn I think make it so you don't need an extension any where (experimentally it is the case that you don't need an extension).

Tony S Yu

Merged changes by @tacaswell and @adrn. Also added entries to User Guide and What's New page. Anything else.

Tony S Yu

Note: I rebased because I thought there were changes in master that broke my doc build. Turns out that I had some
bit-rotted files that were breaking the build. If you checked out this branch (and want to test some more), you'll have to do forced pull.

Michael Droettboom mdboom commented on the diff
doc/users/whats_new.rst
@@ -53,6 +53,25 @@ Controls whether figures are saved with a transparent
background by default. Previously `savefig` always defaulted
to a non-transparent background.
+
+``style`` package added
+```````````````````````
+You can now easily switch between different styles using the new ``style``
+package::
+
+ >>> from matplotlib import style
+ >>> style.use('dark_background')
Michael Droettboom Owner
mdboom added a note

It would be nice to have an inline plot here. Doesn't have to be done before merge, just a note to self, really.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Michael Droettboom
Owner

I think this is ready to merge, but I'm going to get to the bottom of the test failures first.

Adrian Price-Whelan

Thanks for taking care of this @tacaswell

Michael Droettboom
Owner

I've filed a PR against this that should hopefully fix the issues with the tests having styles leaking from one test to the next.

Michael Droettboom
Owner

Now something is really broken on Python 3, if anyone wants to have a look.

Tony S Yu

Unlikely, but I'm going to close in reopen in case there was somehow an error in the Travis CI build that caused the two Python 3 builds to fail at the same time. ("Have you tried turning it off and back on again?")

Tony S Yu tonysyu closed this
Tony S Yu tonysyu reopened this
Tony S Yu

Note: Rebased on master and forced a push. If you've pulled this down as a local branches, you'll have to do a clean (or forced) pull.

Phil Elson pelson commented on the diff
lib/matplotlib/pyplot.py
@@ -25,6 +25,7 @@
import matplotlib
import matplotlib.colorbar
+from matplotlib import style
Phil Elson Collaborator
pelson added a note

I think we should start a block of "guaranteed imports" within the pyplot namespace, otherwise this is liable to get nuked with an over-eager pyflakes (sure, there are test which catch this, but it does no harm to be explicit here).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/matplotlib/style/__init__.py
@@ -0,0 +1 @@
+from core import use, context, available, library, reload_library
Phil Elson Collaborator
pelson added a note

I love the fact you've used a folder here. sub-packages are one honking great idea -- let's do more of those!

Phil Elson Collaborator
pelson added a note

Obviously this comment only makes sense I've you've gone through the matplotlib code in as much detail as I have and you dislike having to scroll through the tens and tens of ".py" files in the top level matplotlib package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Phil Elson
Collaborator

Once the tests pass, I'm happy for this to go in. The one word I'd like to see in the "what's new" is "experimental" as I think we should accept that as users make use of this, we may want to change interfaces etc.
Other than that, I can see that this will be a very powerful and useful feature and we should get it merged asap.

Thanks @tonysyu!

Thomas A Caswell
Owner

@tonysyu it looks like you lost mbdoom's patch to fix style-leakage when you re-based.

Tony S Yu

@tacaswell Thanks for catching the dropped commits.

@pelson Thanks for the review. I added a note about the experimental nature of this feature. Unfortunately, I have no idea what's causing the test failures on the Python 3 buildbot.

Thomas A Caswell
Owner

It looks like there was a major issue building the test-suite in python 3. Is there a way to re-trigger this build?

Michael Droettboom
Owner

I don't know of a way to retrigger the build other than doing a rebase (i.e. something that would change the commit hash).

EDIT: Correction -- if I sign in to Travis, I can restart the build, and I've done so.

Thomas A Caswell
Owner

Sorry, I sent a PR to @tonysyu but didn't leave a note here. The issue with this PR is that some of the new files escaped the mass from __future__ ... import + six change over and there were some print blah statements.

Tony S Yu tonysyu closed this
Tony S Yu tonysyu reopened this
Thomas A Caswell
Owner

@tonysyu Needs a rebase, it doesn't merge cleanly right now.

Tony S Yu

Latest commit is just a sanity check on the Travis CI test failures. Do not merge until it's reverted.

Tony S Yu

Surprisingly, the Travis CI test failures for Python 3 have nothing to do with the tests added for this PR, as demonstrated by the failure of tonysyu@124cdfc.

Any ideas?

tonysyu and others added some commits
Tony S Yu tonysyu Add easy style sheet selection. 643c74b
Tony S Yu tonysyu Add rc_params_in_file to return partially-filled RcParams
This allows style sheets to update the current settings instead of overwriting them.
3270aa4
Tony S Yu tonysyu Rename style files to `*.style` d83a03c
Tony S Yu tonysyu Allow style.use to open URLs c8cc486
Tony S Yu tonysyu Remove pyplot import 455b54c
Tony S Yu tonysyu Add style context manager and tests 7769b29
Tony S Yu tonysyu Change style extension to *.mplstyle 3914089
Tony S Yu tonysyu Got a little crazy with the whitespace c3fae2e
Tony S Yu tonysyu Add style package and data to setupext.py a3de231
Tony S Yu tonysyu Move test so that it actually runs. d56f73e
Tony S Yu tonysyu Use explicit string check ec6ce6b
Tony S Yu tonysyu Hide rc_params_in_file from parent namespace 5fdc037
Adrian Price-Whelan adrn 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
ea63c99
Tony S Yu tonysyu Remove usage of import * 0c7437c
Tony S Yu tonysyu Clarify docstring 200d2e0
Thomas A Caswell tacaswell added `matplotlib.style` to pyplot import list 7392ce6
Thomas A Caswell tacaswell fixed divergent naming scheme eaa23ee
Thomas A Caswell tacaswell pep8 clean up 5f80ca1
Tony S Yu tonysyu Add docs for style package f5ecf5e
Tony S Yu tonysyu Import style package for easy use. a8ef5bf
Tony S Yu tonysyu Use style package from pyplot dc291e0
Tony S Yu tonysyu Fix test c5b5bb4
Michael Droettboom mdboom Clear style settings between tests
Conflicts:
	lib/matplotlib/tests/test_style.py

Conflicts:
	lib/matplotlib/tests/test_style.py
46a725b
Thomas A Caswell tacaswell pep8 7ac26ee
Tony S Yu tonysyu Add note that style sheets are experimental. d372a35
Thomas A Caswell tacaswell added python3 emulation code + six + fixed up print calls e714c77
Thomas A Caswell tacaswell removed unneeded print statements
removed some of the unneeded imports
512b77c
Tony S Yu tonysyu Attempt to fix python 3 test errors on Travis CI 17282c8
Tony S Yu tonysyu Remove test from list to test Travis CI failure. a6142fc
Tony S Yu tonysyu Remove test file to test Travis CI failure 19e7bed
Tony S Yu tonysyu 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.
c604498
Tony S Yu tonysyu Fix import for python 3 0b098e2
Tony S Yu tonysyu Use iteritems from `six` module 7e2bffb
Tony S Yu tonysyu Add compatibility layer for Python 3's urlopen 1d87f34
Tony S Yu

Tests are almost passing. Maybe Python 2.6 has an issue with a context-manager returning a generator?

Michael Droettboom
Owner

I've made a PR against this one with a Python 2.6 fix

Tony S Yu

Woot! Tests passing!

Michael Droettboom mdboom merged commit 0e7daad into from
Michael Droettboom
Owner

Merged. Thanks, Tony. This was a big one!

Chris Beaumont

Phew! That was harder than I expected, but I'm glad this is now part of MPL

Tony S Yu

Thanks to everyone for all the improvements and fixes. It definitely took longer than expected, but it's great to have this in master. On to the rcparams refactor! (I'm not volunteering to lead that charge, but I'm happy to help where I can :).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 17, 2013
  1. Tony S Yu
  2. Tony S Yu

    Add rc_params_in_file to return partially-filled RcParams

    tonysyu authored
    This allows style sheets to update the current settings instead of overwriting them.
  3. Tony S Yu
  4. Tony S Yu

    Allow style.use to open URLs

    tonysyu authored
  5. Tony S Yu

    Remove pyplot import

    tonysyu authored
  6. Tony S Yu
  7. Tony S Yu
  8. Tony S Yu
  9. Tony S Yu
  10. Tony S Yu
  11. Tony S Yu

    Use explicit string check

    tonysyu authored
  12. Tony S Yu
  13. Adrian Price-Whelan Tony S Yu

    fix url rc specification

    adrn authored tonysyu committed
    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
  14. Tony S Yu

    Remove usage of import *

    tonysyu authored
  15. Tony S Yu

    Clarify docstring

    tonysyu authored
  16. Thomas A Caswell Tony S Yu

    added `matplotlib.style` to pyplot import list

    tacaswell authored tonysyu committed
  17. Thomas A Caswell Tony S Yu

    fixed divergent naming scheme

    tacaswell authored tonysyu committed
  18. Thomas A Caswell Tony S Yu

    pep8 clean up

    tacaswell authored tonysyu committed
  19. Tony S Yu

    Add docs for style package

    tonysyu authored
  20. Tony S Yu
  21. Tony S Yu

    Use style package from pyplot

    tonysyu authored
  22. Tony S Yu

    Fix test

    tonysyu authored
  23. Michael Droettboom Tony S Yu

    Clear style settings between tests

    mdboom authored tonysyu committed
    Conflicts:
    	lib/matplotlib/tests/test_style.py
    
    Conflicts:
    	lib/matplotlib/tests/test_style.py
  24. Thomas A Caswell Tony S Yu

    pep8

    tacaswell authored tonysyu committed
  25. Tony S Yu
  26. Thomas A Caswell Tony S Yu

    added python3 emulation code + six + fixed up print calls

    tacaswell authored tonysyu committed
  27. Thomas A Caswell Tony S Yu

    removed unneeded print statements

    tacaswell authored tonysyu committed
    removed some of the unneeded imports
  28. Tony S Yu
  29. Tony S Yu
  30. Tony S Yu
  31. Tony S Yu

    Revert commits used to test Travis CI test failures.

    tonysyu authored
    End result of the experiment: The failure isn't related to the tests
    added by this PR.
  32. Tony S Yu

    Fix import for python 3

    tonysyu authored
  33. Tony S Yu
  34. Tony S Yu
Commits on Nov 18, 2013
  1. Michael Droettboom

    Fix _fix_url on Python 2.6

    mdboom authored
  2. Tony S Yu

    Merge pull request #6 from mdboom/style/py26-fixes

    tonysyu authored
    Fix for Python 2.6
This page is out of date. Refresh to see the latest.
4 doc/conf.py
View
@@ -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'),
1  doc/users/beginner.rst
View
@@ -13,6 +13,7 @@ Beginner's Guide
:maxdepth: 2
pyplot_tutorial.rst
+ style_sheets.rst
navigation_toolbar.rst
index_text.rst
image_tutorial.rst
89 doc/users/style_sheets.rst
View
@@ -0,0 +1,89 @@
+.. _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::
+
+ >>> import matplotlib.pyplot as plt
+ >>> plt.style.use('ggplot')
+
+To list all available styles, use::
+
+ >>> print plt.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::
+
+ >>> import matplotlib.pyplot as plt
+ >>> plt.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::
+
+ >>> 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.
+
+
+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
+ >>>
+ >>> 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
+ >>>
+ >>> plt.show()
+
+
+.. _matplotlibrc: http://matplotlib.sourceforge.net/users/customizing.html
+.. _ggplot: http://had.co.nz/ggplot/
+.. _R: http://www.r-project.org/
20 doc/users/whats_new.rst
View
@@ -98,6 +98,26 @@ 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')
Michael Droettboom Owner
mdboom added a note

It would be nice to have an inline plot here. Doesn't have to be done before merge, just a note to self, really.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+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.
+
+*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
23 examples/style_sheets/plot_dark_background.py
View
@@ -0,0 +1,23 @@
+"""
+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
+
+
+plt.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()
50 examples/style_sheets/plot_ggplot.py
View
@@ -0,0 +1,50 @@
+"""
+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
+
+plt.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()
32 examples/style_sheets/plot_grayscale.py
View
@@ -0,0 +1,32 @@
+"""
+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
+
+
+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)
+
+
+plt.style.use('grayscale')
+
+fig, (ax1, ax2) = plt.subplots(ncols=2)
+
+color_cycle_example(ax1)
+image_and_patch_example(ax2)
+
+plt.show()
127 lib/matplotlib/__init__.py
View
@@ -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,21 +884,51 @@ 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.
+URL_REGEX = re.compile(r'http://|https://|ftp://|file://|file:\\')
+
+
+def is_url(filename):
+ """Return True if string is an http, ftp, or file URL path."""
+ 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('utf8')
+
+
+@contextlib.contextmanager
+def _open_file_or_url(fname):
+ if is_url(fname):
+ f = urlopen(fname)
+ yield _url_lines(f)
+ f.close()
+ else:
+ with open(fname) as f:
+ yield f
+
+
+_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 = {}
- 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()
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 +938,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 +976,50 @@ 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, use_default_template=True):
+ """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.
+ 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)
+
+ if not use_default_template:
+ return config_from_file
+
+ 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'])
+
+ 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
@@ -1257,6 +1323,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',
1  lib/matplotlib/pyplot.py
View
@@ -25,6 +25,7 @@
import matplotlib
import matplotlib.colorbar
+from matplotlib import style
Phil Elson Collaborator
pelson added a note

I think we should start a block of "guaranteed imports" within the pyplot namespace, otherwise this is liable to get nuked with an over-eager pyflakes (sure, there are test which catch this, but it does no harm to be explicit here).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
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
3  lib/matplotlib/style/__init__.py
View
@@ -0,0 +1,3 @@
+from __future__ import absolute_import
+
+from .core import use, context, available, library, reload_library
161 lib/matplotlib/style/core.py
View
@@ -0,0 +1,161 @@
+from __future__ import (absolute_import, division, print_function,
+ unicode_literals)
+
+import six
+
+"""
+Core functions and attributes for the matplotlib style library:
+
+``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``
+ A dictionary of style names and matplotlib settings.
+"""
+import os
+import re
+import contextlib
+
+import matplotlib as mpl
+from matplotlib import cbook
+from matplotlib import rc_params_from_file
+
+
+__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_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
+
+
+def use(name):
+ """Use matplotlib style settings from a known style sheet or from a file.
+
+ 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.
+ """
+ if cbook.is_string_like(name):
+ name = [name]
+
+ for style in name:
+ if style in library:
+ mpl.rcParams.update(library[style])
+ else:
+ try:
+ rc = rc_params_from_file(style, use_default_template=False)
+ mpl.rcParams.update(rc)
+ except:
+ msg = ("'%s' not found in the style library and input is "
+ "not a valid URL or path. See `style.available` for "
+ "list of available styles.")
+ raise ValueError(msg % 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()
+ 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)
+ 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]
+
+
+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] = rc_params_from_file(path, use_default_template=False)
+ 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 six.iteritems(new_dict):
+ if name in main_dict:
+ main_dict[name].update(rc_dict)
+ else:
+ main_dict[name] = rc_dict
+ return main_dict
+
+
+# Load style library
+# ==================
+_base_library = load_base_library()
+
+library = None
+available = []
+
+
+def reload_library():
+ """Reload style library."""
+ global library, available
+ library = update_user_library(_base_library)
+ available[:] = library.keys()
+reload_library()
23 lib/matplotlib/style/stylelib/dark_background.mplstyle
View
@@ -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 lib/matplotlib/style/stylelib/ggplot.mplstyle
View
@@ -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 lib/matplotlib/style/stylelib/grayscale.mplstyle
View
@@ -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
+
73 lib/matplotlib/tests/test_style.py
View
@@ -0,0 +1,73 @@
+from __future__ import (absolute_import, division, print_function,
+ unicode_literals)
+
+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
+
+import six
+
+
+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 six.iteritems(settings):
+ 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()
4 setupext.py
View
@@ -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',
]}
Something went wrong with that request. Please try again.