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

时间:2018-11-16 14:21:13

标签: python matplotlib spyder

我正在寻找一种生成可在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>后端”中,设置为“内联”或“自动”(并在更改此设置后重新启动内核。)

2 个答案:

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