我正在寻找一种生成可在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
参数而出现警告: UserWarning:此图包括不兼容的轴和 matplotlib当前正在使用非GUI后端。
(请注意,我需要tight_layout
,因为否则通常会剪切轴标签或具有多个子图的图形的边距会很差。)
Linux / Windows Spyder,内联图:使用plt.plot(...); plt.show()
(而不是fig.plot(...); fig.show()
面向对象的方式),内联图在程序执行期间显示。
如何编写在程序执行过程中(可能是在等待按键时)生成交互式图表的代码,这些代码可以从Windows命令行,Linux命令行和Spyder正确运行?
编辑:用plt.show()
代替fig.show()
可以正确显示绘图,但是在IPython之外,它将阻止脚本执行,直到我单击图形窗口的关闭按钮。当有多个数字或计算尚未完成时,这将非常麻烦。并且plt.show(block=False)
与fig.show()
具有相同的行为。
我正在使用非定制的Anaconda 5.1环境以及Python 3.6.4,matplotlib 2.1.2,spyder 3.2.6。在“ Spyder:工具>首选项> IPython>后端”中,设置为“内联”或“自动”(并在更改此设置后重新启动内核。)
答案 0 :(得分:0)
以下是您所追求的:
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,但是没有启动任何适当的事件循环。因此,即使在显示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()
答案 1 :(得分:0)
这是一种可行的方法,但不适用于所有环境:
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()