3

我最近开始修补 Python 和 Enthought Python Distribution(免费版本之一)附带的 matplotlib 模块(1.1.0)。我想到了一个我可以做的有趣的项目,并想出了这样的事情:

  • 获取互联网地址的 ping
  • 通过管道将其sys.stdin导入python脚本

现在在python脚本中:

  • 正则表达式回答时间,如果没有回答时间:使用NaNor 只是 0 作为数字
  • 通过 matplotlib 绘制数据
  • 不断添加数据

我设法通过以下方式获得 ping:ping stackoverflow.com | python script.py 得到应答时间并不是特别难。但是在绘制数据时。我被困住了。我知道 matplotlib 中有一个动画模块,但我认为基于计时器的绘图会比这更难编程,而且我不知道如何使用事件。我想要的是:

  • 等待 sys.stdin 获得一个新字符串,从而获得 ping 时间
  • 将其添加到数据数组
  • 绘制数据数组 但似乎没那么容易。除此之外,错误处理还没有完成。不幸的是我找不到任何类似的代码,虽然我做了很多谷歌搜索:/也许这个设计不是这样的。。

有谁知道如何完成这个重新绘制?它不需要高效,因为 ping 每隔一秒左右才会出现一次。我考虑过缓冲传入的标准输入并执行基于计时器的常规绘图,但我现在有了这样做的想法。

先感谢您,

乔纳斯

更新1:

我可以使用以下方法消除此错误:

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

之前l1.set_ydata(..),但它仍然没有绘制任何内容,并且窗口也没有响应。至少它显示了绘图轴。

代码

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())
4

2 回答 2

2

重新绘制数据似乎对我有用:

#!/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())
于 2012-10-25T21:00:08.650 回答
1

在你第一次拥有的地方plt.show(),不要使用它。而是fig.show()在那里使用。然后就plt.draw()在你的循环中。其余的似乎都不错。事情是plt.show()启动一个主循环,锁定进一步的执行(在图的事件之外)。fig.show()似乎没有,但是按钮可能不会像这样工作(我没有尝试)。

您将不得不添加自动缩放:

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()

但实际上它应该更干净,不要为此使用 pyplot。对于如何将 matplotlib 嵌入gui 的研究,这真的很容易,并且可以使用几个不同的 gui 工具包来完成。

于 2012-09-01T13:55:26.360 回答