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.stdininto python script
now in the python script:
- regex the answer time, if there is no answer time: use
NaNor 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())
Where you first have
plt.show(), do not use that. Instead usefig.show()there. And then justplt.draw()in your loop. The rest seems good. The thing is thatplt.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:
But really it should be cleaner not use pyplot for this. For that look into how to embed matplotlib into a gui, it is really easy and can be done for a couple of different gui toolkits.