Skip to content

Commit 3122457

Browse files
committed
Merge pull request #4972 from pwuertz/qt5_fixes_combined
MNT: Move agg draw to main thread and fix rubberband in Qt
2 parents 485c04b + d27ad98 commit 3122457

File tree

4 files changed

+47
-33
lines changed

4 files changed

+47
-33
lines changed

lib/matplotlib/backend_bases.py

+6
Original file line numberDiff line numberDiff line change
@@ -2763,6 +2763,10 @@ def draw_rubberband(self, event, x0, y0, x1, y1):
27632763
"""Draw a rectangle rubberband to indicate zoom limits"""
27642764
pass
27652765

2766+
def remove_rubberband(self):
2767+
"""Remove the rubberband"""
2768+
pass
2769+
27662770
def forward(self, *args):
27672771
"""Move forward in the view lim stack"""
27682772
self._views.forward()
@@ -3033,6 +3037,8 @@ def release_zoom(self, event):
30333037
self.canvas.mpl_disconnect(zoom_id)
30343038
self._ids_zoom = []
30353039

3040+
self.remove_rubberband()
3041+
30363042
if not self._xypress:
30373043
return
30383044

lib/matplotlib/backends/backend_qt4agg.py

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def __init__(self, figure):
6969
if DEBUG:
7070
print('FigureCanvasQtAgg: ', figure)
7171
FigureCanvasQT.__init__(self, figure)
72+
FigureCanvasQTAggBase.__init__(self, figure)
7273
FigureCanvasAgg.__init__(self, figure)
7374
self._drawRect = None
7475
self.blitbox = None

lib/matplotlib/backends/backend_qt5.py

+3-18
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@ def __init__(self, figure):
239239
super(FigureCanvasQT, self).__init__(figure=figure)
240240
self.figure = figure
241241
self.setMouseTracking(True)
242-
self._idle = True
243242
w, h = self.get_width_height()
244243
self.resize(w, h)
245244

@@ -415,23 +414,6 @@ def stop_event_loop(self):
415414

416415
stop_event_loop.__doc__ = FigureCanvasBase.stop_event_loop_default.__doc__
417416

418-
def draw_idle(self):
419-
# This cannot be a call to 'update', we need a slightly longer
420-
# delay, otherwise mouse releases from zooming, panning, or
421-
# lassoing might not finish processing and will not redraw properly.
422-
# We use the guard flag to prevent infinite calls to 'draw_idle' which
423-
# happens with the 'stale' figure & axes callbacks.
424-
d = self._idle
425-
self._idle = False
426-
427-
def idle_draw(*args):
428-
try:
429-
self.draw()
430-
finally:
431-
self._idle = True
432-
if d:
433-
QtCore.QTimer.singleShot(0, idle_draw)
434-
435417

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

684+
def remove_rubberband(self):
685+
self.canvas.drawRectangle(None)
686+
702687
def configure_subplots(self):
703688
image = os.path.join(matplotlib.rcParams['datapath'],
704689
'images', 'matplotlib.png')

lib/matplotlib/backends/backend_qt5agg.py

+37-15
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,23 @@ class FigureCanvasQTAggBase(object):
5858
5959
Public attribute
6060
61-
figure - A Figure instance
62-
"""
61+
figure - A Figure instance
62+
"""
63+
64+
def __init__(self, figure):
65+
super(FigureCanvasQTAggBase, self).__init__(figure=figure)
66+
self._agg_draw_pending = False
6367

6468
def drawRectangle(self, rect):
6569
self._drawRect = rect
66-
self.draw_idle()
70+
self.update()
6771

6872
def paintEvent(self, e):
6973
"""
7074
Copy the image from the Agg canvas to the qt.drawable.
7175
In Qt, all drawing should be done inside of here when a widget is
7276
shown onscreen.
7377
"""
74-
# If we have not rendered the Agg backend yet, do so now.
75-
if not hasattr(self, 'renderer'):
76-
FigureCanvasAgg.draw(self)
77-
7878
# FigureCanvasQT.paintEvent(self, e)
7979
if DEBUG:
8080
print('FigureCanvasQtAgg.paintEvent: ', self,
@@ -136,21 +136,44 @@ def paintEvent(self, e):
136136
pixmap = QtGui.QPixmap.fromImage(qImage)
137137
p = QtGui.QPainter(self)
138138
p.drawPixmap(QtCore.QPoint(l, self.renderer.height-t), pixmap)
139+
140+
# draw the zoom rectangle to the QPainter
141+
if self._drawRect is not None:
142+
p.setPen(QtGui.QPen(QtCore.Qt.black, 1, QtCore.Qt.DotLine))
143+
x, y, w, h = self._drawRect
144+
p.drawRect(x, y, w, h)
139145
p.end()
140146
self.blitbox = None
141-
self._drawRect = None
142147

143148
def draw(self):
144149
"""
145-
Draw the figure with Agg, and queue a request
146-
for a Qt draw.
150+
Draw the figure with Agg, and queue a request for a Qt draw.
147151
"""
148-
# The Agg draw is done here; delaying it until the paintEvent
149-
# causes problems with code that uses the result of the
150-
# draw() to update plot elements.
152+
# The Agg draw is done here; delaying causes problems with code that
153+
# uses the result of the draw() to update plot elements.
151154
FigureCanvasAgg.draw(self)
152155
self.update()
153156

157+
def draw_idle(self):
158+
"""
159+
Queue redraw of the Agg buffer and request Qt paintEvent.
160+
"""
161+
# The Agg draw needs to be handled by the same thread matplotlib
162+
# modifies the scene graph from. Post Agg draw request to the
163+
# current event loop in order to ensure thread affinity and to
164+
# accumulate multiple draw requests from event handling.
165+
# TODO: queued signal connection might be safer than singleShot
166+
if not self._agg_draw_pending:
167+
self._agg_draw_pending = True
168+
QtCore.QTimer.singleShot(0, self.__draw_idle_agg)
169+
170+
def __draw_idle_agg(self, *args):
171+
try:
172+
FigureCanvasAgg.draw(self)
173+
self.update()
174+
finally:
175+
self._agg_draw_pending = False
176+
154177
def blit(self, bbox=None):
155178
"""
156179
Blit the region in bbox
@@ -186,8 +209,7 @@ class FigureCanvasQTAgg(FigureCanvasQTAggBase,
186209
def __init__(self, figure):
187210
if DEBUG:
188211
print('FigureCanvasQtAgg: ', figure)
189-
FigureCanvasQT.__init__(self, figure)
190-
FigureCanvasAgg.__init__(self, figure)
212+
super(FigureCanvasQTAgg, self).__init__(figure=figure)
191213
self._drawRect = None
192214
self.blitbox = None
193215
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)

0 commit comments

Comments
 (0)