Hi there,

Finally with lot of try I've finally managed to make blitting of a cmap
working.
I've also patched QT4agg backend, to make a redraw immediately.
(replaced self.draw by self.repaint)
In my current project I was able to stream a 655KB network stream, do
demodulation of an IQ stream and display the spectrogram in real time (4096
pixel per 1024) with matplotlib and QT4 backend, thx to the patch + blitting
example attached.

Without patch, the refrech was veeery long. (waiting for QT loop to execute
code wen it wanted.)
And do NOT always work. If you've got a powerfull pc you've got chance to
see only white background...
(or simply the loop timer to 0, you will see that without patch blit does
not work).

Only tested under windows currently, 'cause my project is on windows
currently...

There is one bug in the example, that I didn't manage to correct yet.
Depending on screen resolution, the color of the cmap blitted area can be
kind of alpha'ed. (that is look like a little bit transparent...) , Any idea
on the root of this issue?

By the way to make the example work I needed to do this:
        self.ax2.draw_artist(self.line2)
        self.blit(self.ax2.bbox)
        self.restore_region(self.background2)

that is blit before restore... don't understand why yet.

Any comment welcomed.

Is there any chance, after review, to find a way to include this in main
trunk?
What do think about this?

Cheers,
Laurent
"""
Render to qt from agg
"""
from __future__ import division

import os, sys

import matplotlib
from matplotlib.figure import Figure

from backend_agg import FigureCanvasAgg
from backend_qt4 import QtCore, QtGui, FigureManagerQT, FigureCanvasQT,\
     show, draw_if_interactive, backend_version, \
     NavigationToolbar2QT

DEBUG = False


def new_figure_manager( num, *args, **kwargs ):
    """
    Create a new figure manager instance
    """
    if DEBUG: print 'backend_qtagg.new_figure_manager'
    FigureClass = kwargs.pop('FigureClass', Figure)
    thisFig = FigureClass( *args, **kwargs )
    canvas = FigureCanvasQTAgg( thisFig )
    return FigureManagerQT( canvas, num )

class NavigationToolbar2QTAgg(NavigationToolbar2QT):
    def _get_canvas(self, fig):
        return FigureCanvasQTAgg(fig)

class FigureManagerQTAgg(FigureManagerQT):
    def _get_toolbar(self, canvas, parent):
        # must be inited after the window, drawingArea and figure
        # attrs are set
        if matplotlib.rcParams['toolbar']=='classic':
            print "Classic toolbar is not supported"
        elif matplotlib.rcParams['toolbar']=='toolbar2':
            toolbar = NavigationToolbar2QTAgg(canvas, parent)
        else:
            toolbar = None
        return toolbar

class FigureCanvasQTAgg( FigureCanvasQT, FigureCanvasAgg ):
    """
    The canvas the figure renders into.  Calls the draw and print fig
    methods, creates the renderers, etc...

    Public attribute

      figure - A Figure instance
   """

    def __init__( self, figure ):
        if DEBUG: print 'FigureCanvasQtAgg: ', figure
        FigureCanvasQT.__init__( self, figure )
        FigureCanvasAgg.__init__( self, figure )
        self.drawRect = False
        self.rect = []
        self.replot = True
        self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)

    def drawRectangle( self, rect ):
        self.rect = rect
        self.drawRect = True
        self.repaint( )

    def paintEvent( self, e ):
        """
        Draw to the Agg backend and then copy the image to the qt.drawable.
        In Qt, all drawing should be done inside of here when a widget is
        shown onscreen.
        """

        #FigureCanvasQT.paintEvent( self, e )
        if DEBUG: print 'FigureCanvasQtAgg.paintEvent: ', self, \
           self.get_width_height()

        # only replot data when needed
        if type(self.replot) is bool: # might be a bbox for blitting
            if self.replot:
                FigureCanvasAgg.draw(self)

            # matplotlib is in rgba byte order.  QImage wants to put the bytes
            # into argb format and is in a 4 byte unsigned int.  Little endian
            # system is LSB first and expects the bytes in reverse order
            # (bgra).
            if QtCore.QSysInfo.ByteOrder == QtCore.QSysInfo.LittleEndian:
                stringBuffer = self.renderer._renderer.tostring_bgra()
            else:
                stringBuffer = self.renderer._renderer.tostring_argb()

            qImage = QtGui.QImage(stringBuffer, self.renderer.width,
                                  self.renderer.height,
                                  QtGui.QImage.Format_ARGB32)
            p = QtGui.QPainter(self)
            p.drawPixmap(QtCore.QPoint(0, 0), QtGui.QPixmap.fromImage(qImage))

            # draw the zoom rectangle to the QPainter
            if self.drawRect:
                p.setPen( QtGui.QPen( QtCore.Qt.black, 1, QtCore.Qt.DotLine ) )
                p.drawRect( self.rect[0], self.rect[1], self.rect[2], 
self.rect[3] )

            p.end()
        # we are blitting here
        else:
            bbox = self.replot
            l, b, r, t = bbox.extents
            w = int(r) - int(l)
            h = int(t) - int(b)
            t = int(b) + h
            reg = self.copy_from_bbox(bbox)
            stringBuffer = reg.to_string_argb()
            qImage = QtGui.QImage(stringBuffer, w, h, 
QtGui.QImage.Format_ARGB32)
            pixmap = QtGui.QPixmap.fromImage(qImage)
            p = QtGui.QPainter( self )
            p.drawPixmap(QtCore.QPoint(l, self.renderer.height-t), pixmap)
            p.end()
        self.replot = False
        self.drawRect = False

    def draw( self ):
        """
        Draw the figure when xwindows is ready for the update
        """

        if DEBUG: print "FigureCanvasQtAgg.draw", self
        self.replot = True
        FigureCanvasAgg.draw(self)
        self.update()

    def blit(self, bbox=None):
        """
        Blit the region in bbox
        """

        self.replot = bbox
        l, b, w, h = bbox.bounds
        t = b + h
        self.repaint(l, self.renderer.height-t, w, h)

    def print_figure(self, *args, **kwargs):
        FigureCanvasAgg.print_figure(self, *args, **kwargs)
        self.draw()
