Skip to content

MEP27 Decouple pyplot from backends (refactoring Gcf out of backend code) #4143

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

Closed
wants to merge 55 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
24caf2b
Refactor pass 1. Refactoring Gcf out of specific backend (backend_gt…
OceanWolf Feb 21, 2015
2b05d38
Refactor Pass 2. Refactored Gcf out of all backend code.
OceanWolf Feb 22, 2015
f4fc354
Quick fix to figure for safe unpickling.
OceanWolf Feb 25, 2015
0b31e3a
GTK3Agg
OceanWolf Feb 26, 2015
6fb452e
Refactored making `FigureManager` a *figure* manager, plus added miss…
OceanWolf Feb 27, 2015
0a868a2
keyword
OceanWolf Feb 27, 2015
61ba2b4
Make add_element more general, and make sure the code complies with it.
OceanWolf Feb 27, 2015
f0eb84c
Better destroy order.
OceanWolf Feb 27, 2015
8fe9cd7
GTK simplifications
OceanWolf Feb 28, 2015
494e5f1
Added doc and cleaned backend_managers, don't want our new file dirty.
OceanWolf Mar 3, 2015
ed16178
Improve layout!
OceanWolf Apr 6, 2015
21b8f58
Move knowledge of the backend to the manager.
OceanWolf Apr 7, 2015
cf42e3b
Incorporate MEP22 into MEP27
OceanWolf Apr 12, 2015
f027b16
Improved new toolbar and updated tool_manager example accoridingly.
OceanWolf Apr 12, 2015
24b7b73
fullscreen
OceanWolf Apr 13, 2015
bc99129
MEP update
OceanWolf Apr 14, 2015
7f7f05e
Finish MEP22 conversion
OceanWolf Apr 14, 2015
713abcb
rename window method
OceanWolf Apr 17, 2015
80adaaf
Add backend anme to widgets
OceanWolf Apr 17, 2015
4e5f69d
Handle FigureManager destroy internaly without pyplot.
OceanWolf Jun 4, 2015
160ef57
Make functionality more consistant for embedded applications
OceanWolf Jun 7, 2015
b6d6acc
Backend getter method for FigureManager
OceanWolf Jun 13, 2015
ecd5038
Improve example after new method
OceanWolf Jun 13, 2015
f8e83fe
Clean up the code a bit
OceanWolf Jun 13, 2015
c53b79a
Remove old code from backend_managers
OceanWolf Jun 15, 2015
34c6b12
Cleanup
OceanWolf Jun 22, 2015
8a4268a
Explicity get set manager as None if appropiate.
OceanWolf Jun 22, 2015
44df199
figure attribute and canvas property
fariza Jun 22, 2015
860a8ed
Fix FigureCanvasBase
OceanWolf Jun 23, 2015
c44e744
super
OceanWolf Jun 25, 2015
2fe9215
figure setter
fariza Jun 25, 2015
3b434ef
Improve MEP22 Tool Searching Structure
OceanWolf Jun 25, 2015
490629f
adding example file
fariza Jun 25, 2015
8eb987b
super dooper
OceanWolf Jun 26, 2015
224a4f3
Revert old example and fix new one.
OceanWolf Jun 26, 2015
cdbd51b
Improve MEP22 tool-searching method.
OceanWolf Jun 27, 2015
50e3719
MEP22 Save Figure Tool
OceanWolf Jun 27, 2015
85be519
pep8
OceanWolf Jun 27, 2015
7edaf5a
Make ToolConfigureSubplots a generic tool
OceanWolf Jun 28, 2015
8e6e252
Improve flow handling and make it a lot more generic
OceanWolf Jun 28, 2015
72575cb
Missing resize method
OceanWolf Jun 28, 2015
4a78246
Convert to new structure for finding tools
OceanWolf Jun 28, 2015
e300707
doc
OceanWolf Jun 29, 2015
ee76451
Add ExpandableBase
OceanWolf Jun 29, 2015
6f0c7ab
Template Backend plus fix FigManager for non-GUI backends and add gen…
OceanWolf Jun 29, 2015
ae9bf5b
rcParam and Travis
OceanWolf Jul 19, 2015
fb004e0
test
OceanWolf Jul 20, 2015
1d2095b
test always MEP27
OceanWolf Jul 20, 2015
24e43b3
Fix FigureManager to allow pyplot to work for non GUI backends
OceanWolf Jul 27, 2015
208c3be
Fix Gcf.show_all()
OceanWolf Jul 27, 2015
a44ebd9
doc
OceanWolf Jul 27, 2015
f8f9cf2
pep8
OceanWolf Jul 27, 2015
0e09a54
remove show_popup
OceanWolf Jul 27, 2015
a38b6d7
AttributeError
OceanWolf Sep 22, 2015
64f0c61
Fixes for MEP27
OceanWolf Aug 3, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move knowledge of the backend to the manager.
  • Loading branch information
OceanWolf committed Aug 3, 2016
commit 21b8f58dede8193b0b01649f34099f60b8c89628
12 changes: 6 additions & 6 deletions lib/matplotlib/backend_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
from matplotlib import rcParams
from matplotlib.figure import Figure
from matplotlib.backend_bases import key_press_handler
from matplotlib.backends import get_backends
FigureCanvas, Window, Toolbar2, MainLoop = get_backends()
from matplotlib.backends import get_backend


class FigureManagerEvent(object):
Expand Down Expand Up @@ -72,11 +71,12 @@ def __init__(self, figure, num):
cbook.EventEmitter.__init__(self)
Copy link
Member

Choose a reason for hiding this comment

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

This should use super

Copy link
Member Author

Choose a reason for hiding this comment

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

Any reason for using super()? Personally I find it more clunky and difficult to read/modify, especially in multiple inheritance, calling it this way we have fine control making it explicitly clear which class you call.

Copy link
Member

Choose a reason for hiding this comment

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

because super is the right way to deal with MI. If you do it by hand there are ways for it to not call all of the base classes it needs to or call some base classes more than once. Don't re-invent what Guido has already done for us 😉

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay, been a tough battle but now I think I understand super, I saw it as only a top down thing, not a sideways thing... they should really rename super as, at least to me, it implies parent when it doesn't work like that at all... wow, mind blown! This will take some time for me to get accustomed to... 😄

Copy link
Member

Choose a reason for hiding this comment

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

My in to understanding 'super' was to consider the mro of some classes with nasty MI. That is what turns the sidways thing into the parent thing.

Copy link
Member

Choose a reason for hiding this comment

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

How many classes does that touch? One of the issues with super is that like const in c++ or the GPL it is viral.

Copy link
Member Author

Choose a reason for hiding this comment

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

They derive just from the MPL base and from the backend GUI component.

Yes, I see super() viral nature, one of the reasons which makes we wary of using it, but you implied in the Qt branch that you thought we should use it everywhere... I just want to make sure if we use it, we use it properly, super really works all or nothing (and I do like it because of diamond inheritance). I think I still need to do a few plays with it to fully grasp the paradigm shift in using it with arguments...

Copy link
Member

Choose a reason for hiding this comment

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

@tacaswell, regarding your earlier comment about "understanding 'super' was to consider the mro of some classes with nasty MI": to me this doesn't necessarily imply that we should be plastering "super" all over the place; we should be tying hard to avoid "nasty MI". I'm always worried about the code base getting harder and harder to understand. Sometimes I'm close to the point of giving up, and saying that as an amateur, I just can't handle it any more. Many times when I plunge in to try to figure out a problem, it takes too long. Each fancy trick, each complicated inheritance tree, makes it less likely that a given mpl user will be able to submit a PR fixing a bug, instead of merely reporting the bug. OK, enough tirade. I learned to (sort of) like decorators; maybe I will get used to "super" if we end up relying on it.

Copy link
Member

Choose a reason for hiding this comment

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

I agree we should avoid MI, but in the case of the gui backends we already
have it and using super as it is designed should make that code
simpler/easier to understand (and make the cases where we are using single
inheritance a bit more robust to refactoring and is defensive programming.
I think that the improvements to super in 3 are one of the biggest selling
points).

It is my sense that the hardest to understand code we have comes from when
we try to avoid using language features (or it was written before the
feature was added) and do something in a 'simpler' way which ends up being
many more lines of brittle code.

At it's core all super is doing is saying 'try to find this thing one layer
up in the mro' which, while convenient, does not really matter unless you
have MI with diamonds (other than object).

On Wed, Jun 24, 2015 at 2:15 PM Eric Firing notifications@github.com
wrote:

In lib/matplotlib/backend_managers.py
#4143 (comment):


  • canvas : matplotlib.backend_bases.FigureCanvasBase
  •    The GUI element on which we draw.
    
  • toolbar : matplotlib.backend_bases.NavigationToolbar2
  •    The toolbar used for interacting with the figure.
    
  • window : matplotlib.backend_bases.WindowBase
  •    The window that holds the canvas and toolbar.
    
  • num : int
  •    The figure number.
    
  • """
  • def init(self, figure, num):
  •    cbook.EventEmitter.**init**(self)
    

@tacaswell https://github.com/tacaswell, regarding your earlier comment
about "understanding 'super' was to consider the mro of some classes with
nasty MI": to me this doesn't necessarily imply that we should be
plastering "super" all over the place; we should be tying hard to avoid
"nasty MI". I'm always worried about the code base getting harder and
harder to understand. Sometimes I'm close to the point of giving up, and
saying that as an amateur, I just can't handle it any more. Many times when
I plunge in to try to figure out a problem, it takes too long. Each fancy
trick, each complicated inheritance tree, makes it less likely that a given
mpl user will be able to submit a PR fixing a bug, instead of merely
reporting the bug. OK, enough tirade. I learned to (sort of) like
decorators; maybe I will get used to "super" if we end up relying on it.


Reply to this email directly or view it on GitHub
https://github.com/matplotlib/matplotlib/pull/4143/files#r33178070.

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 empathise a lot with what you say @efiring. I definitely felt intimidated by the code base when I reported my first bug (#2616), I did a bit of initial digging and decided that that part of the codebase looked way too verbose, "spaghetti code", so yes, I just reported the bug and left. Part of the reason for me wanting to work on these MEPs comes from the desire to make the codebase easier to understand, de-spaghettifi the code.

Decorators appear next on my list of things to learn, I also agree they look complicated. Atm I treat them as voodoo, and as a sort of an embellishment to the code, but that has happened time and again with python (coming from the world of php/java/c) with things list comprehension etcetera, so I see decorators and super in amongst them.

The more I learn about super, the more I like it, on the one hand I agree, it has a learning curve to it and that may put people off, on the other hand, if people just see it as voodoo and trust that it works (at least to begin with) and makes the code structure generally clearer then it may actually lower the boundaries to code contribution.

I found https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ which helped to convert me, along with experimenting with some of the examples from there (don't worry most of the page gets filled up with comments, so much shorter then it looks).

self.num = num

self._mainloop = MainLoop()
self.window = Window('Figure %d' % num)
self._backend = get_backend()
self._mainloop = self._backend.MainLoop()
self.window = self._backend.Window('Figure %d' % num)
self.window.mpl_connect('window_destroy_event', self._destroy)

self.canvas = FigureCanvas(figure, manager=self)
self.canvas = self._backend.FigureCanvas(figure, manager=self)

self.key_press_handler_id = self.canvas.mpl_connect('key_press_event',
self.key_press)
Expand Down Expand Up @@ -158,7 +158,7 @@ def _get_toolbar(self):
# must be inited after the window, drawingArea and figure
# attrs are set
if rcParams['toolbar'] == 'toolbar2':
toolbar = Toolbar2(self.canvas, self.window)
toolbar = self._backend.Toolbar2(self.canvas, self.window)
else:
toolbar = None
return toolbar
Expand Down
18 changes: 3 additions & 15 deletions lib/matplotlib/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,10 @@ def get_backend_name(name=None):
return backend_name


def get_backends():
def get_backend():
Copy link
Member

Choose a reason for hiding this comment

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

@tacaswell @OceanWolf what about moving this a little bit up and making it a property matplotlib.backend

Copy link
Member

Choose a reason for hiding this comment

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

Latter on, if we get it right, we can add a setter, and remove the matplotlib.use and even better, if get it really right, we can make the backends to be runtime swappable .

Copy link
Member

Choose a reason for hiding this comment

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

don't fully follow what you mean.

Copy link
Member

Choose a reason for hiding this comment

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

the matplotlib.backends.get_backend method can be converted to a property as matplotlib.backend
For now only the getter, but later we can transform matplotlib.use to the setter.

Copy link
Member Author

Choose a reason for hiding this comment

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

@fariza yes, I put the setter as the basic idea... At the moment we have matplotlib.pyplot.switch_backend()

I have written the code with the possibility to:

  1. Add a setter method for FigureManager.backend so we can apply the switch_backend() code to a specific figure.
  2. use the pyplot method to switch the backend only for future backends.

Not too convinced about the need to move the module method get_backend up further up, as we already have switch_backend

Copy link
Member

Choose a reason for hiding this comment

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

I am still confused. Are you suggesting putting a property on the top-level mpl module?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, why not?
On 27 Jun 2015 7:02 pm, "Thomas A Caswell" notifications@github.com wrote:

In lib/matplotlib/backends/init.py
#4143 (comment):

-def pylab_setup():

  • 'return new_figure_manager, draw_if_interactive and show for pylab'
    +def get_backend():

I am still confused. Are you suggesting putting a property on the
top-level mpl module?


Reply to this email directly or view it on GitHub
https://github.com/matplotlib/matplotlib/pull/4143/files#r33417288.

Copy link
Member

Choose a reason for hiding this comment

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

For one thing, doing that requires doing some non-standard things to sys.module (http://stackoverflow.com/questions/880530/can-python-modules-have-properties-the-same-way-that-objects-can).

Copy link
Member

Choose a reason for hiding this comment

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

OK. That might be a problem.
But still worth to think about
On 27 Jun 2015 7:19 pm, "Thomas A Caswell" notifications@github.com wrote:

In lib/matplotlib/backends/init.py
#4143 (comment):

-def pylab_setup():

  • 'return new_figure_manager, draw_if_interactive and show for pylab'
    +def get_backend():

For one thing, doing that requires doing some non-standard things to
sys.module (
http://stackoverflow.com/questions/880530/can-python-modules-have-properties-the-same-way-that-objects-can
).


Reply to this email directly or view it on GitHub
https://github.com/matplotlib/matplotlib/pull/4143/files#r33417385.

Copy link
Member Author

Choose a reason for hiding this comment

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

Any objections to just making/marking this module method as private module method and returning to this in a later PR/issue?

backend_name = get_backend_name()
_temp = __import__(backend_name, globals(), locals(),
['Window', 'Toolbar2', 'FigureCanvas', 'MainLoop'], 0)
try:
Window = _temp.Window
Toolbar2 = _temp.Toolbar2
FigureCanvas = _temp.FigureCanvas
MainLoop = _temp.MainLoop
except AttributeError:
Window = None
Toolbar2 = None
FigureCanvas = None
MainLoop = getattr(_temp, 'show', do_nothing_show)

return FigureCanvas, Window, Toolbar2, MainLoop
return __import__(backend_name, globals(), locals(),
[backend_name], 0)


def pylab_setup(name=None):
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1562,7 +1562,7 @@ def __setstate__(self, state):
import matplotlib.backend_managers as managers
allnums = plt.get_fignums()
num = max(allnums) + 1 if allnums else 1
if managers.Window is not None: # Can we use the new code?
if hasattr(plt._backend_mod, 'Window'): # Can we use MEP 27 code?
Copy link
Member

Choose a reason for hiding this comment

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

why not use the rcParams?

Copy link
Member Author

Choose a reason for hiding this comment

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

will do, see OceanWolf#4 remember ;).

mgr = managers.FigureManager(self, num)
else:
mgr = plt._backend_mod.new_figure_manager_given_figure(num,
Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def show(*args, **kw):
described above.
"""
global _show
if backend_managers.Window is not None: # Can we use the new code?
if hasattr(_backend_mod, 'Window'): # Can we use the new code?
return _pylab_helpers.Gcf.show_all(*args, **kw)
else:
_show(*args, **kw)
Copy link
Member

Choose a reason for hiding this comment

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

missing a return?

Expand Down Expand Up @@ -528,7 +528,7 @@ def figure(num=None, # autoincrement if None, else integer from 1-N
if get_backend().lower() == 'ps':
dpi = 72

if backend_managers.Window is not None: # Can we use the new code?
if hasattr(_backend_mod, 'Window'): # Can we use the MEP 27 code?
Copy link
Member

Choose a reason for hiding this comment

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

@tacaswell how do you see this way of getting the new figure manager?
Personally I have never liked the new_figure_manager and new_figure_manager_given_figure methods, but having the mechanics in two separate places seems even less natural than those two awkward methods

fig = FigureClass(figsize=figsize, dpi=dpi, facecolor=facecolor,
edgecolor=edgecolor, frameon=frameon, **kwargs)
figManager = backend_managers.FigureManager(fig, num)
Expand Down