Matplotlib在Spyder中使用input()时冻结 [英] Matplotlib Freezes When input() used in Spyder

查看:159
本文介绍了Matplotlib在Spyder中使用input()时冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Windows7.如果在命令行上打开普通的ipython终端,则可以输入:

Windows 7. If I open a plain ipython terminal at the command line I can type:

import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4, 5])
plt.show(block=False)
input("Hello ")

但是,如果我在Spyder中执行相同的操作,则当我请求用户输入时,Matplotlib窗口会冻结,因此无法与其进行交互.显示提示时,我需要与剧情进行交互.

But if I do the same thing in Spyder, as soon as I ask for user input, the Matplotlib window freezes, so I can't interact with it. I need to interact with the plot while the prompt is showing.

在Spyder和普通控制台中,matplotlib.get_backend()返回'Qt4Agg'

In both Spyder and the plain console, matplotlib.get_backend() return 'Qt4Agg'

编辑:为澄清起见,我在自己的窗口中设置了matplotlib,而不是将其嵌入为PNG. (我必须将后端:最初设置为自动"才能获得此行为)

To clarify, I have matplotlib set up where it shows up in its own window, not embedded as a PNG. (I had to set Backend: Automatic originally to get this behavior)

顺便说一句,在Spyder中,该图在plt.plot()之后立即打开.在常规控制台中,它仅在plt.show()之后打开.另外,如果我在Spyder中输入input()后按Ctrl-C,则整个控制台会意外挂起. VS.在IPython中,它只是引发KeyboardInterrupt并将控制权返回到控制台.

As an aside, in Spyder, the plot opens instantly after plt.plot(). In the regular console, it only opens after plt.show(). Also, if I press Ctrl-C after typing input() in Spyder, the entire console hangs unexpectedly. Vs. in IPython, it just raises KeyboardInterrupt and returns control to the console.

:甚至更完整的示例:在IPython控制台中有效,而不在Spyder(冻结)中有效.要根据用户输入来移动图.

Even more complete example: Works in IPython console, not in Spyder (freezes). Want to move the plot around, according to user input.

import matplotlib.pyplot as pl

def anomaly_selection(indexes, fig, ax):
    selected = []

    for i in range(0, len(indexes)):
        index = indexes[i]
        ax.set_xlim(index-100, index+100)
        ax.autoscale_view()
        fig.canvas.draw()
        print("[%d/%d] Index %d " % (i, len(indexes), index), end="")
        while True:   
            response = input("Particle? ")
            if response == "y":
                selected.append(index)
                break
            elif response == "x":
                return selected
            elif response == "n":
                break

fig, ax = pl.subplots(2, sharex=True)
ax[0].plot([1, 2, 3, 4, 5]) # just pretend data
pl.show(block=False)

sel = anomaly_selection([100, 1000, 53000, 4300], fig, ax[0])

许多修改:我认为这是input()阻止Qt的问题.如果此问题没有解决,我的解决方法是构建一个嵌入了Matplotlib图的Qt窗口,然后通过该窗口获取键盘输入.

Lots of I believe this is an issue with input() blocking Qt. My workaround if this question doesn't get traction, is to build a Qt window with the Matplotlib plot embedded in it, and get keyboard input through the window instead.

推荐答案

经过更多的挖掘,我得出的结论是,您应该只是制作一个GUI.我建议您使用PySide或PyQt.为了使matplotlib具有图形窗口,它运行事件循环.任何单击或鼠标移动都会触发一个事件,该事件触发图形部分执行某项操作.脚本编写的问题在于,每一段代码都是顶级代码;这表明代码正在按顺序运行.

After a lot more digging I came to the conclusion that you simply should be making a GUI. I would suggest you use PySide or PyQt. In order for matplotlib to have a graphical window it runs an event loop. Any click or mouse movement fires an event which triggers the graphical part to do something. The problem with scripting is that every bit of code is top level; it suggests the code is running sequentially.

当您手动将代码输入到ipython控制台中时,它可以工作!这是因为ipython已经启动了GUI事件循环.您调用的每个命令都在事件循环中处理,从而也允许发生其他事件.

When you manually input the code into the ipython console it works! This is because ipython has already started a GUI event loop. Every command that you call is handled within the event loop allowing other events to happen as well.

您应该创建一个GUI并将该GUI后端声明为相同的matplotlib后端.如果单击按钮会触发anomaly_selection函数,则该函数将在单独的线程中运行,并且应允许您仍在GUI中进行交互.

You should be creating a GUI and declare that GUI backend as the same matplotlib backend. If you have a button click trigger the anomaly_selection function then that function is running in a separate thread and should allow you to still interact within the GUI.

在调用函数的方式上有很多摆弄和四处走动的情况,您可以使thread_input函数正常工作.

With lots of fiddling and moving around the way you call fucntions you could get the thread_input function to work.

幸运的是,PySide和PyQt允许您手动调用以处理GUI事件.我添加了一种方法,该方法在一个单独的线程中要求输入,并循环等待结果.等待时,它告诉GUI处理事件.如果您已安装PySide(或PyQt)并将其用作matplotlib的后端,则return_input方法将有望工作.

Fortunately, PySide and PyQt allow you to manually make a call to process GUI events. I added a method that asks for input in a separate thread and loops through waiting for a result. While it is waiting it tells the GUI to process events. The return_input method will hopefully work if you have PySide (or PyQt) installed and are using it as matplotlib's backend.

import threading

def _get_input(msg, func):
    """Get input and run the function."""
    value = input(msg)
    if func is not None:
        func(value)
    return value
# end _get_input

def thread_input(msg="", func=None):
    """Collect input from the user without blocking. Call the given function when the input has been received.

    Args:
        msg (str): Message to tell the user.
        func (function): Callback function that will be called when the user gives input.
    """
    th = threading.Thread(target=_get_input, args=(msg, func))
    th.daemon = True
    th.start()
# end thread_input

def return_input(msg=""):
    """Run the input method in a separate thread, and return the input."""
    results = []
    th = threading.Thread(target=_store_input, args=(msg, results))
    th.daemon = True
    th.start()
    while len(results) == 0:
        QtGui.qApp.processEvents()
        time.sleep(0.1)

    return results[0]
# end return_input

if __name__ == "__main__":

    stop = [False]
    def stop_print(value):
        print(repr(value))
        if value == "q":
            stop[0] = True
            return
        thread_input("Enter value:", stop_print)

    thread_input("Enter value:", stop_print)
    add = 0
    while True:
        add += 1
        if stop[0]:
            break

    print("Total value:", add)

此代码似乎对我有用.尽管确实给了我ipython内核一些问题.

This code seems to work for me. Although it did give me some issues with ipython kernel.

from matplotlib import pyplot as pl

import threading


def anomaly_selection(selected, indexes, fig, ax):
    for i in range(0, len(indexes)):
        index = indexes[i]
        ax.set_xlim(index-100, index+100)
        ax.autoscale_view()
        #fig.canvas.draw_idle() # Do not need because of pause
        print("[%d/%d] Index %d " % (i, len(indexes), index), end="")
        while True:
            response = input("Particle? ")
            if response == "y":
                selected.append(index)
                break
            elif response == "x":
                selected[0] = True
                return selected
            elif response == "n":
                break

    selected[0] = True
    return selected


fig, ax = pl.subplots(2, sharex=True)
ax[0].plot([1, 2, 3, 4, 5]) # just pretend data
pl.show(block=False)

sel = [False]
th = threading.Thread(target=anomaly_selection, args=(sel, [100, 1000, 53000, 4300], fig, ax[0]))
th.start()
#sel = anomaly_selection([100, 1000, 53000, 4300], fig, ax[0])


while not sel[0]:
    pl.pause(1)
th.join()

这篇关于Matplotlib在Spyder中使用input()时冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