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.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
257210FigureCanvas = FigureCanvasQTAgg
0 commit comments