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

Skip to content

Commit ae81ae5

Browse files
committed
Unify QtAgg nonblitting and blitting code paths.
Follow a similar architecture to the Gtk3Agg backend: a default draw can be implemented as a blitting of the entire canvas. Also unify the Qt4Agg and Qt5Agg canvas classes a bit more.
1 parent 6ff34d4 commit ae81ae5

File tree

2 files changed

+39
-112
lines changed

2 files changed

+39
-112
lines changed

lib/matplotlib/backends/backend_qt4agg.py

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from matplotlib.figure import Figure
1616

1717

18-
from .backend_qt5agg import FigureCanvasQTAggBase as _FigureCanvasQTAggBase
18+
from .backend_qt5agg import FigureCanvasQTAggBase
1919

2020
from .backend_agg import FigureCanvasAgg
2121
from .backend_qt4 import QtCore
@@ -54,13 +54,7 @@ def new_figure_manager_given_figure(num, figure):
5454
return FigureManagerQT(canvas, num)
5555

5656

57-
class FigureCanvasQTAggBase(_FigureCanvasQTAggBase):
58-
def __init__(self, figure):
59-
self._agg_draw_pending = False
60-
61-
62-
class FigureCanvasQTAgg(FigureCanvasQTAggBase,
63-
FigureCanvasQT, FigureCanvasAgg):
57+
class FigureCanvasQTAgg(FigureCanvasQTAggBase, FigureCanvasQT):
6458
"""
6559
The canvas the figure renders into. Calls the draw and print fig
6660
methods, creates the renderers, etc...
@@ -72,16 +66,6 @@ class FigureCanvasQTAgg(FigureCanvasQTAggBase,
7266
7367
"""
7468

75-
def __init__(self, figure):
76-
if DEBUG:
77-
print('FigureCanvasQtAgg: ', figure)
78-
FigureCanvasQT.__init__(self, figure)
79-
FigureCanvasQTAggBase.__init__(self, figure)
80-
FigureCanvasAgg.__init__(self, figure)
81-
self._drawRect = None
82-
self.blitbox = []
83-
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
84-
8569

8670
FigureCanvas = FigureCanvasQTAgg
8771
FigureManager = FigureManagerQT

lib/matplotlib/backends/backend_qt5agg.py

Lines changed: 37 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
import six
88

99
import ctypes
10-
import sys
1110
import traceback
1211

1312
from matplotlib.figure import Figure
13+
from matplotlib.transforms import Bbox
1414

1515
from .backend_agg import FigureCanvasAgg
1616
from .backend_qt5 import QtCore
@@ -28,11 +28,6 @@
2828

2929
DEBUG = False
3030

31-
_decref = ctypes.pythonapi.Py_DecRef
32-
_decref.argtypes = [ctypes.py_object]
33-
_decref.restype = None
34-
35-
3631
def new_figure_manager(num, *args, **kwargs):
3732
"""
3833
Create a new figure manager instance
@@ -52,7 +47,7 @@ def new_figure_manager_given_figure(num, figure):
5247
return FigureManagerQT(canvas, num)
5348

5449

55-
class FigureCanvasQTAggBase(object):
50+
class FigureCanvasQTAggBase(FigureCanvasAgg):
5651
"""
5752
The canvas the figure renders into. Calls the draw and print fig
5853
methods, creates the renderers, etc...
@@ -66,7 +61,10 @@ class FigureCanvasQTAggBase(object):
6661

6762
def __init__(self, figure):
6863
super(FigureCanvasQTAggBase, self).__init__(figure=figure)
64+
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
6965
self._agg_draw_pending = False
66+
self._bbox_queue = []
67+
self._drawRect = None
7068

7169
def drawRectangle(self, rect):
7270
if rect is not None:
@@ -86,88 +84,37 @@ def paintEvent(self, e):
8684
if not hasattr(self, 'renderer'):
8785
return
8886

