8000 Backend refactor qt by tacaswell · Pull Request #5 · OceanWolf/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Backend refactor qt #5

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

Open
wants to merge 10 commits into
base: backend-refactor
Choose a base branch
from
Prev Previous commit
Next Next commit
Refactor Pass 2. Refactored Gcf out of all backend code.
  • Loading branch information
OceanWolf committed Feb 26, 2015
commit 526c2b4e15801811aef3a728b99384741ddae2c4
59 changes: 58 additions & 1 deletion lib/matplotlib/_pylab_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import gc
import atexit

from matplotlib import is_interactive


def error_msg(msg):
print(msg, file=sys.stderr)
Expand All @@ -35,6 +37,16 @@ class Gcf(object):
_activeQue = []
figs = {}

@classmethod
def add_figure_manager(cls, manager):
cls.figs[manager.num] = manager
try: # TODO remove once all backends converted to use the new manager.
manager.mpl_connect('window_destroy_event', cls.destroy_cbk)
except:
pass

cls.set_active(manager)

@classmethod
def get_fig_manager(cls, num):
"""
Expand All @@ -46,6 +58,49 @@ def get_fig_manager(cls, num):
cls.set_active(manager)
return manager

@classmethod
def show_all(cls, block=None):
"""
Show all figures. If *block* is not None, then
it is a boolean that overrides all other factors
determining whether show blocks by calling mainloop().
The other factors are:
it does not block if run inside ipython's "%pylab" mode
it does not block in interactive mode.
"""
managers = cls.get_all_fig_managers()
if not managers:
return

for manager in managers:
manager.show()

if block is not None:
if block:
manager.mainloop()
return

from matplotlib import pyplot
try:
ipython_pylab = not pyplot.show._needmain
# IPython versions >= 0.10 tack the _needmain
# attribute onto pyplot.show, and always set
# it to False, when in %pylab mode.
ipython_pylab = ipython_pylab and get_backend() != 'WebAgg'
# TODO: The above is a hack to get the WebAgg backend
# working with ipython's `%pylab` mode until proper
# integration is implemented.
except AttributeError:
ipython_pylab = False

# Leave the following as a separate step in case we
# want to control this behavior with an rcParam.
if ipython_pylab:
block = False

if not is_interactive() or get_backend() == 'WebAgg':
manager.mainloop()

@classmethod
def destroy(cls, num):
"""
Expand Down Expand Up @@ -134,7 +189,9 @@ def set_active(cls, manager):
if m != manager:
cls._activeQue.append(m)
cls._activeQue.append(manager)
cls.figs[manager.num] = manager

@classmethod
def destroy_cbk(cls, event):
cls.destroy(event.figure_manager.num)

atexit.register(Gcf.destroy_all)
145 changes: 35 additions & 110 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import io

import numpy as np
import matplotlib # temporary )assuming we refactor where marked below)
import matplotlib.cbook as cbook
import matplotlib.colors as colors
import matplotlib.transforms as transforms
Expand Down Expand Up @@ -133,6 +132,33 @@ def get_registered_canvas_class(format):
return backend_class


class MainLoopBase(object):
"""This gets used as a key maintaining the event loop.
Backends should only need to override begin and end.
It should not matter if this gets used as a singleton or not due to
clever magic.
"""
_instance_count = {}
def __init__(self):
MainLoopBase._instance_count.setdefault(self.__class__, 0)
MainLoopBase._instance_count[self.__class__] += 1

def begin(self):
pass

def end(self):
pass

def __call__(self):
self.begin()

def __del__(self):
MainLoopBase._instance_count[self.__class__] -= 1
if (MainLoopBase._instance_count[self.__class__] <= 0 and
not is_interactive()):
self.end()


