-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
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
Conversation
This is very handy. +1. |
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): |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
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. |
@pelson: Thanks for finding the way to avoid the large |
Yes @pelson! Good spot. I didn't think it was possible. I will obviously update to reflect this. Awesome! :D |
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')
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. |
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? |
@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. |
Ok, 3e4dea3 is a rather large commit. @pelson's suggestion of making each backend have a 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 |
@@ -330,6 +330,12 @@ def __init__(self, | |||
self.clf() | |||
self._cachedRenderer = None | |||
|
|||
def _setup_canvas(self): | |||
# TODO: docstring |
There was a problem hiding this comment.
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.
@@ -268,7 +268,6 @@ class TimerMac(_macosx.Timer, TimerBase): | |||
''' | |||
# completely implemented at the C-level (in _macosx.Timer) | |||
|
|||
|
There was a problem hiding this comment.
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.
Looks good. I would be happy to merge this once there was a docstring on 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.
@pelson - agree, the docs can be worked on in a different PR. |
Should there be a way to optionally make it behave like before, i.e. to disable setting the canvas? e.g. something like EDIT: alternatively, a |
I'm not that worried by it. The easy answer is for users to simply do Unless anybody has any concerns, I will merge this in 18 hours. Good stuff @dmcdougall. |
@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! :-) |
@astrofrog Thanks for the interest :) Glad I could help make someone's life easier. |
Ah. I'm sorry @dmcdougall - I was a bit eager. Would you mind adding an entry in the what's new section? |
@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. |
I also added a nice example exposing the new change, just because there's no example anywhere else. |
Reduce object-oriented boilerplate for users
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in #1214.
The attempt to make OO use easier broke the Tkagg and Wx* backends.
Now, one can work with the default backend to create figures, without
the need to attach a canvas manually. For example:
This is an attempt at resolving issue #1094.
Disclaimer: I have no idea what implications this has on
pyplot
andpylab
, but 'it just works' for me, and I tried theMacOSX
,Agg
andPDF
backends.