Skip to content

Reduce object-oriented boilerplate for users #1125

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 5 commits into from
Sep 4, 2012
Merged

Conversation

dmcdougall
Copy link
Member

Now, one can work with the default backend to create figures, without
the need to attach a canvas manually. For example:

import matplotlib
matplotlib.use('agg')
from matplotlib.figure import Figure

fig = Figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot([1, 2, 3], [1, 2, 3])
fig.savefig('oo.png')

This is an attempt at resolving issue #1094.

Disclaimer: I have no idea what implications this has on pyplot and pylab, but 'it just works' for me, and I tried the MacOSX, Agg and PDF backends.

@mdboom
Copy link
Member

mdboom commented Aug 28, 2012

This is very handy. +1.

@efiring
Copy link
Member

efiring commented Aug 28, 2012

The basic idea seems good. Ideally, I would think that each backend should provide a canvas in way that does not require the huge if...elif structure, but this would require a larger change, so perhaps it should be left until later. figure.py should not need to know about all possible backends; it should be able to do something like "import matplotlib.backends; matplotlib.backends.get_current_canvas()".

In any case, all backends supported by rcsetup.validate_backends (and listed in rcsetup.all_backends) should be included; some are missing at present.

@@ -323,6 +323,48 @@ def __init__(self,
self.clf()
self._cachedRenderer = None

def _current_figure_canvas(self):
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps this could be a function in backend_bases? pylab_setup pretty much does this for you. Something like:

import matplotlib.backends as mbackends

 # NOTE: requires a change from #1020
backend_mod, _, _, _ = mbackends.pylab_setup()


# NOTE: would require all backend modules to have 
# a FigureCanvas rather than say a FigureCanvasGTKAgg
backend_mod.FigureCanvas(self) 

Copy link
Member

Choose a reason for hiding this comment

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

Also, needs a docstring, and the name of the method is misleading.

@pelson
Copy link
Member

pelson commented Aug 29, 2012

Ah. I've only just read @efiring 's comment. It seems we agree on this issue. I definitely think this should be done properly at this stage, and would think it was cleaner to do this via the "backend_bases" module. Other than that, I think this is a great idea and will make working in a full OO way much easier.

@mdboom
Copy link
Member

mdboom commented Aug 29, 2012

@pelson: Thanks for finding the way to avoid the large elif statement. I hadn't seen this earlier, so thought it was the best we could do for now. That's definitely preferable.

@dmcdougall
Copy link
Member Author

Yes @pelson! Good spot. I didn't think it was possible. I will obviously update to reflect this. Awesome! :D

@dmcdougall
Copy link
Member Author

Also, thanks @efiring, @pelson and @mdboom for looking through the code. You are all heroes.

Now, one can work with the default backend to create figures, without
the need to attach a canvas manually. For example:

import matplotlib
matplotlib.use('agg')
from matplotlib.figure import Figure

fig = Figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot([1, 2, 3], [1, 2, 3])
fig.savefig('oo.png')
@dmcdougall
Copy link
Member Author

I've rebased this against master to get the awesome changes from #1175. This will save me from conflicts with the changes I'm about to make.

@astrofrog
Copy link
Contributor

I wonder whether as part of this PR, it would make sense to also update the documentation to add clearer information about the pure-OO API? At the moment, it's very hard to find any information about it. Maybe there should be a top-level section under 'User guide' that talks about the pure-OO API and the pros and cons compared to using pyplot or th hybrid pyplot-OO?

@dmcdougall
Copy link
Member Author

@astrofrog That's a great suggestion.

Would any of the wider community appreciate a couple of example scripts that use the object-oriented interface? I'd be happy to write some.

@dmcdougall
Copy link
Member Author

Ok, 3e4dea3 is a rather large commit. @pelson's suggestion of making each backend have a FigureCanvas as opposed to, say, a FigureCanvasAgg is a backwards incompatible change. Instead, I though it was better to just write an alias for each backend. That's the main crux of the new commit, just in case anybody didn't have the stamina to scroll through a colossal diff. In fact, I think one of the backends already had this alias, which is nice.

I'm still a newbie when it comes to the codebase, which is why I need you guys to tell me if I've put something in a sub-optimal place. You've been really helpful with all of my suggestions. So thank you.

Please test it out. Specifically, I don't want this to break anything in pylab. So if any of you are pylab users, your feedback is very welcome.

@@ -330,6 +330,12 @@ def __init__(self,
self.clf()
self._cachedRenderer = None

def _setup_canvas(self):
# TODO: docstring
Copy link
Member Author

Choose a reason for hiding this comment

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

I will do it. I promise.

@travisbot
Copy link

This pull request fails (merged 3e4dea3a into cadd152).

@@ -268,7 +268,6 @@ class TimerMac(_macosx.Timer, TimerBase):
'''
# completely implemented at the C-level (in _macosx.Timer)


Copy link
Member

Choose a reason for hiding this comment

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

Double new line was intentional here.

@pelson
Copy link
Member

pelson commented Sep 2, 2012

Looks good. I would be happy to merge this once there was a docstring on _setup_canvas.

I know I'm always picky with newlines [and I hate myself for it ;-) ] but normally one would put a double new line between top level objects in a module (classes, functions, groups of constants etc.) and a single new line for the rest (in particular methods). I don't this doing this should block this PR though - I think this is a really helpful change.

@astrofrog 's suggestion of adding a section on pure OO & its benefits is a good idea, but lets keep this implementation and PR separate - there is no need to hold up one with the other.

figure.py now uses pylab_setup() to probe for the backend module to set
up the figure canvas.
@astrofrog
Copy link
Contributor

@pelson - agree, the docs can be worked on in a different PR.

@astrofrog
Copy link
Contributor

Should there be a way to optionally make it behave like before, i.e. to disable setting the canvas? e.g. something like set_canvas or something like that? (that would default to True)

EDIT: alternatively, a canvas argument that defaults to auto but can be set to None or a canvas instance?

@travisbot
Copy link

This pull request fails (merged f83fbc9 into cadd152).

@travisbot
Copy link

This pull request fails (merged 88e299b into cadd152).

@pelson
Copy link
Member

pelson commented Sep 2, 2012

Should there be a way to optionally make it behave like before, i.e. to disable setting the canvas?

I'm not that worried by it. The easy answer is for users to simply do figure.canvas = None if they don't want the canvas which has been given to them. Maybe other have concerns about this?

Unless anybody has any concerns, I will merge this in 18 hours. Good stuff @dmcdougall.

@astrofrog
Copy link
Contributor

@pelson - ok, I don't have a strong opinion about that, so we can just address it if people ask for it. I might open a PR if I run into issues with this some day.

Looking forward to using this! :-)

@dmcdougall
Copy link
Member Author

@astrofrog Thanks for the interest :) Glad I could help make someone's life easier.

@pelson
Copy link
Member

pelson commented Sep 4, 2012

Ah. I'm sorry @dmcdougall - I was a bit eager. Would you mind adding an entry in the what's new section?

@dmcdougall
Copy link
Member Author

@pelson And the changelog, too?

@pelson
Copy link
Member

pelson commented Sep 4, 2012

@pelson And the changelog, too?

My feeling is not. Your not breaking anything, simply making one use case easier. We only need one entry: whichever you choose will be fine in this case.

@dmcdougall
Copy link
Member Author

I also added a nice example exposing the new change, just because there's no example anywhere else.

@travisbot
Copy link

This pull request fails (merged 8738c1c into cadd152).

pelson added a commit that referenced this pull request Sep 4, 2012
Reduce object-oriented boilerplate for users
@pelson pelson merged commit dd9cfa4 into matplotlib:master Sep 4, 2012
@pelson
Copy link
Member

pelson commented Sep 4, 2012

Thanks to @dmcdougall for doing the work & to @astrofrog for suggesting it. This is a real nice addition.

@@ -33,6 +33,7 @@ def _get_toolbar(self, canvas):
toolbar = None
return toolbar

FigureCanvas = FigureManagerGTKAgg
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be FigureCanvasGTKAgg (and moved after the definition of FigureCanvasGTKAgg). At least this doesn't work.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops. Yes. Good spot. I will open another PR to address this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, GtkAgg backend currently does not work, which I believe is due to this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry! Fixed in #1201.

"""
import matplotlib.backends as mbackends # lazy import
backend_mod = mbackends.pylab_setup()[0]
return backend_mod.FigureCanvas(self)
Copy link
Member

Choose a reason for hiding this comment

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

@verbit made a comment on this line on the commit 8bd1d57. @verbit : Would you mind re-iterating your comments here?

Thanks

Copy link

Choose a reason for hiding this comment

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

What if the module from pylab_setup()[0] doesn't have a FigureCanvas class. Then the line return backend_mod.FigureCanvas(self) would result in an error. So it does with IPython that uses his own backend module but without a FigureCanvas class.

The old def _current_figure_canvas(self)works because in case it doesn't find the right FigureCanvas it returns None.

Copy link
Member Author

Choose a reason for hiding this comment

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

@verbit Line 508 of lib/matplotlib/backends/backend_tkagg is

FigureCanvas = FigureCanvasTkAgg

so the return value of pylab_setup[0] does indeed have a FigureCanvas for this backend. Do you see this behaviour with qt4agg or gtk3agg?

Perhaps this is actually a bug in the tkagg backend?

Copy link

Choose a reason for hiding this comment

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

I saw this behaviour with IPython's custom backend (see: backend_inline.py) that doesn't return a FigureCanvas.
I'm just concerned about the fact, that this new _setup_canvas function might break other applications that don't provide a FigureCanvas class as they are used to the old way.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed in #1214.

efiring added a commit to efiring/matplotlib that referenced this pull request Sep 8, 2012
The attempt to make OO use easier broke the Tkagg and Wx* backends.
efiring added a commit that referenced this pull request Sep 8, 2012
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.

9 participants