From faafa59fa216861dff574717de37fdeeeb646cbe Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 20 Aug 2012 10:59:48 -0400 Subject: [PATCH 1/2] Add a "show" method to figures that don't have a GUI backend. Fixed #835. --- lib/matplotlib/backend_bases.py | 6 ++++++ lib/matplotlib/figure.py | 18 ++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 1f1a87bd33a3..2fcbe2807be4 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2433,6 +2433,12 @@ def __init__(self, canvas, num): """ + def show(self): + """ + For GUI backends, show the figure window and redraw. + """ + pass + def destroy(self): pass diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index bc16458061fa..56213c8b7dba 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -338,6 +338,20 @@ def _setup_canvas(self): backend_mod = mbackends.pylab_setup()[0] return backend_mod.FigureCanvas(self) + def show(self): + """ + If using a GUI backend, display the figure window. + + For non-GUI backends, this does nothing. + """ + manager = getattr(self.canvas, 'manager') + if manager is not None: + manager.show() + import warnings + warnings.warn( + "matplotlib is currently using a non-GUI backend, " + "so can not show the figure") + def _get_axes(self): return self._axstack.as_list() @@ -1188,10 +1202,10 @@ def __getstate__(self): # and re-attached to another. for attr_to_pop in ('_axobservers', 'show', 'canvas', '_cachedRenderer') : state.pop(attr_to_pop, None) - + # add version information to the state state['__mpl_version__'] = _mpl_version - + # check to see if the figure has a manager and whether it is registered # with pyplot if getattr(self.canvas, 'manager', None) is not None: From 51ffd4bb312575ce202bc82042c7634858b109e1 Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Sat, 1 Sep 2012 21:47:45 -1000 Subject: [PATCH 2/2] Remove figure.show monkeypatch from GUI backends. This also adds the warn kwarg to figure.show for non-gui backends. --- lib/matplotlib/backend_bases.py | 7 +++- lib/matplotlib/backends/backend_fltkagg.py | 2 +- lib/matplotlib/backends/backend_gtk.py | 9 ++--- lib/matplotlib/backends/backend_gtk3.py | 5 +-- lib/matplotlib/backends/backend_macosx.py | 3 -- lib/matplotlib/backends/backend_qt.py | 5 +-- lib/matplotlib/backends/backend_qt4.py | 3 -- lib/matplotlib/backends/backend_tkagg.py | 43 ++++++++++------------ lib/matplotlib/backends/backend_wx.py | 18 +++------ lib/matplotlib/figure.py | 22 +++++++---- 10 files changed, 52 insertions(+), 65 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 2fcbe2807be4..ac43ee0511ce 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2403,6 +2403,8 @@ def key_press_handler(event, canvas, toolbar=None): else: a.set_navigate(i==n) +class NonGuiException(Exception): + pass class FigureManagerBase: """ @@ -2436,8 +2438,11 @@ def __init__(self, canvas, num): 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. """ - pass + raise NonGuiException() def destroy(self): pass diff --git a/lib/matplotlib/backends/backend_fltkagg.py b/lib/matplotlib/backends/backend_fltkagg.py index d132e6489c89..a8215e4aa2cf 100644 --- a/lib/matplotlib/backends/backend_fltkagg.py +++ b/lib/matplotlib/backends/backend_fltkagg.py @@ -143,7 +143,7 @@ def handle(self, event): self._key=special_key[ikey] except: self._key=None - + # TODO: Handle ctrl, alt, super modifiers. FigureCanvasBase.key_press_event(self._source, self._key) return 1 diff --git a/lib/matplotlib/backends/backend_gtk.py b/lib/matplotlib/backends/backend_gtk.py index 3d107352f6f5..87ac0398a525 100644 --- a/lib/matplotlib/backends/backend_gtk.py +++ b/lib/matplotlib/backends/backend_gtk.py @@ -335,14 +335,14 @@ def _get_key(self, event): key = chr(event.keyval) else: key = None - + for key_mask, prefix in ( [gdk.MOD4_MASK, 'super'], - [gdk.MOD1_MASK, 'alt'], + [gdk.MOD1_MASK, 'alt'], [gdk.CONTROL_MASK, 'ctrl'],): if event.state & key_mask: key = '{}+{}'.format(prefix, key) - + return key def configure_event(self, widget, event): @@ -553,9 +553,6 @@ def __init__(self, canvas, num): self.canvas.show() - # attach a show method to the figure for pylab ease of use - self.canvas.figure.show = lambda *args: self.window.show() - self.vbox.pack_start(self.canvas, True, True) self.toolbar = self._get_toolbar(canvas) diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index 7e2fc585b1d9..3c65bc3f7c36 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -378,9 +378,6 @@ def __init__(self, canvas, num): self.canvas.show() - # attach a show method to the figure for pylab ease of use - self.canvas.figure.show = lambda *args: self.window.show() - self.vbox.pack_start(self.canvas, True, True, 0) self.toolbar = self._get_toolbar(canvas) @@ -564,7 +561,7 @@ def configure_subplots(self, button): window = Gtk.Window() - try: + try: window.set_icon_from_file(window_icon) except (SystemExit, KeyboardInterrupt): # re-raise exit type Exceptions diff --git a/lib/matplotlib/backends/backend_macosx.py b/lib/matplotlib/backends/backend_macosx.py index f06dd36ed4a7..9f64f967aec9 100644 --- a/lib/matplotlib/backends/backend_macosx.py +++ b/lib/matplotlib/backends/backend_macosx.py @@ -376,9 +376,6 @@ def notify_axes_change(fig): if self.toolbar != None: self.toolbar.update() self.canvas.figure.add_axobserver(notify_axes_change) - # This is ugly, but this is what tkagg and gtk are doing. - # It is needed to get ginput() working. - self.canvas.figure.show = lambda *args: self.show() if matplotlib.is_interactive(): self.show() diff --git a/lib/matplotlib/backends/backend_qt.py b/lib/matplotlib/backends/backend_qt.py index 094d5d68c684..8ea2c048d2cd 100644 --- a/lib/matplotlib/backends/backend_qt.py +++ b/lib/matplotlib/backends/backend_qt.py @@ -260,9 +260,6 @@ def __init__( self, canvas, num ): if matplotlib.is_interactive(): self.window.show() - # attach a show method to the figure for pylab ease of use - self.canvas.figure.show = lambda *args: self.window.show() - def notify_axes_change( fig ): # This will be called whenever the current axes is changed if self.toolbar != None: self.toolbar.update() @@ -330,7 +327,7 @@ def _init_toolbar( self ): continue fname = os.path.join(basedir, image_file + '.ppm') - image = qt.QPixmap() + image = qt.QPixmap() image.load( fname ) button = qt.QPushButton( qt.QIconSet( image ), "", self ) diff --git a/lib/matplotlib/backends/backend_qt4.py b/lib/matplotlib/backends/backend_qt4.py index 3ba2814424e7..0ff9a69d2a28 100644 --- a/lib/matplotlib/backends/backend_qt4.py +++ b/lib/matplotlib/backends/backend_qt4.py @@ -435,9 +435,6 @@ def __init__( self, canvas, num ): if matplotlib.is_interactive(): self.window.show() - # attach a show method to the figure for pylab ease of use - self.canvas.figure.show = lambda *args: self.window.show() - def notify_axes_change( fig ): # This will be called whenever the current axes is changed if self.toolbar is not None: diff --git a/lib/matplotlib/backends/backend_tkagg.py b/lib/matplotlib/backends/backend_tkagg.py index 2135e0183e4f..53d86f81cf87 100644 --- a/lib/matplotlib/backends/backend_tkagg.py +++ b/lib/matplotlib/backends/backend_tkagg.py @@ -85,7 +85,7 @@ def new_figure_manager_given_figure(num, figure): """ _focus = windowing.FocusManager() window = Tk.Tk() - + if Tk.TkVersion >= 8.5: # put a mpl icon on the window rather than the default tk icon. Tkinter # doesn't allow colour icons on linux systems, but tk >=8.5 has a iconphoto @@ -101,7 +101,7 @@ def new_figure_manager_given_figure(num, figure): except: # log the failure, but carry on verbose.report('Could not load matplotlib icon: %s' % sys.exc_info()[1]) - + canvas = FigureCanvasTkAgg(figure, master=window) figManager = FigureManagerTkAgg(canvas, num, window) if matplotlib.is_interactive(): @@ -199,7 +199,7 @@ class FigureCanvasTkAgg(FigureCanvasAgg): 65439 : 'dec', 65421 : 'enter', } - + _keycode_lookup = { 262145: 'control', 524320: 'alt', @@ -254,7 +254,7 @@ def filter_destroy(evt): self._master = master self._tkcanvas.focus_set() - + def resize(self, event): width, height = event.width, event.height if self._resize_callback is not None: @@ -443,31 +443,31 @@ def _get_key(self, event): key = chr(val) else: key = None - - # add modifier keys to the key string. Bit details originate from + + # add modifier keys to the key string. Bit details originate from # http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm - # BIT_SHIFT = 0x001; BIT_CAPSLOCK = 0x002; BIT_CONTROL = 0x004; - # BIT_LEFT_ALT = 0x008; BIT_NUMLOCK = 0x010; BIT_RIGHT_ALT = 0x080; + # BIT_SHIFT = 0x001; BIT_CAPSLOCK = 0x002; BIT_CONTROL = 0x004; + # BIT_LEFT_ALT = 0x008; BIT_NUMLOCK = 0x010; BIT_RIGHT_ALT = 0x080; # BIT_MB_1 = 0x100; BIT_MB_2 = 0x200; BIT_MB_3 = 0x400; - # In general, the modifier key is excluded from the modifier flag, - # however this is not the case on "darwin", so double check that + # In general, the modifier key is excluded from the modifier flag, + # however this is not the case on "darwin", so double check that # we aren't adding repeat modifier flags to a modifier key. - modifiers = [(6, 'super', 'super'), - (3, 'alt', 'alt'), + modifiers = [(6, 'super', 'super'), + (3, 'alt', 'alt'), (2, 'ctrl', 'control'), ] if sys.platform == 'darwin': - modifiers = [(3, 'super', 'super'), - (4, 'alt', 'alt'), + modifiers = [(3, 'super', 'super'), + (4, 'alt', 'alt'), (2, 'ctrl', 'control'), ] - + if key is not None: # note, shift is not added to the keys as this is already accounted for for bitmask, prefix, key_name in modifiers: - if event.state & (1 << bitmask) and key_name not in key: + if event.state & (1 << bitmask) and key_name not in key: key = '{}+{}'.format(prefix, key) - + return key def key_press(self, event): @@ -542,9 +542,6 @@ def notify_axes_change(fig): if self.toolbar != None: self.toolbar.update() self.canvas.figure.add_axobserver(notify_axes_change) - # attach a show method to the figure for pylab ease of use - self.canvas.figure.show = lambda *args: self.show() - def resize(self, width, height=None): # before 09-12-22, the resize method takes a single *event* # parameter. On the other hand, the resize method of other @@ -852,14 +849,14 @@ def _init_toolbar(self): for text, tooltip_text, image_file, callback in self.toolitems: if text is None: - # spacer, unhandled in Tk + # spacer, unhandled in Tk pass else: button = self._Button(text=text, file=image_file, command=getattr(self, callback)) if tooltip_text is not None: ToolTip.createToolTip(button, tooltip_text) - + self.message = Tk.StringVar(master=self) self._message_label = Tk.Label(master=self, textvariable=self.message) self._message_label.pack(side=Tk.RIGHT) @@ -954,7 +951,7 @@ def leave(event): toolTip.hidetip() widget.bind('', enter) widget.bind('', leave) - + def __init__(self, widget): self.widget = widget self.tipwindow = None diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index d76914aab072..628025362728 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -778,8 +778,8 @@ def Copy_to_Clipboard(self, event=None): "copy bitmap of canvas to system clipboard" bmp_obj = wx.BitmapDataObject() bmp_obj.SetBitmap(self.bitmap) - - if not wx.TheClipboard.IsOpened(): + + if not wx.TheClipboard.IsOpened(): open_success = wx.TheClipboard.Open() if open_success: wx.TheClipboard.SetData(bmp_obj) @@ -1251,7 +1251,7 @@ def _get_key(self, evt): key = None for meth, prefix in ( - [evt.AltDown, 'alt'], + [evt.AltDown, 'alt'], [evt.ControlDown, 'ctrl'], ): if meth(): key = '{}+{}'.format(prefix, key) @@ -1421,7 +1421,7 @@ def _create_wx_app(): # retain a reference to the app object so it does not get garbage # collected and cause segmentation faults _create_wx_app.theWxApp = wxapp - + def draw_if_interactive(): """ @@ -1521,7 +1521,7 @@ def __init__(self, num, fig): self.Fit() self.canvas.SetMinSize((2, 2)) - + # give the window a matplotlib icon rather than the stock one. # This is not currently working on Linux and is untested elsewhere. #icon_path = os.path.join(matplotlib.rcParams['datapath'], @@ -1603,12 +1603,6 @@ def notify_axes_change(fig): if self.tb != None: self.tb.update() self.canvas.figure.add_axobserver(notify_axes_change) - def showfig(*args): - frame.Show() - - # attach a show method to the figure - self.canvas.figure.show = showfig - def show(self): self.frame.Show() @@ -1882,7 +1876,7 @@ def save_figure(self, *args): os.path.join(dirname, filename), format=format) except Exception as e: error_msg_wx(str(e)) - + def set_cursor(self, cursor): cursor =wx.StockCursor(cursord[cursor]) self.canvas.SetCursor( cursor ) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 56213c8b7dba..eca962ba40a0 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -42,7 +42,7 @@ from matplotlib.text import Text, _process_text_args from matplotlib.transforms import (Affine2D, Bbox, BboxTransformTo, TransformedBbox) - +from matplotlib.backend_bases import NonGuiException docstring.interpd.update(projection_names = get_projection_names()) @@ -338,19 +338,25 @@ def _setup_canvas(self): backend_mod = mbackends.pylab_setup()[0] return backend_mod.FigureCanvas(self) - def show(self): + def show(self, warn=True): """ If using a GUI backend, display the figure window. - For non-GUI backends, this does nothing. + For non-GUI backends, this does nothing, in which case + a warning will be issued if *warn* is True. """ manager = getattr(self.canvas, 'manager') if manager is not None: - manager.show() - import warnings - warnings.warn( - "matplotlib is currently using a non-GUI backend, " - "so can not show the figure") + try: + manager.show() + return + except NonGuiException: + pass + if warn: + import warnings + warnings.warn( + "matplotlib is currently using a non-GUI backend, " + "so cannot show the figure") def _get_axes(self): return self._axstack.as_list()