89-
if DEBUG:
90-
print('FigureCanvasQtAgg.paintEvent: ', self,
91-
self.get_width_height())
92-
93-
if len(self.blitbox) == 0:
94-
# matplotlib is in rgba byte order. QImage wants to put the bytes
95-
# into argb format and is in a 4 byte unsigned int. Little endian
96-
# system is LSB first and expects the bytes in reverse order
97-
# (bgra).
98-
if QtCore.QSysInfo.ByteOrder == QtCore.QSysInfo.LittleEndian:
99-
stringBuffer = self.renderer._renderer.tostring_bgra()
100-
else:
101-
stringBuffer = self.renderer._renderer.tostring_argb()
102-
103-
refcnt = sys.getrefcount(stringBuffer)
104-
105-
# convert the Agg rendered image -> qImage
106-
qImage = QtGui.QImage(stringBuffer, self.renderer.width,
107-
self.renderer.height,
108-
QtGui.QImage.Format_ARGB32)
109-
if hasattr(qImage, 'setDevicePixelRatio'):
87+
painter = QtGui.QPainter(self)
88+
89+
bbox_queue = self._bbox_queue or [
90+
Bbox([[0, 0], [self.renderer.width, self.renderer.height]])]
91+
self._bbox_queue = []
92+
for bbox in bbox_queue:
93+
l, b, r, t = map(int, bbox.extents)
94+
w = r - l
95+
h = t - b
96+
reg = self.copy_from_bbox(bbox)
97+
buf = reg.to_string_argb()
98+
qimage = QtGui.QImage(buf, w, h, QtGui.QImage.Format_ARGB32)
99+
if hasattr(qimage, 'setDevicePixelRatio'):
110100
# Not available on Qt4 or some older Qt5.
111-
qImage.setDevicePixelRatio(self._dpi_ratio)
112-
# get the rectangle for the image
113-
rect = qImage.rect()
114-
p = QtGui.QPainter(self)
115-
# reset the image area of the canvas to be the back-ground color
116-
p.eraseRect(rect)
117-
# draw the rendered image on to the canvas
118-
p.drawPixmap(QtCore.QPoint(0, 0), QtGui.QPixmap.fromImage(qImage))
119-
120-
# draw the zoom rectangle to the QPainter
121-
if self._drawRect is not None:
122-
pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio,
123-
QtCore.Qt.DotLine)
124-
p.setPen(pen)
125-
x, y, w, h = self._drawRect
126-
p.drawRect(x, y, w, h)
127-
p.end()
128-
129-
# This works around a bug in PySide 1.1.2 on Python 3.x,
130-
# where the reference count of stringBuffer is incremented
131-
# but never decremented by QImage.
132-
# TODO: revert PR #1323 once the issue is fixed in PySide.
133-
del qImage
134-
if refcnt != sys.getrefcount(stringBuffer):
135-
_decref(stringBuffer)
136-
else:
137-
p = QtGui.QPainter(self)
138-
139-
while len(self.blitbox):
140-
bbox = self.blitbox.pop()
141-
l, b, r, t = bbox.extents
142-
w = int(r) - int(l)
143-
h = int(t) - int(b)
144-
t = int(b) + h
145-
reg = self.copy_from_bbox(bbox)
146-
stringBuffer = reg.to_string_argb()
147-
qImage = QtGui.QImage(stringBuffer, w, h,
148-
QtGui.QImage.Format_ARGB32)
149-
if hasattr(qImage, 'setDevicePixelRatio'):
150-
# Not available on Qt4 or some older Qt5.
151-
qImage.setDevicePixelRatio(self._dpi_ratio)
152-
# Adjust the stringBuffer reference count to work
153-
# around a memory leak bug in QImage() under PySide on
154-
# Python 3.x
155-
if QT_API == 'PySide' and six.PY3:
156-
ctypes.c_long.from_address(id(stringBuffer)).value = 1
157-
158-
origin = QtCore.QPoint(l, self.renderer.height - t)
159-
pixmap = QtGui.QPixmap.fromImage(qImage)
160-
p.drawPixmap(origin / self._dpi_ratio, pixmap)
161-
162-
# draw the zoom rectangle to the QPainter
163-
if self._drawRect is not None:
164-
pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio,
165-
QtCore.Qt.DotLine)
166-
p.setPen(pen)
167-
x, y, w, h = self._drawRect
168-
p.drawRect(x, y, w, h)
169-
170-
p.end()
101+
qimage.setDevicePixelRatio(self._dpi_ratio)
102+
origin = QtCore.QPoint(l, self.renderer.height - t)
103+
painter.drawImage(origin / self._dpi_ratio, qimage)
104+
# Adjust the buf reference count to work around a memory
105+
# leak bug in QImage under PySide on Python 3.
106+
if QT_API == 'PySide' and six.PY3:
107+
ctypes.c_long.from_address(id(buf)).value = 1
108+
109+
# draw the zoom rectangle to the QPainter
110+
if self._drawRect is not None:
111+
pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio,
112+
QtCore.Qt.DotLine)
113+
painter.setPen(pen)
114+
x, y, w, h = self._drawRect
115+
painter.drawRect(x, y, w, h)
116+
117+
painter.end()
171118

172119
def draw(self):
173120
"""
@@ -213,7 +160,7 @@ def blit(self, bbox=None):
213160
if bbox is None and self.figure:
214161
bbox = self.figure.bbox
215162

216-
self.blitbox.append(bbox)
163+
self._bbox_queue.append(bbox)
217164

218165
# repaint uses logical pixels, not physical pixels like the renderer.
219166
l, b, w, h = [pt / self._dpi_ratio for pt in bbox.bounds]
@@ -225,8 +172,7 @@ def print_figure(self, *args, **kwargs):
225172
self.draw()
226173

227174

228-
class FigureCanvasQTAgg(FigureCanvasQTAggBase,
229-
FigureCanvasQT, FigureCanvasAgg):
175+
class FigureCanvasQTAgg(FigureCanvasQTAggBase, FigureCanvasQT):
230176
"""
231177
The canvas the figure renders into. Calls the draw and print fig
232178
methods, creates the renderers, etc.
@@ -244,14 +190,11 @@ def __init__(self, figure):
244190
if DEBUG:
245191
print('FigureCanvasQtAgg: ', figure)
246192
super(FigureCanvasQTAgg, self).__init__(figure=figure)
247-
self._drawRect = None
248-
self.blitbox = []
249193
# We don't want to scale up the figure DPI more than once.
250194
# Note, we don't handle a signal for changing DPI yet.
251195
if not hasattr(self.figure, '_original_dpi'):
252196
self.figure._original_dpi = self.figure.dpi
253197
self.figure.dpi = self._dpi_ratio * self.figure._original_dpi
254-
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
255198

256199

257200
FigureCanvas = FigureCanvasQTAgg

0 commit comments

Comments
 (0)