7
7
import six
8
8
9
9
import ctypes
10
- import sys
11
10
import traceback
12
11
13
12
from matplotlib .figure import Figure
13
+ from matplotlib .transforms import Bbox
14
14
15
15
from .backend_agg import FigureCanvasAgg
16
16
from .backend_qt5 import QtCore
28
28
29
29
DEBUG = False
30
30
31
- _decref = ctypes .pythonapi .Py_DecRef
32
- _decref .argtypes = [ctypes .py_object ]
33
- _decref .restype = None
34
-
35
-
36
31
def new_figure_manager (num , * args , ** kwargs ):
37
32
"""
38
33
Create a new figure manager instance
@@ -52,7 +47,7 @@ def new_figure_manager_given_figure(num, figure):
52
47
return FigureManagerQT (canvas , num )
53
48
54
49
55
- class FigureCanvasQTAggBase (object ):
50
+ class FigureCanvasQTAggBase (FigureCanvasAgg ):
56
51
"""
57
52
The canvas the figure renders into. Calls the draw and print fig
58
53
methods, creates the renderers, etc...
@@ -66,7 +61,10 @@ class FigureCanvasQTAggBase(object):
66
61
67
62
def __init__ (self , figure ):
68
63
super (FigureCanvasQTAggBase , self ).__init__ (figure = figure )
64
+ self .setAttribute (QtCore .Qt .WA_OpaquePaintEvent )
69
65
self ._agg_draw_pending = False
66
+ self ._bbox_queue = []
67
+ self ._drawRect = None
70
68
71
69
def drawRectangle (self , rect ):
72
70
if rect is not None :
@@ -86,88 +84,37 @@ def paintEvent(self, e):
86
84
if not hasattr (self , 'renderer' ):
87
85
return
88
86
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' ):
110
100
# 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 ()
171
118
172
119
def draw (self ):
173
120
"""
@@ -213,7 +160,7 @@ def blit(self, bbox=None):
213
160
if bbox is None and self .figure :
214
161
bbox = self .figure .bbox
215
162
216
- self .blitbox .append (bbox )
163
+ self ._bbox_queue .append (bbox )
217
164
218
165
# repaint uses logical pixels, not physical pixels like the renderer.
219
166
l , b , w , h = [pt / self ._dpi_ratio for pt in bbox .bounds ]
@@ -225,8 +172,7 @@ def print_figure(self, *args, **kwargs):
225
172
self .draw ()
226
173
227
174
228
- class FigureCanvasQTAgg (FigureCanvasQTAggBase ,
229
- FigureCanvasQT , FigureCanvasAgg ):
175
+ class FigureCanvasQTAgg (FigureCanvasQTAggBase , FigureCanvasQT ):
230
176
"""
231
177
The canvas the figure renders into. Calls the draw and print fig
232
178
methods, creates the renderers, etc.
@@ -244,14 +190,11 @@ def __init__(self, figure):
244
190
if DEBUG :
245
191
print ('FigureCanvasQtAgg: ' , figure )
246
192
super (FigureCanvasQTAgg , self ).__init__ (figure = figure )
247
- self ._drawRect = None
248
- self .blitbox = []
249
193
# We don't want to scale up the figure DPI more than once.
250
194
# Note, we don't handle a signal for changing DPI yet.
251
195
if not hasattr (self .figure , '_original_dpi' ):
252
196
self .figure ._original_dpi = self .figure .dpi
253
197
self .figure .dpi = self ._dpi_ratio * self .figure ._original_dpi
254
- self .setAttribute (QtCore .Qt .WA_OpaquePaintEvent )
255
198
256
199
257
200
FigureCanvas = FigureCanvasQTAgg
0 commit comments