Skip to content

Simplify wx rubberband drawing. #18599

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

Merged
merged 1 commit into from
Oct 11, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 29 additions & 149 deletions lib/matplotlib/backends/backend_wx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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):
Expand Down