77import six
88
99import ctypes
10- import sys
1110import traceback
1211
12+ from matplotlib import cbook
1313from matplotlib .figure import Figure
14+ from matplotlib .transforms import Bbox
1415
1516from .backend_agg import FigureCanvasAgg
1617from .backend_qt5 import QtCore
2829
2930DEBUG = False
3031
31- _decref = ctypes .pythonapi .Py_DecRef
32- _decref .argtypes = [ctypes .py_object ]
33- _decref .restype = None
34-
35-
3632def 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.0" )
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,42 @@ 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
97+ self ._bbox_queue = []
13698 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 ()
171129
172130 def draw (self ):
173131 """
@@ -213,7 +171,7 @@ def blit(self, bbox=None):
213171 if bbox is None and self .figure :
214172 bbox = self .figure .bbox
215173
216- self .blitbox .append (bbox )
174+ self ._bbox_queue .append (bbox )
217175
218176 # repaint uses logical pixels, not physical pixels like the renderer.
219177 l , b , w , h = [pt / self ._dpi_ratio for pt in bbox .bounds ]
@@ -225,8 +183,7 @@ def print_figure(self, *args, **kwargs):
225183 self .draw ()
226184
227185
228- class FigureCanvasQTAgg (FigureCanvasQTAggBase ,
229- FigureCanvasQT , FigureCanvasAgg ):
186+ class FigureCanvasQTAgg (FigureCanvasQTAggBase , FigureCanvasQT ):
230187 """
231188 The canvas the figure renders into. Calls the draw and print fig
232189 methods, creates the renderers, etc.
@@ -244,14 +201,11 @@ def __init__(self, figure):
244201 if DEBUG :
245202 print ('FigureCanvasQtAgg: ' , figure )
246203 super (FigureCanvasQTAgg , self ).__init__ (figure = figure )
247- self ._drawRect = None
248- self .blitbox = []
249204 # We don't want to scale up the figure DPI more than once.
250205 # Note, we don't handle a signal for changing DPI yet.
251206 if not hasattr (self .figure , '_original_dpi' ):
252207 self .figure ._original_dpi = self .figure .dpi
253208 self .figure .dpi = self ._dpi_ratio * self .figure ._original_dpi
254- self .setAttribute (QtCore .Qt .WA_OpaquePaintEvent )
255209
256210
257211FigureCanvas = FigureCanvasQTAgg
0 commit comments