diff --git a/lib/matplotlib/_pylab_helpers.py b/lib/matplotlib/_pylab_helpers.py index 2ead0e88cac8..3f3b00827b9b 100644 --- a/lib/matplotlib/_pylab_helpers.py +++ b/lib/matplotlib/_pylab_helpers.py @@ -9,6 +9,8 @@ import gc import atexit +from matplotlib import is_interactive + def error_msg(msg): print(msg, file=sys.stderr) @@ -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): """ @@ -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): """ @@ -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) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index edcc81f95f89..d3bbc843cb83 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -38,6 +38,7 @@ import warnings import time import io +import weakref import numpy as np import matplotlib.cbook as cbook @@ -132,6 +133,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. @@ -1664,7 +1692,7 @@ class FigureCanvasBase(object): register_backend('tiff', 'matplotlib.backends.backend_agg', 'Tagged Image File Format') - def __init__(self, figure): + def __init__(self, figure, manager=None): figure.set_canvas(self) self.figure = figure # a dictionary from event name to a dictionary that maps cid->func @@ -1678,6 +1706,7 @@ def __init__(self, figure): self.mouse_grabber = None # the axes currently grabbing mouse self.toolbar = None # NavigationToolbar2 will set me self._is_saving = False + self.manager = manager def is_saving(self): """ @@ -2422,6 +2451,19 @@ def stop_event_loop_default(self): """ self._looping = False + def destroy(self): + pass + + @property + def manager(self): + if self._manager is not None: + return self._manager() + + @manager.setter + def manager(self, manager): + if manager is not None: + self._manager = weakref.ref(manager) + def key_press_handler(event, canvas, toolbar=None): """ @@ -2461,7 +2503,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') @@ -2537,6 +2582,64 @@ class NonGuiException(Exception): pass +class WindowEvent(object): + def __init__(self, name, window): + self.name = name + self.window = window + + +class WindowBase(cbook.EventEmitter): + def __init__(self, title): + cbook.EventEmitter.__init__(self) + + def show(self): + """ + For GUI backends, show the figure window and redraw. + For non-GUI backends, raise an exception to be caught + by :meth:`~matplotlib.figure.Figure.show`, for an + optional warning. + """ + raise NonGuiException() + + def destroy(self): + pass + + def set_fullscreen(self, fullscreen): + pass + + def set_default_size(self, w, h): + self.resize(w, h) + + def resize(self, w, h): + """"For gui backends, resize the window (in pixels).""" + pass + + 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 'image' + + 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). + """ + pass + + def add_element_to_window(self, element, expand, fill, pad, side='bottom'): + """ Adds a gui widget to the window. + This has no effect for non-GUI backends + """ + pass + + def destroy_event(self, *args): + s = 'window_destroy_event' + event = WindowEvent(s, self) + self._callbacks.process(s, event) + + class FigureManagerBase(object): """ Helper class for pyplot mode, wraps everything up into a neat bundle diff --git a/lib/matplotlib/backend_managers.py b/lib/matplotlib/backend_managers.py new file mode 100644 index 000000000000..b248679965ce --- /dev/null +++ b/lib/matplotlib/backend_managers.py @@ -0,0 +1,130 @@ +from matplotlib import is_interactive +from matplotlib import rcParams +from matplotlib.figure import Figure +from matplotlib import cbook +from matplotlib.backend_bases import key_press_handler +from matplotlib.backends import get_backends +(FigureCanvas, Window, Toolbar2, MainLoop, + old_new_figure_manager) = get_backends() + + +class FigureManagerEvent(object): + def __init__(self, s, fm): + self.name = s + self.figure_manager = fm + + +class FigureManager(cbook.EventEmitter): + def __init__(self, figure, num): + cbook.EventEmitter.__init__(self) + self.num = num + + self.mainloop = MainLoop() + self.window = Window('Figure %d' % num) + self.window.mpl_connect('window_destroy_event', self._destroy) + + self.canvas = FigureCanvas(figure, manager=self) + + self.key_press_handler_id = self.canvas.mpl_connect('key_press_event', + self.key_press) + + 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, 'top') + + self.toolbar = self._get_toolbar() + if self.toolbar is not None: + h += self.window.add_element_to_window(self.toolbar, + False, False, 0, 'bottom') + + self.window.set_default_size(w, h) + + if 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) + + 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=None): + # Callback from the when the window wants to destroy itself + s = 'window_destroy_event' + event = FigureManagerEvent(s, self) + self._callbacks.process(s, event) + + def destroy(self, *args): + self.canvas.destroy() + if self.toolbar: + self.toolbar.destroy() + self.window.destroy() + + self.mainloop.__del__() + + 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 _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) + else: + toolbar = None + return toolbar + + def show_popup(self, msg): + """ + Display message in a popup -- GUI only + """ + pass + + +def new_figure_manager(num, *args, **kwargs): + """ + Create a new figure manager instance + """ + show = kwargs.pop('show', None) + if old_new_figure_manager is None: # Test if we can use the new code + FigureClass = kwargs.pop('FigureClass', Figure) + thisFig = FigureClass(*args, **kwargs) + manager = new_figure_manager_given_figure(num, thisFig) + else: # TODO remove once Gcf removed from backends. Default to old code. + manager = old_new_figure_manager(num, *args, **kwargs) + manager.mainloop = MainLoop + return manager + + +def new_figure_manager_given_figure(num, figure): + manager = FigureManager(figure, num) + return manager diff --git a/lib/matplotlib/backends/__init__.py b/lib/matplotlib/backends/__init__.py index cf80dc0f9ff5..f43366d923b3 100644 --- a/lib/matplotlib/backends/__init__.py +++ b/lib/matplotlib/backends/__init__.py @@ -14,18 +14,32 @@ 'new_figure_manager', 'backend_version'] backend = matplotlib.get_backend() # validates, to match all_backends +if backend.startswith('module://'): + backend_name = backend[9:] +else: + backend_name = 'matplotlib.backends.backend_' + backend.lower() + +def get_backends(): + _temp = __import__(backend_name, globals(), locals(), + ['Window', 'Toolbar2', 'FigureCanvas', 'MainLoop', + 'new_figure_manager'], 0) + FigureCanvas = _temp.FigureCanvas + try: + Window = _temp.Window + Toolbar2 = _temp.Toolbar2 + MainLoop = _temp.MainLoop + old_new_figure_manager = None + except AttributeError: + Window = None + Toolbar2 = None + MainLoop = getattr(_temp, 'show', do_nothing_show) + old_new_figure_manager = _temp.new_figure_manager + + return FigureCanvas, Window, Toolbar2, MainLoop, old_new_figure_manager def pylab_setup(): 'return new_figure_manager, draw_if_interactive and show for pylab' # Import the requested backend into a generic module object - - if backend.startswith('module://'): - backend_name = backend[9:] - else: - backend_name = 'backend_'+backend - backend_name = backend_name.lower() # until we banish mixed case - backend_name = 'matplotlib.backends.%s'%backend_name.lower() - # the last argument is specifies whether to use absolute or relative # imports. 0 means only perform absolute imports. backend_mod = __import__(backend_name, @@ -37,18 +51,10 @@ def pylab_setup(): # image backends like pdf, agg or svg do not need to do anything # for "show" or "draw_if_interactive", so if they are not defined # by the backend, just do nothing - def do_nothing_show(*args, **kwargs): - frame = inspect.currentframe() - fname = frame.f_back.f_code.co_filename - if fname in ('', ''): - warnings.warn(""" -Your currently selected backend, '%s' does not support show(). -Please select a GUI backend in your matplotlibrc file ('%s') -or with matplotlib.use()""" % - (backend, matplotlib.matplotlib_fname())) + def do_nothing(*args, **kwargs): pass backend_version = getattr(backend_mod,'backend_version', 'unknown') - show = getattr(backend_mod, 'show', do_nothing_show) + show = None if hasattr(backend_mod, 'show') else do_nothing_show draw_if_interactive = getattr(backend_mod, 'draw_if_interactive', do_nothing) # Additional imports which only happen for certain backends. This section @@ -60,3 +66,13 @@ def do_nothing(*args, **kwargs): pass matplotlib.verbose.report('backend %s version %s' % (backend,backend_version)) return backend_mod, new_figure_manager, draw_if_interactive, show + +def do_nothing_show(*args, **kwargs): + frame = inspect.currentframe() + fname = frame.f_back.f_code.co_filename + if fname in ('', ''): + warnings.warn(""" +Your currently selected backend, '%s' does not support show(). +Please select a GUI backend in your matplotlibrc file ('%s') +or with matplotlib.use()""" % + (backend, matplotlib.matplotlib_fname())) diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index 62622715322a..647344322b7e 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -28,8 +28,9 @@ def fn_name(): return sys._getframe(1).f_code.co_name import matplotlib from matplotlib._pylab_helpers import Gcf -from matplotlib.backend_bases import RendererBase, GraphicsContextBase, \ - FigureManagerBase, FigureCanvasBase, NavigationToolbar2, cursors, TimerBase +from matplotlib.backend_bases import (RendererBase, GraphicsContextBase, + FigureManagerBase, FigureCanvasBase, NavigationToolbar2, cursors, + TimerBase, WindowBase, MainLoopBase) from matplotlib.backend_bases import ShowBase from matplotlib.cbook import is_string_like, is_writable_file_like @@ -67,6 +68,17 @@ def draw_if_interactive(): if figManager is not None: figManager.canvas.draw_idle() + +class MainLoop(MainLoopBase): + def begin(self): + if Gtk.main_level() == 0: + Gtk.main() + + def end(self): + if Gtk.main_level() >= 1: + Gtk.main_quit() + + class Show(ShowBase): def mainloop(self): if Gtk.main_level() == 0: @@ -181,9 +193,9 @@ class FigureCanvasGTK3 (Gtk.DrawingArea, FigureCanvasBase): Gdk.EventMask.POINTER_MOTION_HINT_MASK| Gdk.EventMask.SCROLL_MASK) - def __init__(self, figure): + def __init__(self, figure, manager=None): if _debug: print('FigureCanvasGTK3.%s' % fn_name()) - FigureCanvasBase.__init__(self, figure) + FigureCanvasBase.__init__(self, figure, manager) GObject.GObject.__init__(self) self._idle_draw_id = 0 @@ -374,6 +386,66 @@ def stop_event_loop(self): FigureCanvasBase.stop_event_loop_default(self) stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__ +class WindowGTK3(WindowBase, Gtk.Window): + def __init__(self, title): + WindowBase.__init__(self, title) + Gtk.Window.__init__(self) + self.set_window_title(title) + + try: + self.set_icon_from_file(window_icon) + except (SystemExit, KeyboardInterrupt): + # re-raise exit type Exceptions + raise + except: + # some versions of gtk throw a glib.GError but not + # all, so I am not sure how to catch it. I am unhappy + # doing a blanket catch here, but am not sure what a + # better way is - JDH + verbose.report('Could not load matplotlib icon: %s' % sys.exc_info()[1]) + + self.vbox = Gtk.Box() + self.vbox.set_property('orientation', Gtk.Orientation.VERTICAL) + self.add(self.vbox) + self.vbox.show() + + self.connect('destroy', self.destroy_event) + self.connect('delete_event', self.destroy_event) + + def add_element_to_window(self, element, expand, fill, pad, side='bottom'): + element.show() + if side == 'top': + self.vbox.pack_start(element, expand, fill, pad) + elif side == 'bottom': + self.vbox.pack_end(element, expand, fill, pad) + else: + raise KeyError('Unknown value for side, %s' % side) + size_request = element.size_request() + return size_request.height + + def set_default_size(self, width, height): + Gtk.Window.set_default_size(self, width, height) + + def show(self): + # show the figure window + Gtk.Window.show(self) + + def destroy(self): + self.vbox.destroy() + Gtk.Window.destroy(self) + + def set_fullscreen(self, fullscreen): + if fullscreen: + self.fullscreen() + else: + self.unfullscreen() + + def get_window_title(self): + return self.get_title() + + def set_window_title(self, title): + self.set_title(title) + class FigureManagerGTK3(FigureManagerBase): """ diff --git a/lib/matplotlib/backends/backend_gtk3agg.py b/lib/matplotlib/backends/backend_gtk3agg.py index c3eb1da68be3..5bc4bdcf0afd 100644 --- a/lib/matplotlib/backends/backend_gtk3agg.py +++ b/lib/matplotlib/backends/backend_gtk3agg.py @@ -21,8 +21,8 @@ class FigureCanvasGTK3Agg(backend_gtk3.FigureCanvasGTK3, backend_agg.FigureCanvasAgg): - def __init__(self, figure): - backend_gtk3.FigureCanvasGTK3.__init__(self, figure) + def __init__(self, figure, manager=None): + backend_gtk3.FigureCanvasGTK3.__init__(self, figure, manager) self._bbox_queue = [] def _renderer_init(self): @@ -121,4 +121,7 @@ def new_figure_manager_given_figure(num, figure): FigureCanvas = FigureCanvasGTK3Agg FigureManager = FigureManagerGTK3Agg +Window = backend_gtk3.WindowGTK3 +Toolbar2 = backend_gtk3.NavigationToolbar2GTK3 +MainLoop = backend_gtk3.MainLoop show = backend_gtk3.show diff --git a/lib/matplotlib/backends/backend_gtk3cairo.py b/lib/matplotlib/backends/backend_gtk3cairo.py index da8f099be7f6..d00ebdebd9c6 100644 --- a/lib/matplotlib/backends/backend_gtk3cairo.py +++ b/lib/matplotlib/backends/backend_gtk3cairo.py @@ -22,8 +22,8 @@ def set_context(self, ctx): class FigureCanvasGTK3Cairo(backend_gtk3.FigureCanvasGTK3, backend_cairo.FigureCanvasCairo): - def __init__(self, figure): - backend_gtk3.FigureCanvasGTK3.__init__(self, figure) + def __init__(self, figure, manager=None): + backend_gtk3.FigureCanvasGTK3.__init__(self, figure, manager) def _renderer_init(self): """use cairo renderer""" @@ -72,4 +72,7 @@ def new_figure_manager_given_figure(num, figure): FigureCanvas = FigureCanvasGTK3Cairo FigureManager = FigureManagerGTK3Cairo +Window = backend_gtk3.WindowGTK3 +Toolbar2 = backend_gtk3.NavigationToolbar2GTK3 +MainLoop = backend_gtk3.MainLoop show = backend_gtk3.show diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index 652b47bcae16..b51fecc2273d 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -127,10 +127,10 @@ def bind(actor,event,action,id=None): import matplotlib from matplotlib import verbose -from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\ - FigureCanvasBase, FigureManagerBase, NavigationToolbar2, \ - cursors, TimerBase -from matplotlib.backend_bases import ShowBase +from matplotlib.backend_bases import (RendererBase, GraphicsContextBase, + FigureCanvasBase, FigureManagerBase, NavigationToolbar2, + cursors, TimerBase, WindowBase) +from matplotlib.backend_bases import MainLoopBase, ShowBase from matplotlib.backend_bases import _has_pil from matplotlib._pylab_helpers import Gcf @@ -701,7 +701,7 @@ class FigureCanvasWx(FigureCanvasBase, wx.Panel): wx.WXK_NUMPAD_DELETE : 'delete', } - def __init__(self, parent, id, figure): + def __init__(self, figure, id=-1, parent=None, manager=None): """ Initialise a FigureWx instance. @@ -710,8 +710,14 @@ def __init__(self, parent, id, figure): EVT_SIZE (Resize event) EVT_PAINT (Paint event) """ - - FigureCanvasBase.__init__(self, figure) + if manager is not None: + parent = manager.window + elif isinstance(parent, Figure) or isinstance(figure, wx.Window): + # User calls bad API, a deprecation warning should get issued here. + temp = parent + parent = figure + figure = temp + FigureCanvasBase.__init__(self, figure, manager) # Set preferred window size hint - helps the sizer (if one is # connected) l,b,w,h = figure.bbox.bounds @@ -1211,6 +1217,10 @@ def _onEnter(self, evt): """Mouse has entered the window.""" FigureCanvasBase.enter_notify_event(self, guiEvent = evt) + def destroy(self, *args, **kwargs): + pass # Destroy throws a sefault atm on ctrl+w if we destroy this. + #self.Destroy(*args, **kwargs) + ######################################################################## # @@ -1246,6 +1256,27 @@ def draw_if_interactive(): if figManager is not None: figManager.canvas.draw_idle() + +class MainLoop(MainLoopBase): + def __init__(self): + MainLoopBase.__init__(self) + wxapp = wx.GetApp() + if wxapp is None: + wxapp = wx.App(False) + wxapp.SetExitOnFrameDelete(True) + self._app = wxapp + + def begin(self): + needmain = not wx.App.IsMainLoopRunning() + if needmain: + wxapp = wx.GetApp() + if wxapp is not None: + wxapp.MainLoop() + + def end(self): + if hasattr(self, '_app'): + del self._app + class Show(ShowBase): def mainloop(self): needmain = not wx.App.IsMainLoopRunning() @@ -1283,6 +1314,52 @@ def new_figure_manager_given_figure(num, figure): return figmgr +class Window(WindowBase, wx.Frame): + def __init__(self, title): + # On non-Windows platform, explicitly set the position - fix + # positioning bug on some Linux platforms + if wx.Platform == '__WXMSW__': + pos = wx.DefaultPosition + else: + pos = wx.Point(20,20) + + WindowBase.__init__(self, title) + wx.Frame.__init__(self, parent=None, id=-1, pos=pos, title=title) + + self.sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.sizer) + + bind(self, wx.EVT_CLOSE, self.destroy_event) + + def add_element_to_window(self, element, expand, fill, pad, side='bottom'): + element.Fit() + self.sizer.Add(element, int(expand), wx.EXPAND) + w, h = element.GetSizeTuple() + if isinstance(element, NavigationToolbar2): + statbar = StatusBarWx(self) + self.SetStatusBar(statbar) + element.set_status_bar(statbar) + wsbar, hsbar = statbar.GetSizeTuple() + h += hsbar + return h + + def show(self): + self.Show() + + def destroy(self, *args, **kwargs): + self.Destroy(*args, **kwargs) + + def get_window_title(self): + return self.GetTitle() + + def set_window_title(self, title): + self.SetTitle(title) + + def resize(self, width, height): + #Set the canvas size in pixels + self.SetSize((width, height)) + + class FigureFrameWx(wx.Frame): def __init__(self, num, fig): # On non-Windows platform, explicitly set the position - fix @@ -1593,7 +1670,7 @@ def __init__(self, targetfig): class NavigationToolbar2Wx(NavigationToolbar2, wx.ToolBar): - def __init__(self, canvas): + def __init__(self, canvas, window=None): wx.ToolBar.__init__(self, canvas.GetParent(), -1) NavigationToolbar2.__init__(self, canvas) self.canvas = canvas @@ -1753,6 +1830,9 @@ def set_history_buttons(self): self.EnableTool(self.wx_ids['Back'], can_backward) self.EnableTool(self.wx_ids['Forward'], can_forward) + def destroy(self): + self.Destroy() + class StatusBarWx(wx.StatusBar): """ @@ -1863,3 +1943,4 @@ def OnPrintPage(self, page): FigureCanvas = FigureCanvasWx FigureManager = FigureManagerWx Toolbar = NavigationToolbar2Wx +Toolbar2 = NavigationToolbar2Wx diff --git a/lib/matplotlib/backends/backend_wxagg.py b/lib/matplotlib/backends/backend_wxagg.py index 5dd01030560b..f386d6cb6115 100644 --- a/lib/matplotlib/backends/backend_wxagg.py +++ b/lib/matplotlib/backends/backend_wxagg.py @@ -8,9 +8,9 @@ from .backend_agg import FigureCanvasAgg from . import backend_wx # already uses wxversion.ensureMinimal('2.8') -from .backend_wx import FigureManagerWx, FigureCanvasWx, \ - FigureFrameWx, DEBUG_MSG, NavigationToolbar2Wx, error_msg_wx, \ - draw_if_interactive, show, Toolbar, backend_version +from .backend_wx import (FigureManagerWx, FigureCanvasWx, Window, MainLoop, + FigureFrameWx, DEBUG_MSG, NavigationToolbar2Wx, error_msg_wx, + draw_if_interactive, show, Toolbar, backend_version) import wx @@ -105,7 +105,7 @@ def new_figure_manager(num, *args, **kwargs): # in order to expose the Figure constructor to the pylab # interface we need to create the figure here DEBUG_MSG("new_figure_manager()", 3, None) - backend_wx._create_wx_app() + backend_wx._create_wx_app() # TODO looks odd FigureClass = kwargs.pop('FigureClass', Figure) fig = FigureClass(*args, **kwargs) @@ -116,8 +116,8 @@ def new_figure_manager_given_figure(num, figure): """ Create a new figure manager instance for the given figure. """ - frame = FigureFrameWxAgg(num, figure) - figmgr = frame.get_figure_manager() + frame = FigureFrameWxAgg(num, figure) # TODO looks odd + figmgr = frame.get_figure_manager() # TODO again if matplotlib.is_interactive(): figmgr.frame.Show() return figmgr @@ -192,3 +192,4 @@ def _WX28_clipped_agg_as_bitmap(agg, bbox): FigureCanvas = FigureCanvasWxAgg FigureManager = FigureManagerWx +Toolbar2 = NavigationToolbar2WxAgg diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index c0f66c9f27cd..fbc020378a15 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -534,6 +534,17 @@ def process(self, s, *args, **kwargs): proxy(*args, **kwargs) +class EventEmitter(object): + def __init__(self): + self._callbacks = CallbackRegistry() + + def mpl_connect(self, s, func): + return self._callbacks.connect(s, func) + + def mpl_disconnect(self, cid): + return self._callbacks.disconnect(cid) + + class Scheduler(threading.Thread): """ Base class for timeout and idle scheduling diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 986870c78140..ec3024d4a900 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -1358,11 +1358,18 @@ def __setstate__(self, state): if restore_to_pylab: # lazy import to avoid circularity + # TODO clean on removal of Gcf from backends import matplotlib.pyplot as plt import matplotlib._pylab_helpers as pylab_helpers + import matplotlib.backend_managers as managers allnums = plt.get_fignums() num = max(allnums) + 1 if allnums else 1 - mgr = plt._backend_mod.new_figure_manager_given_figure(num, self) + if managers.old_new_figure_manager: + mgr = plt._backend_mod.new_figure_manager_given_figure(num, + self) + mgr.mainloop = plt._show + else: + mgr = managers.FigureManager(self, num) # XXX The following is a copy and paste from pyplot. Consider # factoring to pylab_helpers @@ -1377,7 +1384,7 @@ def make_active(event): mgr._cidgcf = mgr.canvas.mpl_connect('button_press_event', make_active) - pylab_helpers.Gcf.set_active(mgr) + pylab_helpers.Gcf.add_figure_manager(mgr) self.number = num plt.draw_if_interactive() diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index a13d87ee2ba0..00404f4cc026 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -31,6 +31,7 @@ from matplotlib.cbook import _string_to_bool from matplotlib import docstring from matplotlib.backend_bases import FigureCanvasBase +import matplotlib.backend_managers as backend_managers from matplotlib.figure import Figure, figaspect from matplotlib.gridspec import GridSpec from matplotlib.image import imread as _imread @@ -151,7 +152,10 @@ def show(*args, **kw): described above. """ global _show - return _show(*args, **kw) + if _show is None: + return _pylab_helpers.Gcf.show_all(*args, **kw) + else: + _show(*args, **kw) def isinteractive(): @@ -425,13 +429,14 @@ def figure(num=None, # autoincrement if None, else integer from 1-N if get_backend().lower() == 'ps': dpi = 72 - figManager = new_figure_manager(num, figsize=figsize, - dpi=dpi, - facecolor=facecolor, - edgecolor=edgecolor, - frameon=frameon, - FigureClass=FigureClass, - **kwargs) + figManager = backend_managers.new_figure_manager(num, figsize=figsize, + dpi=dpi, + facecolor=facecolor, + edgecolor=edgecolor, + frameon=frameon, + FigureClass=FigureClass, + show=_show, + **kwargs) if figLabel: figManager.set_window_title(figLabel) @@ -444,7 +449,7 @@ def make_active(event): cid = figManager.canvas.mpl_connect('button_press_event', make_active) figManager._cidgcf = cid - _pylab_helpers.Gcf.set_active(figManager) + _pylab_helpers.Gcf.add_figure_manager(figManager) figManager.canvas.figure.number = num draw_if_interactive() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy