matplotlib: (re-)plot ping standard-input data. Doable?
Question: I recently started tinkering with python and the matplotlib module(1.1.0)
shipped with Enthought Python Distribution(the free version one). I thought of
an interesting project I could do and came up with something like this:

  * get ping of an internet address
  * pipe that via sys.stdin into python script

now in the python script:

  * regex the answer time, if there is no answer time: use NaN or just 0 as number
  * plot data via matplotlib
  * add continuously data

I managed to get the ping via this: ping stackoverflow.com | python script.py get the answertime wasn't particulary hard. But when it comes to plotting the data. I am stuck. I know there is an animation module inside matplotlib, but I think a timer-based plot would be harder to program than this, and I don't know how events are used anyway. What I want:

  * Wait for sys.stdin to get a new string and thus the ping time
  * Add it to the data array
  * Plot the data array But it doesn't seem to be that easy. Besides this, Error handling is not yet done.. Unfortunately I couldn't find any comparable code, although I did a lot of googling about it :/ Maybe this design is not meant to be like this..

Does anybody have an idea how to accomplish this replotting? It doesn't need
to be efficient, as the ping only comes in every second or so.. I thought
about buffering the incoming stdin and do a regular timer-based plot, but I
have now idea to do that.

Thank you in advance,

Jonas

# Update1:

I can get rid of this error by using:

l1.set_xdata(range(len(data)))

before the l1.set_ydata(..), but still it doesn't plot anything and the window
doesn't respond either. At least it shows the plot axis.

# Code

    
    
    import sys
    import re
    import numpy as np
    import matplotlib.pyplot as plt
    
    
    def main():
        if sys.stdin.isatty():
            print "Please use a pipe as stdin\nExample: ping stackoverflow.com | python script.py"
            return 0
        regex = re.compile('time=(\d+.\d+)')
        data = []
        fig = plt.figure()
    
        ax = fig.add_subplot(111)
        ax.set_yscale('linear') # or log
        ax.grid(True)
    
        l1, = ax.plot(data)
        fig.show()
    
        while True:
            #get ping, if no string: stream has ended
            line = sys.stdin.readline()
            if line == '':
                break
            #conversion: 64 bytes from 127.0.0.1: icmp_seq=0 ttl=45 time=100.873 ms --> 100.873
            match = regex.findall(line)
            number = 0.
    
            if len(match) > 1:
                raise ValueError()
            if len(match) == 1:
                try:
                    number = float(match[0])
                except ValueError as e:
                    print e
            #add number to array, plot the data
            data.append(number)
            l1.set_xdata(range(len(data)))
            l1.set_ydata(data)
    
            ax.relim()
            ax.autoscale()
    
            plt.draw()
            fig.canvas.flush_events()
        return 0
    
    if __name__ == '__main__':
        sys.exit(main())
    


Answer: Where you first have `plt.show()`, do not use that. Instead use `fig.show()`
there. And then just `plt.draw()` in your loop. The rest seems good. The thing
is that `plt.show()` starts a mainloop, locking further execution (outside the
events for the figure). `fig.show()` seems not to, however the buttons
probably will not work like this (I didn't try).

You will have to add autoscale though:

    
    
    ax.relim()
    ax.autoscale()
    plt.draw()
    # And this allows you to at least close the window (and crash the program by that ;))
    fig.canvas.flush_events()
    

But really it should be cleaner not use pyplot for this. For that look into
how to [embed
matplotlib](http://matplotlib.sourceforge.net/examples/user_interfaces/index.html)
into a gui, it is really easy and can be done for a couple of different gui
toolkits.

