Thanks to visit codestin.com
Credit goes to github.com

Skip to content

RcParams instances for matplotlib.style.use #3795

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 10 commits into from
Nov 25, 2014

Conversation

chebee7i
Copy link
Contributor

External libraries may want to prepare styles dynamically. For example, brewer2mpl provides various colormaps. While it could create it's own context manager for temporarily changing the color_cycle, I think it makes more sense if matplotlib.style.use can work directly with RcParams instances.

>>> with matplotlib.style.context(matplotlib.RcParams(**{'text.usetex':False})):
...     print matplotlib.rcParams['text.usetex']
...      
False

@tacaswell
Copy link
Member

There is already a rcparam context manager matplotlib.rc_contex iirc.

@chebee7i
Copy link
Contributor Author

The intended use-cases are a bit different though.

matplotlib.rc_context seems to be intended for loading complete rcParams from file (ala use_default_template=True) or for updating the existing rcParams from a dictionary (and doing both in combination). This will certainly work for the use case I described.

On the other hand, matplotlib.style.context is tailored towards loading and chaining styles (ala use_default_template=False). This is more flexible and with this PR, we can do something like:

rc = matplotlib.RcParams([('text.usetext', True)])
with matplotlib.style.context(['ggplot2', rc, '/path/to/myfile.mplstyle']):
    pass

To get the same functionality with matplotlib.rc_context, you'd have to 1) use nested with rc_context(rc=blah) blocks, 2) merge the dictionaries beforehand, or use 3) contextlib.nested.

@tacaswell
Copy link
Member

I don't really understand, rc_context works as expected with just a dict as an arg and nests just fine:

import matplotlib
matplotlib.rcParams['font.size'] = 314
print matplotlib.rcParams['text.usetex'], matplotlib.rcParams['font.size']
with matplotlib.rc_context(rc={'text.usetex': True}):
    print matplotlib.rcParams['text.usetex'], matplotlib.rcParams['font.size']
    with matplotlib.rc_context(rc={'text.usetex': False}):
        print matplotlib.rcParams['text.usetex'], matplotlib.rcParams['font.size']
        with matplotlib.rc_context(rc={'font.size': 12}):
            print matplotlib.rcParams['text.usetex'], matplotlib.rcParams['font.size']
        print matplotlib.rcParams['text.usetex'], matplotlib.rcParams['font.size']
    print matplotlib.rcParams['text.usetex'], matplotlib.rcParams['font.size']
print matplotlib.rcParams['text.usetex'], matplotlib.rcParams['font.size']

The use case in your second post does make this change more compelling.

attn @tonysyu

@@ -45,16 +45,19 @@ def use(name):

Parameters
----------
name : str or list of str
name : str, list of str, or RcParams
Copy link
Member

Choose a reason for hiding this comment

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

should accept a dict objects

@tacaswell tacaswell added this to the v1.5.x milestone Nov 14, 2014
@chebee7i
Copy link
Contributor Author

@tacaswell Yes, that's all I was saying: rc_context requires nesting in order to chain, whereas style.context chains for you (and also works with named styles). I'll go ahead and make those changes.

@chebee7i chebee7i force-pushed the style-rcparams branch 3 times, most recently from 4cddc55 to 9cdc0c6 Compare November 14, 2014 08:53
@chebee7i
Copy link
Contributor Author

I ended up trying:

    if cbook.is_string_like(name) or hasattr(name, 'keys'):
        name = [name]

CPython seems to require a keys attribute in order for update() to work at all. From your suggestion, another alternative could be:

if cbook.is_string_like(name):
    name = [name]
else:
    try:
        mpl.rcParams.update(name)
    except:
        # Correctly catches when name is a list, but bad dicts pass through without an error.
        pass
    else:
        name = []

for style in name:
    ...

Let me know what you think is better, or if you have a better idea.

@chebee7i
Copy link
Contributor Author

Unit test failures seem unrelated now: FAIL: matplotlib.tests.test_axes.test_pcolormesh.test

name = [name]

for style in name:
if not cbook.is_string_like(style):
Copy link
Member

Choose a reason for hiding this comment

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

It seems very odd to test not is_string_like in a is_string_like.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The not is_string_like was after (not within) a previous call to is_string_like. The only reason I chose the negated form for the second call was to avoid having to indent everything an extra level. We could also just do the extra indentation:

        if cbook.is_string_like(style):
            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)
        else:
            mpl.rcParams.update(style)

Preferred?

Copy link
Member

Choose a reason for hiding this comment

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

Blah, sorry, I misread the diffs, disregard this comment.

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.
name : str, dict, list of str and/or dict
Copy link
Contributor

Choose a reason for hiding this comment

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

With this addition, the name of this argument is a bit misleading (actually, it was a bit misleading before, too). Maybe something like style_spec, or something similar. (By "spec", I mean specifier.)

Also, the description is a bit difficult to read because of all the different options. Maybe something like:

    style_spec : str, dict, or list
        str: The name of a style or a path/URL to a style file. For a list of
            available style names, see `style.available`.
        dict: Dictionary with valid key/value pairs in `matplotlib.rcParams`.
        list: List of style specifiers (str or dict) applied from first to
            last in the list.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll go for "style" and see what you guys think.

@chebee7i
Copy link
Contributor Author

Anything else needed for this?

@tacaswell
Copy link
Member

Some what out side of the scope of this PR, but this needs to have the bit of logic added to the rc_context recently to make sure that the rcParams is rolled back to it's previous state if something raises an exception in update.

style names, see `style.available`. If given a list, each style is
applied from first to last in the list.
style : str, dict, or list
str: The name of a style or a path/URL to a style file. For a list of
Copy link
Member

Choose a reason for hiding this comment

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

Can you make this a rst table? I don't think this is going to render very will in the html docs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oops, I guess this doesn't render correctly. Personally, I think a simple restructuredtext-list would be easier to read in plain text.

@tacaswell
Copy link
Member

I would like @tonysyu to sign off on this before it gets merged as well.

@chebee7i
Copy link
Contributor Author

@tacaswell Regarding errors on updating, all the code uses the update method on RcParams. This does not provide validation, so while we might catch some errors, we won't catch the errors we really want to catch. Should the updates loop over key/value pairs instead? [This also affects style.context.]

@chebee7i
Copy link
Contributor Author

I tried a table. In plain text it looks ok. Let me know what you think. Too bad sphinx isn't more flexible here.

@tonysyu
Copy link
Contributor

tonysyu commented Nov 25, 2014

The table looks fine too. 👍

@tacaswell
Copy link
Member

update does validate now, #3564

See #3752 for the issue where
the error handling was added to the main context manager.

On Mon Nov 24 2014 at 10:21:50 PM chebee7i [email protected] wrote:

I tried a table. In plain text it looks ok. Let me know what you think.


Reply to this email directly or view it on GitHub
#3795 (comment)
.

@chebee7i
Copy link
Contributor Author

Rebased and added the logic for restore rcParam state.

tacaswell added a commit that referenced this pull request Nov 25, 2014
ENH : RcParams/dict as input to matplotlib.style.use
@tacaswell tacaswell merged commit 980e4cd into matplotlib:master Nov 25, 2014
@tacaswell
Copy link
Member

@chebee7i Thanks!

If you are up for it, some more work done to unify the style and rcparam code. It seems a little silly to have two almost identical context managers and the rcparams code in general could use some cleaning up.

I am not sure the best way to go about doing that (to maintain back compatibility and avoid circular dependencies).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants