diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index f55469923310..54ec4bea1caf 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -528,8 +528,8 @@ def __init__(self, parent, id, figure): # Create the drawing bitmap self.bitmap = wx.Bitmap(w, h) _log.debug("%s - __init__() - bitmap w:%d h:%d", type(self), w, h) - # TODO: Add support for 'point' inspection and plot navigation. self._isDrawn = False + self._rubberband_rect = None self.Bind(wx.EVT_SIZE, self._onSize) self.Bind(wx.EVT_PAINT, self._onPaint) @@ -628,20 +628,22 @@ def gui_repaint(self, drawDC=None, origin='WX'): _log.debug("%s - gui_repaint()", type(self)) # The "if self" check avoids a "wrapped C/C++ object has been deleted" # RuntimeError if doing things after window is closed. - if self and self.IsShownOnScreen(): - if not drawDC: - # not called from OnPaint use a ClientDC - drawDC = wx.ClientDC(self) - - # following is for 'WX' backend on Windows - # the bitmap can not be in use by another DC, - # see GraphicsContextWx._cache - if wx.Platform == '__WXMSW__' and origin == 'WX': - img = self.bitmap.ConvertToImage() - bmp = img.ConvertToBitmap() - drawDC.DrawBitmap(bmp, 0, 0) - else: - drawDC.DrawBitmap(self.bitmap, 0, 0) + if not (self and self.IsShownOnScreen()): + return + if not drawDC: # not called from OnPaint use a ClientDC + drawDC = wx.ClientDC(self) + # For 'WX' backend on Windows, the bitmap can not be in use by another + # DC (see GraphicsContextWx._cache). + bmp = (self.bitmap.ConvertToImage().ConvertToBitmap() + if wx.Platform == '__WXMSW__' and origin == 'WX' + else self.bitmap) + drawDC.DrawBitmap(bmp, 0, 0) + if self._rubberband_rect is not None: + x0, y0, x1, y1 = self._rubberband_rect + drawDC.DrawLineList( + [(x0, y0, x1, y0), (x1, y0, x1, y1), + (x0, y0, x0, y1), (x0, y1, x1, y1)], + wx.Pen('BLACK', 1, wx.PENSTYLE_SHORT_DASH)) filetypes = { **FigureCanvasBase.filetypes, @@ -1250,58 +1252,13 @@ def release_zoom(self, event): self._zoomAxes = None def draw_rubberband(self, event, x0, y0, x1, y1): - if self._retinaFix: # On Macs, use the following code - # wx.DCOverlay does not work properly on Retina displays. - rubberBandColor = '#C0C0FF' - if self._prevZoomRect: - self._prevZoomRect.pop(0).remove() - self.canvas.restore_region(self._savedRetinaImage) - X0, X1 = self._zoomStartX, event.xdata - Y0, Y1 = self._zoomStartY, event.ydata - lineX = (X0, X0, X1, X1, X0) - lineY = (Y0, Y1, Y1, Y0, Y0) - self._prevZoomRect = self._zoomAxes.plot( - lineX, lineY, '-', color=rubberBandColor) - self._zoomAxes.draw_artist(self._prevZoomRect[0]) - self.canvas.blit(self._zoomAxes.bbox) - return - - # Use an Overlay to draw a rubberband-like bounding box. - - dc = wx.ClientDC(self.canvas) - odc = wx.DCOverlay(self._wxoverlay, dc) - odc.Clear() - - # Mac's DC is already the same as a GCDC, and it causes - # problems with the overlay if we try to use an actual - # wx.GCDC so don't try it. - if 'wxMac' not in wx.PlatformInfo: - 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 < x0: - x0, x1 = x1, x0 - - w = x1 - x0 - h = y1 - y0 - rect = wx.Rect(x0, y0, w, h) + self.canvas._rubberband_rect = (x0, height - y0, x1, height - y1) + self.canvas.Refresh() - rubberBandColor = '#C0C0FF' # or load from config? - - # Set a pen for the border - color = wx.Colour(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)) - dc.DrawRectangle(rect) + def remove_rubberband(self): + self.canvas._rubberband_rect = None + self.canvas.Refresh() def set_message(self, s): if self._coordinates: @@ -1449,91 +1406,14 @@ def set_cursor(self, cursor): self._make_classic_style_pseudo_toolbar(), cursor) -if 'wxMac' not in wx.PlatformInfo: - # on most platforms, use overlay - class RubberbandWx(backend_tools.RubberbandBase): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._wxoverlay = None +class RubberbandWx(backend_tools.RubberbandBase): + def draw_rubberband(self, x0, y0, x1, y1): + NavigationToolbar2Wx.draw_rubberband( + self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1) - 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 < x0: - 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 = wx.Colour(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)) - dc.DrawRectangle(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 - # 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): - super().__init__(*args, **kwargs) - self._rect = None - - 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) - - # delete old rubberband - if self._rect: - self.remove_rubberband(dc) - - # draw new rubberband - 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) - dc.DrawRectangle(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 + def remove_rubberband(self): + NavigationToolbar2Wx.remove_rubberband( + self._make_classic_style_pseudo_toolbar()) class _HelpDialog(wx.Dialog):