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:
sys.stdin into python scriptnow in the python script:
NaN or just 0 as numberI 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:
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
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.
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())
Replotting the data seemed to work for me:
#!/usr/bin/python
import sys, 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 = [0]
fig = plt.figure()
ax = fig.add_subplot(111)
#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, = ax.plot(data, 'r')
plt.draw()
return 0
if __name__ == '__main__':
sys.exit(main())
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 into a gui, it is really easy and can be done for a couple of different gui toolkits.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With