Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Reduce object-oriented boilerplate for users #1125

Merged
merged 5 commits into from

9 participants

@dmcdougall
Collaborator

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
Owner

This is very handy. +1.

@efiring
Owner

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.

lib/matplotlib/figure.py
@@ -323,6 +323,48 @@ def __init__(self,
self.clf()
self._cachedRenderer = None
+ def _current_figure_canvas(self):
@pelson Collaborator
pelson added a note

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) 

@pelson Collaborator
pelson added a note

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/matplotlib/figure.py
@@ -323,6 +323,48 @@ def __init__(self,
self.clf()
self._cachedRenderer = None
+ def _current_figure_canvas(self):
+ b = rcParams['backend'].lower()
+ if b == 'agg':
+ from backends.backend_agg import FigureCanvasAgg as FigureCanvas
+ elif b == 'cairo':
+ from backends.backend_cairo import FigureCanvasCairo as FigureCanvas
@pelson Collaborator
pelson added a note

I believe these should be absolute imports (i.e. include the "matplotlib").

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

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
Owner

@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
Collaborator

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

@dmcdougall
Collaborator

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

@dmcdougall dmcdougall Reduce object-oriented boilerplate for users
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')
700b26b
@dmcdougall
Collaborator

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

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
Collaborator

@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
Collaborator

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.

lib/matplotlib/figure.py
@@ -330,6 +330,12 @@ def __init__(self,
self.clf()
self._cachedRenderer = None
+ def _setup_canvas(self):
+ # TODO: docstring
@dmcdougall Collaborator

I will do it. I promise.

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

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

lib/matplotlib/backends/backend_macosx.py
@@ -268,7 +268,6 @@ class TimerMac(_macosx.Timer, TimerBase):
'''
# completely implemented at the C-level (in _macosx.Timer)
-
@pelson Collaborator
pelson added a note

Double new line was intentional here.

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

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.

dmcdougall added some commits
@dmcdougall dmcdougall Make figure.py backend-agnostic
figure.py now uses pylab_setup() to probe for the backend module to set
up the figure canvas.
8bd1d57
@dmcdougall dmcdougall Add docstring to _setup_canvas() f83fbc9
@astrofrog

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

@astrofrog

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

This pull request fails (merged f83fbc9 into cadd152).

@travisbot

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

@pelson
Collaborator

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

@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
Collaborator

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

@pelson
Collaborator

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

@dmcdougall
Collaborator

@pelson And the changelog, too?

@pelson
Collaborator

@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
Collaborator

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

@travisbot

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

@pelson pelson merged commit dd9cfa4 into matplotlib:master
@pelson
Collaborator

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

@jenshnielsen jenshnielsen commented on the diff
lib/matplotlib/backends/backend_gtkagg.py
@@ -33,6 +33,7 @@ def _get_toolbar(self, canvas):
toolbar = None
return toolbar
+FigureCanvas = FigureManagerGTKAgg
@jenshnielsen Owner

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

@dmcdougall Collaborator

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

@leejjoon Owner
leejjoon added a note

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

@dmcdougall Collaborator

Sorry! Fixed in #1201.

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

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.

@pelson pelson commented on the diff
lib/matplotlib/figure.py
@@ -330,6 +330,14 @@ def __init__(self,
self.clf()
self._cachedRenderer = None
+ def _setup_canvas(self):
+ """
+ Return the FigureCanvas instance defined by the currently loaded backend.
+ """
+ import matplotlib.backends as mbackends # lazy import
+ backend_mod = mbackends.pylab_setup()[0]
+ return backend_mod.FigureCanvas(self)
@pelson Collaborator
pelson added a note

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

Thanks

@verbit
verbit added a note

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.

@dmcdougall Collaborator

@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?

@verbit
verbit added a note

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.

@dmcdougall Collaborator

Fixed in #1214.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@pelson pelson commented on the diff
lib/matplotlib/figure.py
@@ -318,7 +318,7 @@ def __init__(self,
self.patch.set_aa(False)
self._hold = rcParams['axes.hold']
- self.canvas = None
+ self.canvas = self._setup_canvas()
@pelson Collaborator
pelson added a note

This change means that I now get two TK widows popping up (one empty, one with a figure) with the following code:

import matplotlib
matplotlib.use('tkagg')
import matplotlib.pyplot as plt

plt.plot(range(10))
plt.show()

Presumably this is true of all other backends too. Looks like we might have to implement @astrofrog 's suggestion of making this togglable (and have pyplot toggle that toggler).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@efiring efiring referenced this pull request from a commit in efiring/matplotlib
@efiring efiring revert PR #1125 and #1201
The attempt to make OO use easier broke the Tkagg and Wx* backends.
b0807ae
@ghost Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@jenshnielsen jenshnielsen referenced this pull request in ipython/ipython
Closed

Adapt inline backend to changes in matplotlib #2384

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 2, 2012
  1. @dmcdougall

    Reduce object-oriented boilerplate for users

    dmcdougall authored
    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')
  2. @dmcdougall

    Make figure.py backend-agnostic

    dmcdougall authored
    figure.py now uses pylab_setup() to probe for the backend module to set
    up the figure canvas.
  3. @dmcdougall
  4. @dmcdougall

    Fix whitespace

    dmcdougall authored
Commits on Sep 4, 2012
  1. @dmcdougall

    Add what's new section

    dmcdougall authored
This page is out of date. Refresh to see the latest.
Showing with 64 additions and 5 deletions.
  1. +11 −0 doc/users/whats_new.rst
  2. +2 −0  lib/matplotlib/backends/backend_agg.py
  3. +2 −0  lib/matplotlib/backends/backend_cairo.py
  4. +1 −1  lib/matplotlib/backends/backend_cocoaagg.py
  5. +2 −0  lib/matplotlib/backends/backend_emf.py
  6. +2 −0  lib/matplotlib/backends/backend_fltkagg.py
  7. +3 −1 lib/matplotlib/backends/backend_gdk.py
  8. +3 −1 lib/matplotlib/backends/backend_gtk.py
  9. +1 −1  lib/matplotlib/backends/backend_gtk3.py
  10. +1 −0  lib/matplotlib/backends/backend_gtk3agg.py
  11. +1 −0  lib/matplotlib/backends/backend_gtk3cairo.py
  12. +1 −0  lib/matplotlib/backends/backend_gtkagg.py
  13. +1 −0  lib/matplotlib/backends/backend_gtkcairo.py
  14. +1 −0  lib/matplotlib/backends/backend_macosx.py
  15. +2 −0  lib/matplotlib/backends/backend_pdf.py
  16. +2 −0  lib/matplotlib/backends/backend_pgf.py
  17. +2 −0  lib/matplotlib/backends/backend_ps.py
  18. +2 −0  lib/matplotlib/backends/backend_qt.py
  19. +2 −0  lib/matplotlib/backends/backend_qt4.py
  20. +2 −0  lib/matplotlib/backends/backend_qt4agg.py
  21. +2 −0  lib/matplotlib/backends/backend_qtagg.py
  22. +2 −0  lib/matplotlib/backends/backend_svg.py
  23. +2 −0  lib/matplotlib/backends/backend_template.py
  24. +2 −0  lib/matplotlib/backends/backend_tkagg.py
  25. +1 −0  lib/matplotlib/backends/backend_wx.py
  26. +2 −0  lib/matplotlib/backends/backend_wxagg.py
  27. +9 −1 lib/matplotlib/figure.py
View
11 doc/users/whats_new.rst
@@ -38,6 +38,17 @@ Features that depend on the Python Imaging Library, such as JPEG
handling, do not work, since the version of PIL for Python 3.x is not
sufficiently mature.
+Object-oriented interface
+-------------------------
+
+Damon McDougall has reduced some of the boilerplate code needed to interact
+with the object-oriented interface. Now a figure canvas is set up by default::
+
+ >>> from matplotlib.figure import Figure
+ >>> fig = Figure()
+ >>> ax = fig.add_subplot(1, 1, 1)
+ >>> fig.savefig('figure.pdf')
+
PGF/TikZ backend
----------------
Peter Würtz wrote a backend that allows matplotlib to export figures as
View
2  lib/matplotlib/backends/backend_agg.py
@@ -515,3 +515,5 @@ def print_to_buffer(self):
(int(renderer.width), int(renderer.height)))
renderer.dpi = original_dpi
return result
+
+FigureCanvas = FigureCanvasAgg
View
2  lib/matplotlib/backends/backend_cairo.py
@@ -512,3 +512,5 @@ def _save (self, fo, format, **kwargs):
ctx.show_page()
surface.finish()
+
+FigureCanvas = FigureCanvasCairo
View
2  lib/matplotlib/backends/backend_cocoaagg.py
@@ -89,7 +89,7 @@ def stop_event_loop(self):
FigureCanvasBase.stop_event_loop_default(self)
stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__
-
+FigureCanvas = FigureCanvasCocoaAgg
NibClassBuilder.extractClasses('Matplotlib.nib', mplBundle)
View
2  lib/matplotlib/backends/backend_emf.py
@@ -727,6 +727,8 @@ def print_emf(self, filename, dpi=300, **kwargs):
def get_default_filetype(self):
return 'emf'
+FigureCanvas = FigureCanvasEMF
+
class FigureManagerEMF(FigureManagerBase):
"""
Wrap everything up into a window for the pylab interface
View
2  lib/matplotlib/backends/backend_fltkagg.py
@@ -231,6 +231,8 @@ def stop_event_loop(self):
FigureCanvasBase.stop_event_loop_default(self)
stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__
+FigureCanvas = FigureCanvasFltkAgg
+
def destroy_figure(ptr, figman):
figman.window.hide()
Fltk.Fl.wait(0) # This is needed to make the last figure vanish.
View
4 lib/matplotlib/backends/backend_gdk.py
@@ -434,7 +434,7 @@ def new_figure_manager_given_figure(num, figure):
return manager
-class FigureCanvasGDK (FigureCanvasBase):
+class FigureCanvasGDK(FigureCanvasBase):
def __init__(self, figure):
FigureCanvasBase.__init__(self, figure)
@@ -472,3 +472,5 @@ def _print_image(self, filename, format, *args, **kwargs):
0, 0, 0, 0, width, height)
pixbuf.save(filename, format)
+
+FigureCanvas = FigureCanvasGDK
View
4 lib/matplotlib/backends/backend_gtk.py
@@ -144,7 +144,7 @@ def _on_timer(self):
return False
-class FigureCanvasGTK (gtk.DrawingArea, FigureCanvasBase):
+class FigureCanvasGTK(gtk.DrawingArea, FigureCanvasBase):
keyvald = {65507 : 'control',
65505 : 'shift',
65513 : 'alt',
@@ -519,6 +519,8 @@ def stop_event_loop(self):
FigureCanvasBase.stop_event_loop_default(self)
stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__
+FigureCanvas = FigureCanvasGTK
+
class FigureManagerGTK(FigureManagerBase):
"""
Public attributes
View
2  lib/matplotlib/backends/backend_gtk3.py
@@ -98,7 +98,7 @@ def _on_timer(self):
self._timer = None
return False
-class FigureCanvasGTK3 (Gtk.DrawingArea, FigureCanvasBase):
+class FigureCanvasGTK3(Gtk.DrawingArea, FigureCanvasBase):
keyvald = {65507 : 'control',
65505 : 'shift',
65513 : 'alt',
View
1  lib/matplotlib/backends/backend_gtk3agg.py
@@ -67,6 +67,7 @@ def print_png(self, filename, *args, **kwargs):
agg = self.switch_backends(backend_agg.FigureCanvasAgg)
return agg.print_png(filename, *args, **kwargs)
+FigureCanvas = FigureCanvasGTK3Agg
class FigureManagerGTK3Agg(backend_gtk3.FigureManagerGTK3):
pass
View
1  lib/matplotlib/backends/backend_gtk3cairo.py
@@ -34,6 +34,7 @@ def on_draw_event(self, widget, ctx):
return False # finish event propagation?
+FigureCanvas = FigureCanvasGTK3Cairo
class FigureManagerGTK3Cairo(backend_gtk3.FigureManagerGTK3):
pass
View
1  lib/matplotlib/backends/backend_gtkagg.py
@@ -33,6 +33,7 @@ def _get_toolbar(self, canvas):
toolbar = None
return toolbar
+FigureCanvas = FigureManagerGTKAgg
@jenshnielsen Owner

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

@dmcdougall Collaborator

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

@leejjoon Owner
leejjoon added a note

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

@dmcdougall Collaborator

Sorry! Fixed in #1201.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
def new_figure_manager(num, *args, **kwargs):
"""
View
1  lib/matplotlib/backends/backend_gtkcairo.py
@@ -55,6 +55,7 @@ def _renderer_init(self):
if _debug: print('%s.%s()' % (self.__class__.__name__, _fn_name()))
self._renderer = RendererGTKCairo (self.figure.dpi)
+FigureCanvas = FigureCanvasGTKCairo
class FigureManagerGTKCairo(FigureManagerGTK):
def _get_toolbar(self, canvas):
View
1  lib/matplotlib/backends/backend_macosx.py
@@ -352,6 +352,7 @@ def new_timer(self, *args, **kwargs):
"""
return TimerMac(*args, **kwargs)
+FigureCanvas = FigureCanvasMac
class FigureManagerMac(_macosx.FigureManager, FigureManagerBase):
"""
View
2  lib/matplotlib/backends/backend_pdf.py
@@ -2299,6 +2299,8 @@ def print_pdf(self, filename, **kwargs):
else: # we opened the file above; now finish it off
file.close()
+FigureCanvas = FigureCanvasPdf
+
class FigureManagerPdf(FigureManagerBase):
pass
View
2  lib/matplotlib/backends/backend_pgf.py
@@ -838,6 +838,8 @@ def _render_texts_pgf(self, fh):
def get_renderer(self):
return RendererPgf(self.figure, None)
+FigureCanvas = FigureCanvasPgf
+
class FigureManagerPgf(FigureManagerBase):
def __init__(self, *args):
FigureManagerBase.__init__(self, *args)
View
2  lib/matplotlib/backends/backend_ps.py
@@ -1357,6 +1357,8 @@ def write(self, *kl, **kwargs):
shutil.move(tmpfile, outfile)
os.chmod(outfile, mode)
+FigureCanvas = FigureCanvasPS
+
def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble,
paperWidth, paperHeight, orientation):
"""
View
2  lib/matplotlib/backends/backend_qt.py
@@ -203,6 +203,8 @@ def stop_event_loop(self):
FigureCanvasBase.stop_event_loop_default(self)
stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__
+FigureCanvas = FigureCanvasQT
+
class FigureManagerQT( FigureManagerBase ):
"""
Public attributes
View
2  lib/matplotlib/backends/backend_qt4.py
@@ -377,6 +377,8 @@ def idle_draw(*args):
self._idle = True
if d: QtCore.QTimer.singleShot(0, idle_draw)
+FigureCanvas = FigureCanvasQT
+
class FigureManagerQT( FigureManagerBase ):
"""
Public attributes
View
2  lib/matplotlib/backends/backend_qt4agg.py
@@ -145,3 +145,5 @@ def blit(self, bbox=None):
def print_figure(self, *args, **kwargs):
FigureCanvasAgg.print_figure(self, *args, **kwargs)
self.draw()
+
+FigureCanvas = FigureCanvasQTAgg
View
2  lib/matplotlib/backends/backend_qtagg.py
@@ -156,3 +156,5 @@ def blit(self, bbox=None):
def print_figure(self, *args, **kwargs):
FigureCanvasAgg.print_figure(self, *args, **kwargs)
self.draw()
+
+FigureCanvas = FigureCanvasQTAgg
View
2  lib/matplotlib/backends/backend_svg.py
@@ -1143,6 +1143,8 @@ def _print_svg(self, filename, svgwriter, fh_to_close=None, **kwargs):
def get_default_filetype(self):
return 'svg'
+FigureCanvas = FigureCanvasSVG
+
class FigureManagerSVG(FigureManagerBase):
pass
View
2  lib/matplotlib/backends/backend_template.py
@@ -245,6 +245,8 @@ def print_foo(self, filename, *args, **kwargs):
def get_default_filetype(self):
return 'foo'
+FigureCanvas = FigureCanvasTemplate
+
class FigureManagerTemplate(FigureManagerBase):
"""
Wrap everything up into a window for the pylab interface
View
2  lib/matplotlib/backends/backend_tkagg.py
@@ -505,6 +505,8 @@ def stop_event_loop(self):
FigureCanvasBase.stop_event_loop_default(self)
stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__
+FigureCanvas = FigureCanvasTkAgg
+
class FigureManagerTkAgg(FigureManagerBase):
"""
Public attributes
View
1  lib/matplotlib/backends/backend_wx.py
@@ -1400,6 +1400,7 @@ def _onEnter(self, evt):
"""Mouse has entered the window."""
FigureCanvasBase.enter_notify_event(self, guiEvent = evt)
+FigureCanvas = FigureCanvasWx
########################################################################
#
View
2  lib/matplotlib/backends/backend_wxagg.py
@@ -105,6 +105,8 @@ def print_figure(self, filename, *args, **kwargs):
if self._isDrawn:
self.draw()
+FigureCanvas = FigureCanvasWxAgg
+
class NavigationToolbar2WxAgg(NavigationToolbar2Wx):
def get_canvas(self, frame, fig):
return FigureCanvasWxAgg(frame, -1, fig)
View
10 lib/matplotlib/figure.py
@@ -318,7 +318,7 @@ def __init__(self,
self.patch.set_aa(False)
self._hold = rcParams['axes.hold']
- self.canvas = None
+ self.canvas = self._setup_canvas()
@pelson Collaborator
pelson added a note

This change means that I now get two TK widows popping up (one empty, one with a figure) with the following code:

import matplotlib
matplotlib.use('tkagg')
import matplotlib.pyplot as plt

plt.plot(range(10))
plt.show()

Presumably this is true of all other backends too. Looks like we might have to implement @astrofrog 's suggestion of making this togglable (and have pyplot toggle that toggler).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
if subplotpars is None:
subplotpars = SubplotParams()
@@ -330,6 +330,14 @@ def __init__(self,
self.clf()
self._cachedRenderer = None
+ def _setup_canvas(self):
+ """
+ Return the FigureCanvas instance defined by the currently loaded backend.
+ """
+ import matplotlib.backends as mbackends # lazy import
+ backend_mod = mbackends.pylab_setup()[0]
+ return backend_mod.FigureCanvas(self)
@pelson Collaborator
pelson added a note

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

Thanks

@verbit
verbit added a note

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.

@dmcdougall Collaborator

@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?

@verbit
verbit added a note

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.

@dmcdougall Collaborator

Fixed in #1214.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
def _get_axes(self):
return self._axstack.as_list()
Something went wrong with that request. Please try again.