互动情节失败,从我写了应该运行一个循环从标准输入读取三维坐标的Python脚本其他程序

问题描述:

管道输入初始化和对mpl_toolkits.mplot3d.Axes3D散点图显示最近读的坐标。互动情节失败,从我写了应该运行一个循环从标准输入读取三维坐标的Python脚本其他程序

,当我通过提示手动输入坐标,它的工作原理(其给出了一些折旧警告虽然):

$ python plot3d.py 
.5 .5 .5 
/usr/lib/pymodules/python2.7/matplotlib/backend_bases.py:2407: MatplotlibDeprecationWarning: Using default event loop until function specific to this GUI is implemented 
    warnings.warn(str, mplDeprecation) 

当我喂坐标从文件到程序也适用:

$ head generated 
0.56 0.40 0.55 
0.61 0.49 0.60 
0.48 0.39 0.48 
0.39 0.33 0.39 
0.32 0.28 0.32 
0.35 0.31 0.35 
0.50 0.47 0.50 
0.40 0.38 0.40 
0.37 0.35 0.37 
0.51 0.50 0.51 
$ python plot3d.py < generated 

但是,当我将生成脚本的输出直接输入绘图脚本并且生成脚本在每次迭代后执行time.sleep()时,它不起作用:

$ python generate3d.py | python plot3d.py 

工作行为,该图窗口被打开时,该显示示出一个点的散点图。不工作的行为是,数字窗口打开,但它只显示灰色背景,没有轴或点。

下面是绘制脚本代码:

from mpl_toolkits.mplot3d import Axes3D 
import matplotlib.pyplot as plt 

plt.ion() 

fig = plt.figure() 
ax = fig.add_subplot(111, projection='3d') 

plotted_items = None 
while True: 
    if plotted_items is not None: 
     plotted_items.remove() 
    try: 
     x,y,z = map(float, raw_input().split()) 
    except: 
     break 
    else: 
     plotted_items = ax.scatter([x],[y],[z]) 
     ax.set_xlim(-5, 5) 
     ax.set_ylim(-5, 5) 
     ax.set_zlim(-5, 5) 
     plt.pause(0.1) 

下面是生成脚本代码:

from random import random 
import time 

last = None 
while True: 
    if last is None: 
     last = [random(), random(), random()] 
    offset = random() 
    for i in range(len(last)): 
     last[i] += 0.25 * (offset - last[i]) 
    print "%.2f %.2f %.2f" % tuple(last) 
    # the value of this sleep duration influences how long it takes to initialize 
    # the plot in the other script!? 
    time.sleep(0.5) 

我观察到的睡眠时间在生成脚本的影响可能与轴初始化所需的时间成正比。睡眠时间接近零时,几乎不需要初始化时间。睡眠时间为0.1时,图表显示已经过了很长时间。 我还没有探讨显示数据的延迟,当它终于在那里。

任何人都可以重现此问题?任何人都可以帮助我理解并解决这个问题难道我做错了什么?

潜在有用的信息:

$ lsb_release -d 
Description: Ubuntu 14.04.5 LTS 
$ python --version 
Python 2.7.6 

>>> matplotlib.__version__ 
'1.3.1' 

我可以matplotlib版本1.4.3和Python 2.7重现。原因似乎是由于蟒蛇stdin阅读功能阻塞,直到管道关闭(描述here),

这个问题的起源是在这些阅读机制用Python实现的方式(从Python的问题,请参见discussion on this issue跟踪器)。在Python 2.7.6中,实现依赖于C的stdio库。

他们中的链接使用的解决方案是运行geneate3d.py作为子进程并设置非阻塞标志,

from subprocess import Popen, PIPE 
import time 
from fcntl import fcntl, F_GETFL, F_SETFL 
from os import O_NONBLOCK, read 

from mpl_toolkits.mplot3d import Axes3D 
import matplotlib.pyplot as plt 
import sys 

plt.ion() 

fig = plt.figure() 
ax = fig.add_subplot(111, projection='3d') 

# run the shell as a subprocess: 
p = Popen(['python', 'generate3d.py'], 
     stdin = PIPE, stdout = PIPE, stderr = PIPE, shell = False) 
# set the O_NONBLOCK flag of p.stdout file descriptor: 
flags = fcntl(p.stdout, F_GETFL) # get current p.stdout flags 
fcntl(p.stdout, F_SETFL, flags | O_NONBLOCK) 
# issue command: 
p.stdin.write('command\n') 
# let the shell output the result: 
time.sleep(0.1) 

plotted_items = None 
while True: 
    if plotted_items is not None: 
     try: 
      plotted_items.remove() 
     except ValueError: 
      print(plotted_items) 
      pass 
    try: 
     x,y,z = map(float, read(p.stdout.fileno(), 1024).split(' ')) 
    except OSError: 
     time.sleep(0.5) 
    except: 
     raise 
    else: 
     plotted_items = ax.scatter([x],[y],[z]) 
     ax.set_xlim(-5, 5) 
     ax.set_ylim(-5, 5) 
     ax.set_zlim(-5, 5) 
     plt.pause(0.1) 
     plt.draw() 

你需要generate3d.py打印后添加sys.stdout.flush()

似乎应该有可能在sys.stdin中设置O_NONBLOCK标志,例如flags = fcntl(sys.stdin, F_GETFL)fcntl(sys.stdin, F_SETFL, flags | O_NONBLOCK)。但是,这对我不起作用。根据bug tracker,我认为这在Python 3.0中不再是问题。

+0

感谢您的输入!我在'generate3d.py'的睡眠之前添加了一个'sys.stdout.flush()',问题消失了。无需将流程作为子流程运行。 – moooeeeep

显然,标准输出上的写入进程缓冲区只有达到一定的大小后才会被刷新。添加一个明确的冲洗解决了这个问题:

from random import random 
import time 
import sys 

last = None 
while True: 
    if last is None: 
     last = [random(), random(), random()] 
    offset = random() 
    for i in range(len(last)): 
     last[i] += 0.25 * (offset - last[i]) 
    print "%.2f %.2f %.2f" % tuple(last) 
    # flush before sleep to make sure the data is actually written on time 
    sys.stdout.flush() 
    time.sleep(1.0) 

另外,原始脚本可以在Python的缓冲模式运行:

$ python -u generate3d.py | python plot3d.py