class ShowBase(object):
"""
Simple base class to generate a show() callable in backends.
Expand Down Expand Up @@ -2462,7 +2488,10 @@ def key_press_handler(event, canvas, toolbar=None):

# quit the figure (defaut key 'ctrl+w')
if event.key in quit_keys:
Gcf.destroy_fig(canvas.figure)
if isinstance(canvas.manager.mainloop, MainLoopBase): # If new no Gcf.
canvas.manager._destroy('window_destroy_event')
else:
Gcf.destroy_fig(canvas.figure)

if toolbar is not None:
# home or reset mnemonic (default key 'h', 'home' and 'r')
Expand Down Expand Up @@ -2537,20 +2566,16 @@ def key_press_handler(event, canvas, toolbar=None):
class NonGuiException(Exception):
pass


class WindowEvent(object):
def __init__(self, name, window):
self.name = name
self.window = window

class WindowBase(object):
def __init__(self, title):
self._callbacks = cbook.CallbackRegistry()

def mpl_connect(self, s, func):
return self._callbacks.connect(s, func)

def mpl_disconnect(self, cid):
return self._callbacks.disconnect(cid)
class WindowBase(cbook.EventEmitter):
def __init__(self, title):
cbook.EventEmitter.__init__(self)

def show(self):
"""
Expand Down Expand Up @@ -2591,112 +2616,12 @@ def add_element_to_window(self, element, expand, fill, padding, from_start=False
"""
pass

def terminate_backend(self):
"""Method to terminate the usage of the backend
"""
# TODO refactor me out on second pass
pass

def destroy_event(self, *args):
s = 'window_destroy_event'
event = WindowEvent(s, self)
self._callbacks.process(s, event)


class FigureManager(object):
def __init__(self, canvas, num, classes):
self._classes = classes
self.canvas = canvas
canvas.manager = self
self.num = num

self.key_press_handler_id = self.canvas.mpl_connect('key_press_event',
self.key_press)

self.window = classes['Window']('Figure %d' % num)
self.window.mpl_connect('window_destroy_event', self._destroy)

w = int(self.canvas.figure.bbox.width)
h = int(self.canvas.figure.bbox.height)

self.window.add_element_to_window(self.canvas, True, True, 0, True)

self.toolbar = self._get_toolbar(canvas)
if self.toolbar is not None:
h += self.window.add_element_to_window(self.toolbar, False, False, 0)

self.window.set_default_size(w,h)

# Refactor this? If so, delete import matplotlib from above.
if matplotlib.is_interactive():
self.window.show()

def notify_axes_change(fig):
'this will be called whenever the current axes is changed'
if self.toolbar is not None: self.toolbar.update()
self.canvas.figure.add_axobserver(notify_axes_change)

self.canvas.grab_focus()

def key_press(self, event):
"""
Implement the default mpl key bindings defined at
:ref:`key-event-handling`
"""
key_press_handler(event, self.canvas, self.canvas.toolbar)

def _destroy(self, event):
Gcf.destroy(self.num) # TODO refactor me out of here on second pass!

def destroy(self, *args):
self.window.destroy()
self.canvas.destroy()
if self.toolbar:
self.toolbar.destroy()

# TODO refactor out on second pass
if Gcf.get_num_fig_managers()==0 and not matplotlib.is_interactive():
self.window.terminate_backend()

def show(self):
self.window.show()

def full_screen_toggle(self):
self._full_screen_flag = not self._full_screen_flag
self.window.set_fullscreen(self._full_screen_flag)

def resize(self, w, h):
self.window.resize(w,h)

def get_window_title(self):
"""
Get the title text of the window containing the figure.
Return None for non-GUI backends (e.g., a PS backend).
"""
return self.window.get_window_title()

def set_window_title(self, title):
"""
Set the title text of the window containing the figure. Note that
this has no effect for non-GUI backends (e.g., a PS backend).
"""
self.window.set_window_title(title)

def show_popup(self, msg):
"""
Display message in a popup -- GUI only
"""
pass

def _get_toolbar(self, canvas):
# must be inited after the window, drawingArea and figure
# attrs are set
if rcParams['toolbar'] == 'toolbar2':
toolbar = self._classes['Toolbar2'](canvas, self.window)
else:
toolbar = None
return toolbar

class FigureManagerBase(object):
"""
Helper class for pyplot mode, wraps everything up into a neat bundle
Expand Down
Loading
0