在Windows、Linux、Spyder、命令行中生成matplotlib交互式图形的可移植方法



我正在寻找一种在Windows和Linux、命令行或Spyder中生成交互式图形的方法。例如,以下脚本:

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1, tight_layout=True)
#plt.ion() # doesn't seem to do much
ax.plot([0,1,2],[0,3,2])
fig.show()
#plt.show(block=False) # similar behavior as fig.show()
#plt.pause(1)
input('Press enter to quit.')

不同环境下的行为:

  • Linux命令行:在脚本等待用户输入时,将显示并响应绘图窗口。即使程序继续运行(不是在这个简短的例子中(,窗口也会保持不变,尽管缩放按钮不再工作。这是所需的行为。

  • Windows命令行:显示一个空的无响应绘图窗口,该窗口在程序结束时消失。添加plt.pause()会产生交互式绘图,但它仅在指定的秒数内做出响应。

  • 带有iPython的Linux/Windows Spyder,配置为自动绘图:图形显示并响应,但仅在脚本完成后。

  • Linux/Windows Spyder,配置为内联绘图:脚本完成后会显示绘图,但由于tight_layout=True参数而出现警告:用户警告:此图包括与tight_layout不兼容的轴,因此结果可能不正确matplotlib当前使用非GUI后端。(请注意,我需要tight_layout,因为在其他情况下,轴标签经常被剪切,或者具有多个子图的图形的边距不好。(

  • Linux/Windows Spyder,内联图:使用plt.plot(...); plt.show()(而不是fig.plot(...); fig.show()面向对象的方式(,内联图会在程序执行期间显示。

如何编写在程序执行期间(可能在等待按键时(生成交互式绘图的代码,以便从Windows命令行、Linux命令行和Spyder正确运行?

编辑:plt.show()而不是fig.show()将导致正确显示绘图,但在IPython之外,它会阻止脚本的执行,直到我单击图形窗口的关闭按钮。当有多个数字或计算尚未完成时,这相当麻烦。并且CCD_ 8具有与CCD_ 9相同的行为。

我使用的是一个非自定义的Anaconda 5.1环境,Python 3.6.4,matplotlib 2.1.2,spyder 3.2.6。在Spyder中:"工具">"首选项">"IPython">"后端",设置为"内联"或"自动"(并在更改此设置后重新启动内核。(

您想要的似乎是以下内容:

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1, tight_layout=True)
plt.ion()
ax.plot([0,1,2],[0,3,2])
plt.show()
plt.pause(0.1)
input('Press enter to quit.')
plt.close()

一般来说,python程序是线性求值的。这与具有阻塞命令行(input(但具有非阻塞GUI的愿望相矛盾
上面的情况是创建了一个GUI,但没有启动任何proper事件循环。因此,即使在显示GUI之后,代码也可以继续执行。这样做的缺点显然是,如果没有事件循环,就无法与GUI交互。

如果需要显示绘图窗口并且需要一些用户输入,或者在显示完全交互式图形之后要执行进一步的代码,则可以在GUI事件循环中运行这样的代码。

import matplotlib.pyplot as plt
def run_after(callback, pause=10, figure=None):
figure = figure or plt.gcf()
timer = figure.canvas.new_timer(interval=pause)
timer.single_shot = True
timer.add_callback(callback)
timer.start()
return timer

fig, ax = plt.subplots(1, 1, tight_layout=True)
ax.plot([0,1,2],[0,3,2])
# This function contains the code to be executed within the GUI event loop
def mycallback():
import tkinter
from tkinter import simpledialog
root = tkinter.Tk()
root.withdraw()
w = simpledialog.askinteger("Title", "How many numbers do you want to print")
for i in range(w):
print(i)

cb = run_after(mycallback)
plt.show()

这里有一种方法可以工作,但并不适用于所有环境:

import matplotlib.pyplot as plt
import sys
import matplotlib
def plt_show_interactive():
"""Show plot windows, with interaction if possible; prompt to continue.""" 
in_ipython = ('get_ipython' in globals())
inline_backend = ('inline' in matplotlib.get_backend())
in_linux = (sys.platform == 'linux')
if inline_backend:
plt.show()
elif not in_linux and in_ipython:
print("Press Ctrl-C to continue.")
try:    
while True:
plt.pause(0.5)
except KeyboardInterrupt:
print("Continuing.")
elif in_linux and not in_ipython:
# Command line: plots are interactive during wait for input.
plt.show(block=False)
input("Press ENTER to continue.")
elif in_linux and in_ipython:
# Loop with plt.pause(1) causes repeated focus stealing.
plt.pause(1)
print("Sorry, plots are not interactive until program has finished.")
elif not in_linux and not in_ipython:
# Ctrl-C is handled differently here.
plt.pause(1)
input("Sorry, not interactive. Press ENTER to continue.")

def prompt_if_not_ipython(verb="end"):
"""Ask user to press ENTER if not we're not inside IPython."""
if ('get_ipython' not in globals()):
input("Press ENTER to %s." % verb)

fig, ax = plt.subplots(1, 1, tight_layout=True)
ax.plot([0,1,2],[0,3,2])
plt_show_interactive()
prompt_if_not_ipython()

相关内容

  • 没有找到相关文章

最新更新