Skip to content

Commit

Permalink
Merge pull request #4972 from pwuertz/qt5_fixes_combined
Browse files Browse the repository at this point in the history
MNT: Move agg draw to main thread and fix rubberband in Qt
  • Loading branch information
tacaswell committed Sep 1, 2015
2 parents 485c04b + d27ad98 commit 3122457
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 33 deletions.
6 changes: 6 additions & 0 deletions lib/matplotlib/backend_bases.py
Expand Up @@ -2763,6 +2763,10 @@ def draw_rubberband(self, event, x0, y0, x1, y1):
"""Draw a rectangle rubberband to indicate zoom limits"""
pass

def remove_rubberband(self):
"""Remove the rubberband"""
pass

def forward(self, *args):
"""Move forward in the view lim stack"""
self._views.forward()
Expand Down Expand Up @@ -3033,6 +3037,8 @@ def release_zoom(self, event):
self.canvas.mpl_disconnect(zoom_id)
self._ids_zoom = []

self.remove_rubberband()

if not self._xypress:
return

Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/backends/backend_qt4agg.py
Expand Up @@ -69,6 +69,7 @@ def __init__(self, figure):
if DEBUG:
print('FigureCanvasQtAgg: ', figure)
FigureCanvasQT.__init__(self, figure)
FigureCanvasQTAggBase.__init__(self, figure)
FigureCanvasAgg.__init__(self, figure)
self._drawRect = None
self.blitbox = None
Expand Down
21 changes: 3 additions & 18 deletions lib/matplotlib/backends/backend_qt5.py
Expand Up @@ -239,7 +239,6 @@ def __init__(self, figure):
super(FigureCanvasQT, self).__init__(figure=figure)
self.figure = figure
self.setMouseTracking(True)
self._idle = True
w, h = self.get_width_height()
self.resize(w, h)

Expand Down Expand Up @@ -415,23 +414,6 @@ def stop_event_loop(self):

stop_event_loop.__doc__ = FigureCanvasBase.stop_event_loop_default.__doc__

def draw_idle(self):
# This cannot be a call to 'update', we need a slightly longer
# delay, otherwise mouse releases from zooming, panning, or
# lassoing might not finish processing and will not redraw properly.
# We use the guard flag to prevent infinite calls to 'draw_idle' which
# happens with the 'stale' figure & axes callbacks.
d = self._idle
self._idle = False

def idle_draw(*args):
try:
self.draw()
finally:
self._idle = True
if d:
QtCore.QTimer.singleShot(0, idle_draw)


class MainWindow(QtWidgets.QMainWindow):
closing = QtCore.Signal()
Expand Down Expand Up @@ -699,6 +681,9 @@ def draw_rubberband(self, event, x0, y0, x1, y1):
rect = [int(val)for val in (min(x0, x1), min(y0, y1), w, h)]
self.canvas.drawRectangle(rect)

def remove_rubberband(self):
self.canvas.drawRectangle(None)

def configure_subplots(self):
image = os.path.join(matplotlib.rcParams['datapath'],
'images', 'matplotlib.png')
Expand Down
52 changes: 37 additions & 15 deletions lib/matplotlib/backends/backend_qt5agg.py
Expand Up @@ -58,23 +58,23 @@ class FigureCanvasQTAggBase(object):
Public attribute
figure - A Figure instance
"""
figure - A Figure instance
"""

def __init__(self, figure):
super(FigureCanvasQTAggBase, self).__init__(figure=figure)
self._agg_draw_pending = False

def drawRectangle(self, rect):
self._drawRect = rect
self.draw_idle()
self.update()

def paintEvent(self, e):
"""
Copy the image from the Agg canvas to the qt.drawable.
In Qt, all drawing should be done inside of here when a widget is
shown onscreen.
"""
# If we have not rendered the Agg backend yet, do so now.
if not hasattr(self, 'renderer'):
FigureCanvasAgg.draw(self)

# FigureCanvasQT.paintEvent(self, e)
if DEBUG:
print('FigureCanvasQtAgg.paintEvent: ', self,
Expand Down Expand Up @@ -136,21 +136,44 @@ def paintEvent(self, e):
pixmap = QtGui.QPixmap.fromImage(qImage)
p = QtGui.QPainter(self)
p.drawPixmap(QtCore.QPoint(l, self.renderer.height-t), pixmap)

# draw the zoom rectangle to the QPainter
if self._drawRect is not None:
p.setPen(QtGui.QPen(QtCore.Qt.black, 1, QtCore.Qt.DotLine))
x, y, w, h = self._drawRect
p.drawRect(x, y, w, h)
p.end()
self.blitbox = None
self._drawRect = None

def draw(self):
"""
Draw the figure with Agg, and queue a request
for a Qt draw.
Draw the figure with Agg, and queue a request for a Qt draw.
"""
# The Agg draw is done here; delaying it until the paintEvent
# causes problems with code that uses the result of the
# draw() to update plot elements.
# The Agg draw is done here; delaying causes problems with code that
# uses the result of the draw() to update plot elements.
FigureCanvasAgg.draw(self)
self.update()

def draw_idle(self):
"""
Queue redraw of the Agg buffer and request Qt paintEvent.
"""
# The Agg draw needs to be handled by the same thread matplotlib
# modifies the scene graph from. Post Agg draw request to the
# current event loop in order to ensure thread affinity and to
# accumulate multiple draw requests from event handling.
# TODO: queued signal connection might be safer than singleShot
if not self._agg_draw_pending:
self._agg_draw_pending = True
QtCore.QTimer.singleShot(0, self.__draw_idle_agg)

def __draw_idle_agg(self, *args):
try:
FigureCanvasAgg.draw(self)
self.update()
finally:
self._agg_draw_pending = False

def blit(self, bbox=None):
"""
Blit the region in bbox
Expand Down Expand Up @@ -186,8 +209,7 @@ class FigureCanvasQTAgg(FigureCanvasQTAggBase,
def __init__(self, figure):
if DEBUG:
print('FigureCanvasQtAgg: ', figure)
FigureCanvasQT.__init__(self, figure)
FigureCanvasAgg.__init__(self, figure)
super(FigureCanvasQTAgg, self).__init__(figure=figure)
self._drawRect = None
self.blitbox = None
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
Expand Down

0 comments on commit 3122457

Please sign in to comment.