7
7
import six
8
8
9
9
import ctypes
10
- import sys
11
10
import traceback
12
11
12
+ from matplotlib import cbook
13
13
from matplotlib .figure import Figure
14
+ from matplotlib .transforms import Bbox
14
15
15
16
from .backend_agg import FigureCanvasAgg
16
17
from .backend_qt5 import QtCore
28
29
29
30
DEBUG = False
30
31
31
- _decref = ctypes .pythonapi .Py_DecRef
32
- _decref .argtypes = [ctypes .py_object ]
33
- _decref .restype = None
34
-
35
-
36
32
def new_figure_manager (num , * args , ** kwargs ):
37
33
"""
38
34
Create a new figure manager instance
@@ -52,7 +48,7 @@ def new_figure_manager_given_figure(num, figure):
52
48
return FigureManagerQT (canvas , num )
53
49
54
50
55
- class FigureCanvasQTAggBase (object ):
51
+ class FigureCanvasQTAggBase (FigureCanvasAgg ):
56
52
"""
57
53
The canvas the figure renders into. Calls the draw and print fig
58
54
methods, creates the renderers, etc...
@@ -66,7 +62,10 @@ class FigureCanvasQTAggBase(object):
66
62
67
63
def __init__ (self , figure ):
68
64
super (FigureCanvasQTAggBase , self ).__init__ (figure = figure )
65
+ self .setAttribute (QtCore .Qt .WA_OpaquePaintEvent )
69
66
self ._agg_draw_pending = False
67
+ self ._bbox_queue = []
68
+ self ._drawRect = None
70
69
71
70
def drawRectangle (self , rect ):
72
71
if rect is not None :
@@ -75,6 +74,11 @@ def drawRectangle(self, rect):
75
74
self ._drawRect = None
76
75
self .update ()
77
76
77
+ @property
78
+ @cbook .deprecated ("2.1" )
79
+ def blitbox (self ):
80
+ return self ._bbox_queue
81
+
78
82
def paintEvent (self , e ):
79
83
"""
80
84
Copy the image from the Agg canvas to the qt.drawable.
@@ -86,88 +90,42 @@ def paintEvent(self, e):
86
90
if not hasattr (self , 'renderer' ):
87
91
return
88
92
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
97
+ self ._bbox_queue = []
136
98
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 ()
99
+ painter .eraseRect (self .rect ())
100
+ bbox_queue = [
101
+ Bbox ([[0 , 0 ], [self .renderer .width , self .renderer .height ]])]
102
+ self ._bbox_queue = []
103
+ for bbox in bbox_queue :
104
+ l , b , r , t = map (int , bbox .extents )
105
+ w = r - l
106
+ h = t - b
107
+ reg = self .copy_from_bbox (bbox )
108
+ buf = reg .to_string_argb ()
109
+ qimage = QtGui .QImage (buf , w , h , QtGui .QImage .Format_ARGB32 )
110
+ if hasattr (qimage , 'setDevicePixelRatio' ):
111
+ # Not available on Qt4 or some older Qt5.
112
+ qimage .setDevicePixelRatio (self ._dpi_ratio )
113
+ origin = QtCore .QPoint (l , self .renderer .height - t )
114
+ painter .drawImage (origin / self ._dpi_ratio , qimage )
115
+ # Adjust the buf reference count to work around a memory
116
+ # leak bug in QImage under PySide on Python 3.
117
+ if QT_API == 'PySide' and six .PY3 :
118
+ ctypes .c_long .from_address (id (buf )).value = 1
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
+ painter .setPen (pen )
125
+ x , y , w , h = self ._drawRect
126
+ painter .drawRect (x , y , w , h )
127
+
128
+ painter .end ()
171
129
172
130
def draw (self ):
173
131
"""
@@ -213,7 +171,7 @@ def blit(self, bbox=None):
213
171
if bbox is None and self .figure :
214
172
bbox = self .figure .bbox
215
173
216
- self .blitbox .append (bbox )
174
+ self ._bbox_queue .append (bbox )
217
175
218
176
# repaint uses logical pixels, not physical pixels like the renderer.
219
177
l , b , w , h = [pt / self ._dpi_ratio for pt in bbox .bounds ]
@@ -225,8 +183,7 @@ def print_figure(self, *args, **kwargs):
225
183
self .draw ()
226
184
227
185
228
- class FigureCanvasQTAgg (FigureCanvasQTAggBase ,
229
- FigureCanvasQT , FigureCanvasAgg ):
186
+ class FigureCanvasQTAgg (FigureCanvasQTAggBase , FigureCanvasQT ):
230
187
"""
231
188
The canvas the figure renders into. Calls the draw and print fig
232
189
methods, creates the renderers, etc.
@@ -244,14 +201,11 @@ def __init__(self, figure):
244
201
if DEBUG :
245
202
print ('FigureCanvasQtAgg: ' , figure )
246
203
super (FigureCanvasQTAgg , self ).__init__ (figure = figure )
247
- self ._drawRect = None
248
- self .blitbox = []
249
204
# We don't want to scale up the figure DPI more than once.
250
205
# Note, we don't handle a signal for changing DPI yet.
251
206
if not hasattr (self .figure , '_original_dpi' ):
252
207
self .figure ._original_dpi = self .figure .dpi
253
208
self .figure .dpi = self ._dpi_ratio * self .figure ._original_dpi
254
- self .setAttribute (QtCore .Qt .WA_OpaquePaintEvent )
255
209
256
210
257
211
FigureCanvas = FigureCanvasQTAgg
0 commit comments