Matplotlib and pygtk

This has been my first time using matplotlib and it has

    > not been entirely successful . I was asked to develop a
    > GUI using Python and omniORB to connect as a client to a
    > server and request a data stream which they wanted me to
    > display. There is a considerable amount of data that is
    > generated and I am not sure that Python was going to be
    > able to handle it. I developed the app in Python and was
    > able to connect to the server using omniORB and register
    > a callback and receive the data. This worked fine. I
    > then created the GUI using matplotlib and pygtk. The
    > display included 2 spectrograms a pan display and 2 bar
    > charts. After data of the appropriate type was received
    > they wanted the displays to be updated dynamically. I
    > used threading but it still seemed that after awhile the
    > GUI would lock up. Are there any examples available that
    > embed multiple dynamically updated figures using gtk? I'm
    > afraid that if I can't get this fixed they will write
    > python and matplotlib off and refuse to use it again.

Hi Melissa,

I have little experience with threads and try to avoid using them
directly - I'll leave that to the pros. I suggest another approach -
a useful pygtk trick is to use the idle manager to avoid calls when
gtk is busy. In the script below, new axes images are created and
drawn only when gtk is idle. It doesn't do you any good to force feed
gtk data it can't handle, so only make calls through the idle handler.

I simulate a data event generator that calls one of two image axes.
On my system, I get about 10 frames per second with the script below
using gtkagg (gtkagg will probably be a little faster than gtk, which
uses string methods to render image data).

As for your clients giving up on us, remind them that image handling
in matplotlib has gotten an order of magnitude faster in the last 5
months and that further enhancements for dynamic updating are in the
works. One important one will be the ability to render only selective
parts of the canvas (eg, just redraw the axes image portion rather
than the entire canvas). My guess is that this change alone would
roughly double the performance of gtkagg for dynamic images. Other
optimizations specifically for updating image data would provide
further performance enhancements, since this is a simple agg->agg
rendering buffer transfer.

Also, although matplotlib-0.60.2 was just released, a bug that affects
image data with data extent set (eg specgram data) was recently fixed
in CVS. You may want to work with
http://nitace.bsd.uchicago.edu:8080/files/share/matplotlib-0.60.3a.tar.gz.
It's not a critical update as the bug is only exposed in the following
combination: 1) image data with extent set, 2) image.origin = 'lower'
and 3) you have changed the axes limits from their defaults. May seem
arcane, but nonetheless, Andrew Straw managed to find it within about
6 minutes of my CVS checkin.

Let me know if this helps,
JDH

#!/usr/bin/env python
"""
An animated image
"""
import sys, time, os, gc
from matplotlib import rcParams

from matplotlib.matlab import *
import gtk

# if hold is on the axes images will accumulate and your performance
# will tank!
rc('axes', hold=False)

class HandleDraws:
    drawing_idle_id = 0
    shape = 100,100 # image size
    cnt = 0
    def __init__(self):
        self.fig = figure(1)
        self.a1 = subplot(211)
        self.a2 = subplot(212)
        
    def idle_update(self, *args):
        'only call a draw if gtk is idle'
        if self.cnt==0: self.tstart = time.time()
        draw()
        self.drawing_idle_id = 0
        self.cnt += 1
        if self.cnt>=50:
            print 'FPS', self.cnt/(time.time() - self.tstart)
            sys.exit()
            
        return False

    def update1(self, data):
        if self.drawing_idle_id == 0:
            self.a1.imshow(data, interpolation='nearest')
            self.drawing_idle_id = gtk.idle_add(self.idle_update)
        else: print 'dropping frame for axes 1'

    def update2(self, data):
        if self.drawing_idle_id == 0:
            self.a2.imshow(data, interpolation='nearest')
            self.drawing_idle_id = gtk.idle_add(self.idle_update)
        else: print 'dropping frame for axes 2'

handler = HandleDraws()

def generate_events(*args):
    data = rand(100,100)

    # randomly pick which axes to update
    if rand()>0.5: handler.update1(data)
    else: handler.update2(data)
    return True

cnt = 0

gtk.timeout_add(10, generate_events)
show()