From eba484b12a77eb2c35968e5b0d1a9bf435b46c8f Mon Sep 17 00:00:00 2001 From: DietmarSchwertberger Date: Tue, 9 Jan 2018 20:04:29 +0100 Subject: [PATCH 1/4] implement wx tools for ToolManager: SaveFigure, SetCursor, Rubberband --- lib/matplotlib/backends/backend_wx.py | 142 +++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index 0aabab7bc098..66d0da759d97 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -17,6 +17,7 @@ unicode_literals) from six.moves import xrange +import six import sys import os @@ -37,7 +38,7 @@ from matplotlib.path import Path from matplotlib.transforms import Affine2D from matplotlib.widgets import SubplotTool -from matplotlib import cbook, rcParams +from matplotlib import cbook, rcParams, backend_tools from . import wx_compat as wxc import wx @@ -1716,6 +1717,145 @@ def set_function(self, string): # self.SetStatusText("Measurement: %s" % string, 2) +# tools for matplotlib.backend_managers.ToolManager: +# for now only SaveFigure, SetCursor and Rubberband are implemented +# once a ToolbarWx is implemented, also FigureManagerWx needs to be +# modified, similar to pull request #9934 + +class SaveFigureWx(backend_tools.SaveFigureBase): + def trigger(self, *args): + # Fetch the required filename and file type. + filetypes, exts, filter_index = self.canvas._get_imagesave_wildcards() + default_dir = os.path.expanduser( + matplotlib.rcParams['savefig.directory']) + default_file = self.canvas.get_default_filename() + dlg = wx.FileDialog(self.canvas.GetTopLevelParent(), "Save to file", + default_dir, default_file, filetypes, + wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) + dlg.SetFilterIndex(filter_index) + if dlg.ShowModal() != wx.ID_OK: + return + + dirname = dlg.GetDirectory() + filename = dlg.GetFilename() + DEBUG_MSG( 'Save file dir:%s name:%s' % (dirname, filename), 3, self) + format = exts[dlg.GetFilterIndex()] + basename, ext = os.path.splitext(filename) + if ext.startswith('.'): + ext = ext[1:] + if ext in ('svg', 'pdf', 'ps', 'eps', 'png') and format != ext: + # looks like they forgot to set the image type drop + # down, going with the extension. + warnings.warn( + 'extension %s did not match the selected ' + 'image type %s; going with %s' % + (ext, format, ext), stacklevel=0) + format = ext + if default_dir != "": + matplotlib.rcParams['savefig.directory'] = dirname + try: + self.canvas.figure.savefig( + os.path.join(dirname, filename), format=format) + except Exception as e: + error_msg_wx(str(e)) + + +class SetCursorWx(backend_tools.SetCursorBase): + def set_cursor(self, cursor): + cursor = wxc.Cursor(cursord[cursor]) + self.canvas.SetCursor(cursor) + self.canvas.Update() + + +if not 'wxMac' in wx.PlatformInfo: + # on most platforms, use overlay + class RubberbandWx(backend_tools.RubberbandBase): + def __init__(self, *args, **kwargs): + backend_tools.RubberbandBase.__init__(self, *args, **kwargs) + self.wxoverlay = None + + def draw_rubberband(self, x0, y0, x1, y1): + # Use an Overlay to draw a rubberband-like bounding box. + if self.wxoverlay is None: + self.wxoverlay = wx.Overlay() + dc = wx.ClientDC(self.canvas) + odc = wx.DCOverlay(self.wxoverlay, dc) + odc.Clear() + + dc = wx.GCDC(dc) + + height = self.canvas.figure.bbox.height + y1 = height - y1 + y0 = height - y0 + + if y1 < y0: + y0, y1 = y1, y0 + if x1 < y0: + x0, x1 = x1, x0 + + w = x1 - x0 + h = y1 - y0 + rect = wx.Rect(x0, y0, w, h) + + rubberBandColor = '#C0C0FF' # or load from config? + + # Set a pen for the border + color = wxc.NamedColour(rubberBandColor) + dc.SetPen(wx.Pen(color, 1)) + + # use the same color, plus alpha for the brush + r, g, b, a = color.Get(True) + color.Set(r, g, b, 0x60) + dc.SetBrush(wx.Brush(color)) + if wxc.is_phoenix: + dc.DrawRectangle(rect) + else: + dc.DrawRectangleRect(rect) + + def remove_rubberband(self): + if self.wxoverlay is None: return + self.wxoverlay.Reset() + self.wxoverlay = None + +else: + # on Mac OS retina displays DCOverlay does not work + class RubberbandWx(backend_tools.RubberbandBase): + def __init__(self, *args, **kwargs): + backend_tools.RubberbandBase.__init__(self, *args, **kwargs) + self._rect = None + + def remove_rubberband(self): + self.draw_rubberband(None,None,None,None,False) + + def draw_rubberband(self, x0, y0, x1, y1, draw_new=True): + + dc = wx.ClientDC(self.canvas) + # this would be required if the Canvas is a ScrolledWindow, + # which is not the case for now + # self.PrepareDC(_dc) + + dc.SetPen(wx.Pen(wx.BLACK, 1, wx.SOLID)) + dc.SetLogicalFunction(wx.INVERT) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + + if self._rect: + dc.DrawRectangle(self._rect) + + if draw_new: + self._rect = (x0,self.canvas._height-y0, x1-x0, -y1+y0) + dc.DrawRectangle(self._rect) + else: + self._rect = None + + dc.SetLogicalFunction(wx.COPY) + + +backend_tools.ToolSaveFigure = SaveFigureWx +backend_tools.ToolSetCursor = SetCursorWx +backend_tools.ToolRubberband = RubberbandWx + + + #< Additions for printing support: Matt Newville class PrintoutWx(wx.Printout): From 0cbd176ff63a26777d7e04b727f4fb33ab4dc1d1 Mon Sep 17 00:00:00 2001 From: DietmarSchwertberger Date: Tue, 9 Jan 2018 22:50:24 +0100 Subject: [PATCH 2/4] paint full bitmap on Mac OS, as dc.SetLogicalFunction(wx.INvERT) does not work --- lib/matplotlib/backends/backend_wx.py | 41 ++++++++++++++++----------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index 66d0da759d97..aa90ec09fcdf 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -1819,35 +1819,42 @@ def remove_rubberband(self): else: # on Mac OS retina displays DCOverlay does not work + # and dc.SetLogicalFunction does not have an effect on any display + # the workaround is to blit the full image for remove_rubberband class RubberbandWx(backend_tools.RubberbandBase): def __init__(self, *args, **kwargs): backend_tools.RubberbandBase.__init__(self, *args, **kwargs) self._rect = None - def remove_rubberband(self): - self.draw_rubberband(None,None,None,None,False) - - def draw_rubberband(self, x0, y0, x1, y1, draw_new=True): - + def draw_rubberband(self, x0, y0, x1, y1): dc = wx.ClientDC(self.canvas) # this would be required if the Canvas is a ScrolledWindow, # which is not the case for now - # self.PrepareDC(_dc) - + # self.PrepareDC(dc) + + # delete old rubberband + if self._rect: self.remove_rubberband(dc) + + # draw new rubberband dc.SetPen(wx.Pen(wx.BLACK, 1, wx.SOLID)) - dc.SetLogicalFunction(wx.INVERT) dc.SetBrush(wx.TRANSPARENT_BRUSH) - - if self._rect: - dc.DrawRectangle(self._rect) - - if draw_new: - self._rect = (x0,self.canvas._height-y0, x1-x0, -y1+y0) + self._rect = (x0,self.canvas._height-y0, x1-x0, -y1+y0) + if wxc.is_phoenix: dc.DrawRectangle(self._rect) else: - self._rect = None - - dc.SetLogicalFunction(wx.COPY) + dc.DrawRectangleRect(self._rect) + + def remove_rubberband(self, dc=None): + if not self._rect: return + if self.canvas.bitmap: + if dc is None: + dc = wx.ClientDC(self.canvas) + dc.DrawBitmap(self.canvas.bitmap, 0, 0) + # for testing the method on Windows, use this code instead: + #img = self.canvas.bitmap.ConvertToImage() + #bmp = img.ConvertToBitmap() + #dc.DrawBitmap(bmp, 0, 0) + self._rect = None backend_tools.ToolSaveFigure = SaveFigureWx From 8dedf66dafdf927eb6d8442b89cc99e681d31cb1 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 11 Feb 2018 15:20:07 -0500 Subject: [PATCH 3/4] STY: whitespace fix --- lib/matplotlib/backends/backend_wx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index aa90ec09fcdf..f589bae67950 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -1738,7 +1738,7 @@ def trigger(self, *args): dirname = dlg.GetDirectory() filename = dlg.GetFilename() - DEBUG_MSG( 'Save file dir:%s name:%s' % (dirname, filename), 3, self) + DEBUG_MSG('Save file dir:%s name:%s' % (dirname, filename), 3, self) format = exts[dlg.GetFilterIndex()] basename, ext = os.path.splitext(filename) if ext.startswith('.'): From f7e12cbeb9972eb283d9a1a1a9cc09223da6a80c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 11 Feb 2018 16:00:21 -0500 Subject: [PATCH 4/4] STY: fix more pep8 violations --- lib/matplotlib/backends/backend_wx.py | 70 ++++++++++++--------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index f589bae67950..719a195dc828 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -61,7 +61,7 @@ def DEBUG_MSG(string, lvl=3, o=None): # Jeremy, often times the commented line won't print but the # one below does. I think WX is redefining stderr, damned # beast - #print >>sys.stderr, "%s- %s in %s" % (_DEBUG_lvls[lvl], string, cls) + # print >>sys.stderr, "%s- %s in %s" % (_DEBUG_lvls[lvl], string, cls) print("%s- %s in %s" % (_DEBUG_lvls[lvl], string, cls)) @@ -82,10 +82,6 @@ class fake_stderr(object): def write(self, msg): print("Stderr: %s\n\r" % msg) -#if _DEBUG < 5: - #sys.excepthook = debug_on_error - #WxLogger =wx.LogStderr() - #sys.stderr = fake_stderr # the True dots per inch on the screen; should be display dependent # see @@ -410,7 +406,7 @@ class GraphicsContextWx(GraphicsContextBase): def __init__(self, bitmap, renderer): GraphicsContextBase.__init__(self) - #assert self.Ok(), "wxMemoryDC not OK to use" + # assert self.Ok(), "wxMemoryDC not OK to use" DEBUG_MSG("__init__()", 1, self) DEBUG_MSG("__init__() 2: %s" % bitmap, 1, self) @@ -1201,10 +1197,10 @@ def __init__(self, num, fig): # This is not currently working on Linux and is untested elsewhere. # icon_path = os.path.join(matplotlib.rcParams['datapath'], # 'images', 'matplotlib.png') - #icon = wx.IconFromBitmap(wx.Bitmap(icon_path)) - # for xpm type icons try: - #icon = wx.Icon(icon_path, wx.BITMAP_TYPE_XPM) - # self.SetIcon(icon) + # icon = wx.IconFromBitmap(wx.Bitmap(icon_path)) + # for xpm type icons try: + # icon = wx.Icon(icon_path, wx.BITMAP_TYPE_XPM) + # self.SetIcon(icon) self.figmgr = FigureManagerWx(self.canvas, num, self) @@ -1308,7 +1304,8 @@ def resize(self, width, height): # Identifiers for toolbar controls - images_wx contains bitmaps for the images # used in the controls. wxWindows does not provide any stock images, so I've # 'stolen' those from GTK2, and transformed them into the appropriate format. -#import images_wx +# import images_wx + _NTB_AXISMENU = wx.NewId() _NTB_AXISMENU_BUTTON = wx.NewId() @@ -1320,7 +1317,7 @@ def resize(self, width, height): _NTB_Y_PAN_DOWN = wx.NewId() _NTB_Y_ZOOMIN = wx.NewId() _NTB_Y_ZOOMOUT = wx.NewId() -#_NTB_SUBPLOT =wx.NewId() +# _NTB_SUBPLOT =wx.NewId() _NTB_SAVE = wx.NewId() _NTB_CLOSE = wx.NewId() @@ -1513,8 +1510,8 @@ def _init_toolbar(self): continue self.wx_ids[text] = wx.NewId() wxc._AddTool(self, self.wx_ids, text, - _load_bitmap(image_file + '.png'), - tooltip_text) + _load_bitmap(image_file + '.png'), + tooltip_text) self.Bind(wx.EVT_TOOL, getattr(self, callback), id=self.wx_ids[text]) @@ -1584,12 +1581,6 @@ def set_cursor(self, cursor): self.canvas.SetCursor(cursor) self.canvas.Update() - def release(self, event): - try: - del self.lastrect - except AttributeError: - pass - @cbook.deprecated("2.1", alternative="canvas.draw_idle") def dynamic_update(self): d = self._idle @@ -1707,7 +1698,7 @@ def __init__(self, parent): wx.StatusBar.__init__(self, parent, -1) self.SetFieldsCount(2) self.SetStatusText("None", 1) - #self.SetStatusText("Measurement: None", 2) + # self.SetStatusText("Measurement: None", 2) # self.Reposition() def set_function(self, string): @@ -1767,7 +1758,7 @@ def set_cursor(self, cursor): self.canvas.Update() -if not 'wxMac' in wx.PlatformInfo: +if 'wxMac' not in wx.PlatformInfo: # on most platforms, use overlay class RubberbandWx(backend_tools.RubberbandBase): def __init__(self, *args, **kwargs): @@ -1813,7 +1804,8 @@ def draw_rubberband(self, x0, y0, x1, y1): dc.DrawRectangleRect(rect) def remove_rubberband(self): - if self.wxoverlay is None: return + if self.wxoverlay is None: + return self.wxoverlay.Reset() self.wxoverlay = None @@ -1831,29 +1823,31 @@ def draw_rubberband(self, x0, y0, x1, y1): # this would be required if the Canvas is a ScrolledWindow, # which is not the case for now # self.PrepareDC(dc) - + # delete old rubberband - if self._rect: self.remove_rubberband(dc) - + if self._rect: + self.remove_rubberband(dc) + # draw new rubberband - dc.SetPen(wx.Pen(wx.BLACK, 1, wx.SOLID)) + dc.SetPen(wx.Pen(wx.BLACK, 1, wx.SOLID)) dc.SetBrush(wx.TRANSPARENT_BRUSH) - self._rect = (x0,self.canvas._height-y0, x1-x0, -y1+y0) + self._rect = (x0, self.canvas._height-y0, x1-x0, -y1+y0) if wxc.is_phoenix: dc.DrawRectangle(self._rect) else: dc.DrawRectangleRect(self._rect) def remove_rubberband(self, dc=None): - if not self._rect: return + if not self._rect: + return if self.canvas.bitmap: if dc is None: dc = wx.ClientDC(self.canvas) dc.DrawBitmap(self.canvas.bitmap, 0, 0) - # for testing the method on Windows, use this code instead: - #img = self.canvas.bitmap.ConvertToImage() - #bmp = img.ConvertToBitmap() - #dc.DrawBitmap(bmp, 0, 0) + # for testing the method on Windows, use this code instead: + # img = self.canvas.bitmap.ConvertToImage() + # bmp = img.ConvertToBitmap() + # dc.DrawBitmap(bmp, 0, 0) self._rect = None @@ -1862,8 +1856,7 @@ def remove_rubberband(self, dc=None): backend_tools.ToolRubberband = RubberbandWx - -#< Additions for printing support: Matt Newville +# < Additions for printing support: Matt Newville class PrintoutWx(wx.Printout): """ @@ -1938,10 +1931,10 @@ def OnPrintPage(self, page): # this cute little number avoid API inconsistencies in wx try: dc.DrawBitmap(self.canvas.bitmap, 0, 0) - except: + except Exception: try: dc.DrawBitmap(self.canvas.bitmap, (0, 0)) - except: + except Exception: pass # restore original figure resolution @@ -1949,7 +1942,7 @@ def OnPrintPage(self, page): self.canvas.figure.dpi = fig_dpi self.canvas.draw() return True -#> +# > ######################################################################## # @@ -1957,6 +1950,7 @@ def OnPrintPage(self, page): # ######################################################################## + Toolbar = NavigationToolbar2Wx