import os
import sys

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.transforms as mtransforms
import matplotlib.cm as cm

from PyQt4 import QtCore, QtGui

ITERS = 100

import numpy as np
import time

class BlitQT(FigureCanvas):

    def __init__(self):
        FigureCanvas.__init__(self, Figure())
        fig = self.figure

        NUMBER_OF_DATA = 1024
        self.x   = np.arange(NUMBER_OF_DATA)

        self.ax1 = fig.add_axes([0.1,0.7,0.8,0.2])
        self.ax1.grid()
        
        self.ax2 = fig.add_axes([0.1,0.4,0.8,0.2])
        self.ax2.grid()
        
        self.ax3 = fig.add_axes([0.1,0.1,0.8,0.2])
        self.ax3.set_axis_bgcolor('k') 
        for l in self.ax3.get_xticklines() + self.ax3.get_yticklines():
            l.set_markersize(0) 
        
        self._width = 1024
        self._height = 256
        self._draw_width  = 1
        self._draw_height = 4096
        self.z   = self.getZ()
        
        self.old_size = self.ax1.bbox.width, self.ax1.bbox.height
        self.cnt = 0
        
        # create the initial lines
        self.line1, = self.ax1.plot(self.x, self.getY(), 'r', animated=True, 
lw=.5)
        self.line2, = self.ax2.plot(self.x, self.getY(), animated=True, lw=.5)
        self.line3  = self.ax3.imshow(self.z, interpolation='nearest', 
cmap=cm.jet, animated = True, aspect=None, origin='lower')
        self.line3.set_extent((self._width-1,0,0,self._height-1))
        dx=8
        self.ax3.set_xlim(-(self._width-1)+dx,dx)

        self.draw()

        self.background1 = self.copy_from_bbox(self.ax1.bbox)
        self.background2 = self.copy_from_bbox(self.ax2.bbox)
        self.background3 = self.copy_from_bbox(self.ax3.bbox)
        
        self.tstart = time.time()
        self.startTimer(0)

    def getZ(self):
        return np.random.random((self._draw_height,self._draw_width))

    def get_bg_bbox(self, axe):
        
        return axe.bbox#.padded(-3)
    
    def getY(self):
        return np.random.random_sample(1024)
    
    def timerEvent(self, evt):
        #if (self.cnt % 3) == 0:
        current_size = self.ax1.bbox.width, self.ax1.bbox.height
        if self.old_size != current_size:
            self.old_size = current_size
            self.ax1.clear()
            self.ax1.grid()
            self.ax2.clear()
            self.ax2.grid()
            self.ax3.clear()
            for l in self.ax3.get_xticklines() + self.ax3.get_yticklines():
                l.set_markersize(0) 
            self.draw()
            self.background1 = self.copy_from_bbox(self.ax1.bbox)
            self.background2 = self.copy_from_bbox(self.ax2.bbox)
            self.background3 = self.copy_from_bbox(self.ax3.bbox)

        # update the data
        self.line1.set_ydata(self.getY())
        self.line2.set_ydata(self.getY())
        self.line3.set_array(self.getZ())

        # restore the clean slate background
        self.ax1.draw_artist(self.line1)
        self.blit(self.ax1.bbox)
        self.restore_region(self.background1)
        
        # just draw the animated artist
        self.ax2.draw_artist(self.line2)
        self.blit(self.ax2.bbox)
        self.restore_region(self.background2)
        
        self.ax3.draw_artist(self.line3)
        self.blit(self.ax3.bbox)

        self.background3 = self.copy_from_bbox(self.get_bg_bbox(self.ax3))
        dx_pixel = 1
        x1, y1, x2, y2 = self.background3.get_extents()
        self.restore_region(self.background3,
                            bbox=(x1+3, y1, x2, y2),
                            xy=(x1-dx_pixel, y1))
                
        if self.cnt == 0:
            self.draw()

        if not (self.cnt%ITERS):
            # print the timing info and quit
            print 'FPS:' , ITERS/(time.time()-self.tstart)
            self.tstart = time.time()

        self.cnt += 1

app = QtGui.QApplication(sys.argv)
widget = BlitQT()
widget.show()

app.exec_()
------------------------------------------------------------------------------
Join us December 9, 2009 for the Red Hat Virtual Experience,
a free event focused on virtualization and cloud computing. 
Attend in-depth sessions from your desk. Your couch. Anywhere.
http://p.sf.net/sfu/redhat-sfdev2dev
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users

Reply via email to