互动情节失败,从我写了应该运行一个循环从标准输入读取三维坐标的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中不再是问题。
显然,标准输出上的写入进程缓冲区只有达到一定的大小后才会被刷新。添加一个明确的冲洗解决了这个问题:
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
感谢您的输入!我在'generate3d.py'的睡眠之前添加了一个'sys.stdout.flush()',问题消失了。无需将流程作为子流程运行。 – moooeeeep