Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2abdcea

Browse files
committed
Qt5Cairo backend.
1 parent 1f3699c commit 2abdcea

File tree

6 files changed

+109
-93
lines changed

6 files changed

+109
-93
lines changed

lib/matplotlib/backends/backend_agg.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -422,18 +422,18 @@ def draw(self):
422422
Draw the figure using the renderer
423423
"""
424424
self.renderer = self.get_renderer(cleared=True)
425-
# acquire a lock on the shared font cache
426-
RendererAgg.lock.acquire()
427-
428425
toolbar = self.toolbar
429426
try:
430427
if toolbar:
431428
toolbar.set_cursor(cursors.WAIT)
432-
self.figure.draw(self.renderer)
429+
with RendererAgg.lock:
430+
self.figure.draw(self.renderer)
431+
# A GUI class may be need to update a window using this draw, so
432+
# don't forget to call the superclass.
433+
super(FigureCanvasAgg, self).draw()
433434
finally:
434435
if toolbar:
435436
toolbar.set_cursor(toolbar._lastCursor)
436-
RendererAgg.lock.release()
437437

438438
def get_renderer(self, cleared=False):
439439
l, b, w, h = self.figure.bbox.bounds

lib/matplotlib/backends/backend_qt5.py

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import re
88
import signal
99
import sys
10-
from six import unichr
10+
import traceback
1111

1212
import matplotlib
1313

@@ -226,19 +226,16 @@ class FigureCanvasQT(QtWidgets.QWidget, FigureCanvasBase):
226226
# QtCore.Qt.XButton2: None,
227227
}
228228

229-
def _update_figure_dpi(self):
230-
dpi = self._dpi_ratio * self.figure._original_dpi
231-
self.figure._set_dpi(dpi, forward=False)
232-
233229
@_allow_super_init
234230
def __init__(self, figure):
235231
_create_qApp()
236232
super(FigureCanvasQT, self).__init__(figure=figure)
237233

238-
figure._original_dpi = figure.dpi
239234
self.figure = figure
235+
# We don't want to scale up the figure DPI more than once.
236+
# Note, we don't handle a signal for changing DPI yet.
237+
figure._original_dpi = figure.dpi
240238
self._update_figure_dpi()
241-
self.resize(*self.get_width_height())
242239
# In cases with mixed resolution displays, we need to be careful if the
243240
# dpi_ratio changes - in this case we need to resize the canvas
244241
# accordingly. We could watch for screenChanged events from Qt, but
@@ -248,13 +245,23 @@ def __init__(self, figure):
248245
# needed.
249246
self._dpi_ratio_prev = None
250247

248+
self._draw_pending = False
249+
self._is_drawing = False
250+
self._draw_rect_callback = lambda painter: None
251+
252+
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
251253
self.setMouseTracking(True)
254+
self.resize(*self.get_width_height())
252255
# Key auto-repeat enabled by default
253256
self._keyautorepeat = True
254257

255258
palette = QtGui.QPalette(QtCore.Qt.white)
256259
self.setPalette(palette)
257260

261+
def _update_figure_dpi(self):
262+
dpi = self._dpi_ratio * self.figure._original_dpi
263+
self.figure._set_dpi(dpi, forward=False)
264+
258265
@property
259266
def _dpi_ratio(self):
260267
# Not available on Qt4 or some older Qt5.
@@ -408,7 +415,7 @@ def _get_key(self, event):
408415
if event_key > MAX_UNICODE:
409416
return None
410417

411-
key = unichr(event_key)
418+
key = six.unichr(event_key)
412419
# qt delivers capitalized letters. fix capitalization
413420
# note that capslock is ignored
414421
if 'shift' in mods:
@@ -453,6 +460,59 @@ def stop_event_loop(self, event=None):
453460
if hasattr(self, "_event_loop"):
454461
self._event_loop.quit()
455462

463+
def draw(self):
464+
"""Render the figure, and queue a request for a Qt draw.
465+
"""
466+
# The Agg draw is done here; delaying causes problems with code that
467+
# uses the result of the draw() to update plot elements.
468+
if self._is_drawing:
469+
return
470+
self._is_drawing = True
471+
try:
472+
super(FigureCanvasQT, self).draw()
473+
finally:
474+
self._is_drawing = False
475+
self.update()
476+
477+
def draw_idle(self):
478+
"""Queue redraw of the Agg buffer and request Qt paintEvent.
479+
"""
480+
# The Agg draw needs to be handled by the same thread matplotlib
481+
# modifies the scene graph from. Post Agg draw request to the
482+
# current event loop in order to ensure thread affinity and to
483+
# accumulate multiple draw requests from event handling.
484+
# TODO: queued signal connection might be safer than singleShot
485+
if not (self._draw_pending or self._is_drawing):
486+
self._draw_pending = True
487+
QtCore.QTimer.singleShot(0, self._draw_idle)
488+
489+
def _draw_idle(self):
490+
if self.height() < 0 or self.width() < 0:
491+
self._draw_pending = False
492+
return
493+
try:
494+
self.draw()
495+
except Exception:
496+
# Uncaught exceptions are fatal for PyQt5, so catch them instead.
497+
traceback.print_exc()
498+
finally:
499+
self._draw_pending = False
500+
501+
def drawRectangle(self, rect):
502+
# Draw the zoom rectangle to the QPainter. _draw_rect_callback needs
503+
# to be called at the end of paintEvent.
504+
if rect is not None:
505+
def _draw_rect_callback(painter):
506+
pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio,
507+
QtCore.Qt.DotLine)
508+
painter.setPen(pen)
509+
painter.drawRect(*(pt / self._dpi_ratio for pt in rect))
510+
else:
511+
def _draw_rect_callback(painter):
512+
return
513+
self._draw_rect_callback = _draw_rect_callback
514+
self.update()
515+
456516

457517
class MainWindow(QtWidgets.QMainWindow):
458518
closing = QtCore.Signal()

lib/matplotlib/backends/backend_qt5agg.py

Lines changed: 5 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import six
88

99
import ctypes
10-
import traceback
1110

1211
from matplotlib import cbook
1312
from matplotlib.transforms import Bbox
@@ -19,32 +18,11 @@
1918
from .qt_compat import QT_API
2019

2120

22-
class FigureCanvasQTAggBase(FigureCanvasAgg):
23-
"""
24-
The canvas the figure renders into. Calls the draw and print fig
25-
methods, creates the renderers, etc...
26-
27-
Attributes
28-
----------
29-
figure : `matplotlib.figure.Figure`
30-
A high-level Figure instance
31-
32-
"""
21+
class FigureCanvasQTAgg(FigureCanvasAgg, FigureCanvasQT):
3322

3423
def __init__(self, figure):
35-
super(FigureCanvasQTAggBase, self).__init__(figure=figure)
36-
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
37-
self._agg_draw_pending = False
38-
self._agg_is_drawing = False
24+
super(FigureCanvasQTAgg, self).__init__(figure=figure)
3925
self._bbox_queue = []
40-
self._drawRect = None
41-
42-
def drawRectangle(self, rect):
43-
if rect is not None:
44-
self._drawRect = [pt / self._dpi_ratio for pt in rect]
45-
else:
46-
self._drawRect = None
47-
self.update()
4826

4927
@property
5028
@cbook.deprecated("2.1")
@@ -110,13 +88,7 @@ def paintEvent(self, e):
11088
if QT_API == 'PySide' and six.PY3:
11189
ctypes.c_long.from_address(id(buf)).value = 1
11290

113-
# draw the zoom rectangle to the QPainter
114-
if self._drawRect is not None:
115-
pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio,
116-
QtCore.Qt.DotLine)
117-
painter.setPen(pen)
118-
x, y, w, h = self._drawRect
119-
painter.drawRect(x, y, w, h)
91+
self._draw_rect_callback(painter)
12092

12193
painter.end()
12294

@@ -130,42 +102,11 @@ def draw(self):
130102

131103
self._agg_is_drawing = True
132104
try:
133-
super(FigureCanvasQTAggBase, self).draw()
105+
super(FigureCanvasQTAgg, self).draw()
134106
finally:
135107
self._agg_is_drawing = False
136108
self.update()
137109

138-
def draw_idle(self):
139-
"""Queue redraw of the Agg buffer and request Qt paintEvent.
140-
"""
141-
# The Agg draw needs to be handled by the same thread matplotlib
142-
# modifies the scene graph from. Post Agg draw request to the
143-
# current event loop in order to ensure thread affinity and to
144-
# accumulate multiple draw requests from event handling.
145-
# TODO: queued signal connection might be safer than singleShot
146-
if not (self._agg_draw_pending or self._agg_is_drawing):
147-
self._agg_draw_pending = True
148-
QtCore.QTimer.singleShot(0, self.__draw_idle_agg)
149-
150-
def __draw_idle_agg(self, *args):
151-
# if nothing to do, bail
152-
if not self._agg_draw_pending:
153-
return
154-
# we have now tried this function at least once, do not run
155-
# again until re-armed. Doing this here rather than after
156-
# protects against recursive calls triggered through self.draw
157-
# The recursive call is via `repaintEvent`
158-
self._agg_draw_pending = False
159-
# if negative size, bail
160-
if self.height() < 0 or self.width() < 0:
161-
return
162-
try:
163-
# actually do the drawing
164-
self.draw()
165-
except Exception:
166-
# Uncaught exceptions are fatal for PyQt5, so catch them instead.
167-
traceback.print_exc()
168-
169110
def blit(self, bbox=None):
170111
"""Blit the region in bbox.
171112
"""
@@ -182,25 +123,10 @@ def blit(self, bbox=None):
182123
self.repaint(l, self.renderer.height / self._dpi_ratio - t, w, h)
183124

184125
def print_figure(self, *args, **kwargs):
185-
super(FigureCanvasQTAggBase, self).print_figure(*args, **kwargs)
126+
super(FigureCanvasQTAgg, self).print_figure(*args, **kwargs)
186127
self.draw()
187128

188129

189-
class FigureCanvasQTAgg(FigureCanvasQTAggBase, FigureCanvasQT):
190-
"""
191-
The canvas the figure renders into. Calls the draw and print fig
192-
methods, creates the renderers, etc.
193-
194-
Modified to import from Qt5 backend for new-style mouse events.
195-
196-
Attributes
197-
----------
198-
figure : `matplotlib.figure.Figure`
199-
A high-level Figure instance
200-
201-
"""
202-
203-
204130
@_BackendQT5.export
205131
class _BackendQT5Agg(_BackendQT5):
206132
FigureCanvas = FigureCanvasQTAgg
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from . import backend_cairo # Keep the RendererCairo class swappable.
2+
from .backend_qt5 import QtCore, QtGui, _BackendQT5, FigureCanvasQT
3+
4+
5+
class FigureCanvasQTCairo(FigureCanvasQT):
6+
def __init__(self, figure):
7+
super(FigureCanvasQTCairo, self).__init__(figure=figure)
8+
self._renderer = backend_cairo.RendererCairo(self.figure.dpi)
9+
10+
def paintEvent(self, event):
11+
width = self.width()
12+
height = self.height()
13+
surface = backend_cairo.cairo.ImageSurface(
14+
backend_cairo.cairo.FORMAT_ARGB32, width, height)
15+
self._renderer.set_ctx_from_surface(surface)
16+
# This should be really done by set_ctx_from_surface...
17+
self._renderer.set_width_height(width, height)
18+
self.figure.draw(self._renderer)
19+
qimage = QtGui.QImage(surface.get_data(), width, height,
20+
QtGui.QImage.Format_ARGB32_Premultiplied)
21+
painter = QtGui.QPainter(self)
22+
painter.drawImage(0, 0, qimage)
23+
self._draw_rect_callback(painter)
24+
painter.end()
25+
26+
27+
@_BackendQT5.export
28+
class _BackendQT5Cairo(_BackendQT5):
29+
FigureCanvas = FigureCanvasQTCairo

lib/matplotlib/rcsetup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
# change for later versions.
4040

4141
interactive_bk = ['GTK', 'GTKAgg', 'GTKCairo', 'MacOSX',
42-
'Qt4Agg', 'Qt5Agg', 'TkAgg', 'WX', 'WXAgg',
42+
'Qt4Agg', 'Qt5Agg', 'Qt5Cairo', 'TkAgg', 'WX', 'WXAgg',
4343
'GTK3Cairo', 'GTK3Agg', 'WebAgg', 'nbAgg']
4444

4545

lib/matplotlib/tests/test_backends_interactive.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def _get_testable_interactive_backends():
2121
for deps, backend in [(["cairocffi", "pgi"], "gtk3agg"),
2222
(["cairocffi", "pgi"], "gtk3cairo"),
2323
(["PyQt5"], "qt5agg"),
24+
(["cairocffi", "PyQt5"], "qt5cairo"),
2425
(["tkinter"], "tkagg"),
2526
(["wx"], "wxagg")]:
2627
reason = None

0 commit comments

Comments
 (0)