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

Skip to content

Commit e11874f

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 e7bc819 commit e11874f

File tree

2 files changed

+49
-112
lines changed

2 files changed

+49
-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: 47 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
import six
88

99
import ctypes
10-
import sys
1110
import traceback
1211

12+
from matplotlib import cbook
1313
from matplotlib.figure import Figure
14+
from matplotlib.transforms import Bbox
1415

1516
from .backend_agg import FigureCanvasAgg
1617
from .backend_qt5 import QtCore
@@ -28,11 +29,6 @@
2829

2930
DEBUG = False
3031

31-
_decref = ctypes.pythonapi.Py_DecRef
32-
_decref.argtypes = [ctypes.py_object]
33-
_decref.restype = None
34-
35-
3632
def new_figure_manager(num, *args, **kwargs):
3733
"""
3834
Create a new figure manager instance
@@ -52,7 +48,7 @@ def new_figure_manager_given_figure(num, figure):
5248
return FigureManagerQT(canvas, num)
5349

5450

55-
class FigureCanvasQTAggBase(object):
51+
class FigureCanvasQTAggBase(FigureCanvasAgg):
5652
"""
5753
The canvas the figure renders into. Calls the draw and print fig
5854
methods, creates the renderers, etc...
@@ -66,7 +62,10 @@ class FigureCanvasQTAggBase(object):
6662

6763
def __init__(self, figure):
6864
super(FigureCanvasQTAggBase, self).__init__(figure=figure)
65+
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
6966
self._agg_draw_pending = False
67+
self._bbox_queue = []
68+
self._drawRect = None
7069

7170
def drawRectangle(self, rect):
7271
if rect is not None:
@@ -75,6 +74,11 @@ def drawRectangle(self, rect):
7574
self._drawRect = None
7675
self.update()
7776

77+
@property
78+
@cbook.deprecated("2.1")
79+
def blitbox(self):
80+
return self._bbox_queue
81+
7882
def paintEvent(self, e):
7983
"""
8084
Copy the image from the Agg canvas to the qt.drawable.
@@ -86,88 +90,41 @@ def paintEvent(self, e):
8690
if not hasattr(self, 'renderer'):
8791
return
8892

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'):
110-
# 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)
93+
painter = QtGui.QPainter(self)
94+
95+
if self._bbox_queue:
96+
bbox_queue = self._bbox_queue
13697
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()
98+
painter.eraseRect(self.rect())
99+
bbox_queue = [
100+
Bbox([[0, 0], [self.renderer.width, self.renderer.height]])]
101+
self._bbox_queue = []
102+
for bbox in bbox_queue:
103+
l, b, r, t = map(int, bbox.extents)
104+
w = r - l
105+
h = t - b
106+
reg = self.copy_from_bbox(bbox)
107+
buf = reg.to_string_argb()
108+
qimage = QtGui.QImage(buf, w, h, QtGui.QImage.Format_ARGB32)
109+
if hasattr(qimage, 'setDevicePixelRatio'):
110+
# Not available on Qt4 or some older Qt5.
111+
qimage.setDevicePixelRatio(self._dpi_ratio)
112+
origin = QtCore.QPoint(l, self.renderer.height - t)
113+
painter.drawImage(origin / self._dpi_ratio, qimage)
114+
# Adjust the buf reference count to work around a memory
115+
# leak bug in QImage under PySide on Python 3.
116+
if QT_API == 'PySide' and six.PY3:
117+
ctypes.c_long.from_address(id(buf)).value = 1
118+
119+
# draw the zoom rectangle to the QPainter
120+
if self._drawRect is not None:
121+
pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio,
122+
QtCore.Qt.DotLine)
123+
painter.setPen(pen)
124+
x, y, w, h = self._drawRect
125+
painter.drawRect(x, y, w, h)
126+
127+
painter.end()
171128

172129
def draw(self):
173130
"""
@@ -213,7 +170,7 @@ def blit(self, bbox=None):
213170
if bbox is None and self.figure:
214171
bbox = self.figure.bbox
215172

216-
self.blitbox.append(bbox)
173+
self._bbox_queue.append(bbox)
217174

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

227184

228-
class FigureCanvasQTAgg(FigureCanvasQTAggBase,
229-
FigureCanvasQT, FigureCanvasAgg):
185+
class FigureCanvasQTAgg(FigureCanvasQTAggBase, FigureCanvasQT):
230186
"""
231187
The canvas the figure renders into. Calls the draw and print fig
232188
methods, creates the renderers, etc.
@@ -244,14 +200,11 @@ def __init__(self, figure):
244200
if DEBUG:
245201
print('FigureCanvasQtAgg: ', figure)
246202
super(FigureCanvasQTAgg, self).__init__(figure=figure)
247-
self._drawRect = None
248-
self.blitbox = []
249203
# We don't want to scale up the figure DPI more than once.
250204
# Note, we don't handle a signal for changing DPI yet.
251205
if not hasattr(self.figure, '_original_dpi'):
252206
self.figure._original_dpi = self.figure.dpi
253207
self.figure.dpi = self._dpi_ratio * self.figure._original_dpi
254-
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
255208

256209

257210
FigureCanvas = FigureCanvasQTAgg

0 commit comments

Comments
 (0)