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,41 @@ 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
136
97
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 ()
171
128
172
129
def draw (self ):
173
130
"""
@@ -213,7 +170,7 @@ def blit(self, bbox=None):
213
170
if bbox is None and self .figure :
214
171
bbox = self .figure .bbox
215
172
216
- self .blitbox .append (bbox )
173
+ self ._bbox_queue .append (bbox )
217
174
218
175
# repaint uses logical pixels, not physical pixels like the renderer.
219
176
l , b , w , h = [pt / self ._dpi_ratio for pt in bbox .bounds ]
@@ -225,8 +182,7 @@ def print_figure(self, *args, **kwargs):
225
182
self .draw ()
226
183
227
184
228
- class FigureCanvasQTAgg (FigureCanvasQTAggBase ,
229
- FigureCanvasQT , FigureCanvasAgg ):
185
+ class FigureCanvasQTAgg (FigureCanvasQTAggBase , FigureCanvasQT ):
230
186
"""
231
187
The canvas the figure renders into. Calls the draw and print fig
232
188
methods, creates the renderers, etc.
@@ -244,14 +200,11 @@ def __init__(self, figure):
244
200
if DEBUG :
245
201
print ('FigureCanvasQtAgg: ' , figure )
246
202
super (FigureCanvasQTAgg , self ).__init__ (figure = figure )
247
- self ._drawRect = None
248
- self .blitbox = []
249
203
# We don't want to scale up the figure DPI more than once.
250
204
# Note, we don't handle a signal for changing DPI yet.
251
205
if not hasattr (self .figure , '_original_dpi' ):
252
206
self .figure ._original_dpi = self .figure .dpi
253
207
self .figure .dpi = self ._dpi_ratio * self .figure ._original_dpi
254
- self .setAttribute (QtCore .Qt .WA_OpaquePaintEvent )
255
208
256
209
257
210
FigureCanvas = FigureCanvasQTAgg
0 commit comments