2016-01-22 70 views
3

Windows 7的使用。如果我打开一个普通的IPython在命令行输入端子()我可以输入:Matplotlib冻结当Spyder的

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

但是,如果我做同样的事情在Spyder的,只要我询问用户输入,Matplotlib窗口冻结,所以我无法与之交互。显示提示时,我需要与剧情互动。

在这两个Spyder的和普通的控制台,matplotlib.get_backend()返回“Qt4Agg”

编辑:为了澄清,我有matplotlib设立它在自己的窗口显示出来,而不是嵌入式的PNG。 (我不得不设置Backend:Automatic来获得这种行为)

另外,在Spyder中,plot会在plt.plot()后立即打开。在常规控制台中,它只会在plt.show()后打开。另外,如果在Spyder中输入input()后按Ctrl-C,整个控制台会意外挂起。比。在IPython中,它只会引发KeyboardInterrupt并将控制权返回给控制台。

编辑:更完整的示例:在IPython控制台中工作,而不是在Spyder(冻结)中。根据用户输入,想要移动情节。

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]) 

大量编辑的:我相信这是与输入)阻塞的Qt的问题(。我的解决方法是,如果这个问题没有得到支持,那就是构建一个嵌入了Matplotlib图的Qt窗口,然后通过窗口获取键盘输入。

+0

此外,有没有人真的使用Spyder?我应该使用别的东西来替代MATLAB吗?我主要希望使用Python作为一种更为理智的语言来构建更大的数值应用程序,但要留在更具实验性的MATLAB风格环境中。 –

+0

(Spyder dev这里*)Spyder很多用户,不知道多少,但是我们每年有大约200,000次下载。 –

+0

请发布您正在使用的代码的简单版本,以便我们了解您要实现的目标。您发布的伪代码(特别是GOTO 2步骤)是不够的。谢谢:-) –

回答

2

更换input()后多了很多挖我得出的结论是你应该只是制作一个GUI。我建议你使用PySide或PyQt。为了使matplotlib有一个图形窗口,它运行一个事件循环。任何点击或鼠标移动都会触发一个触发图形部分执行某些操作的事件。脚本的问题是每一个代码都是顶级的;它建议代码顺序运行。

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

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

有很多摆弄和移动你调用fucntions的方式,你可以让thread_input函数工作。

幸运的是,PySide和PyQt允许您手动进行处理GUI事件的调用。我添加了一个方法,要求在单独的线程中输入并通过等待结果进行循环。它在等待它告诉GUI来处理事件。如果你安装了PySide(或PyQt),并且将它用作matplotlib的后端,return_input方法将有望工作。

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内核的一些问题。

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() 
+0

这是例子不起作用。 Matplotlib只能从Spyder运行,这是一个问题。交互模式也已启用,如问题中所述。我在线程中用户输入做了一个类似的例子,但关键是在循环中调用pyplot.pause(),以便matplotlib窗口不会冻结。如果你能以某种方式解决这个问题,那么你的答案似乎比我的更好。 –

+0

我建议你只是做一个GUI。无论如何你正在申请一个应用程序。脚本只能用于小型快速运行。如果您使用的是PyQt后端,我添加了return_input方法,希望能够工作。我编辑了我的答案,以解释事情是如何工作的。 – HashSplat

+0

我同意你的看法,但这*应该是一次性的。用户不需要了解Qt事件循环就可以获取循环中的键盘输入。在MATLAB中这是一个非问题。 IMO应该在错误报告中,尽管它是架构的一个副作用。 –

3

有人比我知道得更好,如果可能请发表一个答案。我对Python/Scipy/Spyder知之甚少

这是我写的一个kludgy模块,它可以在Spyder下阻止Matplotlib窗口在输入()未决时冻结)。

必须调用事先prompt_hack.start()prompt_hack.finish(),并与prompt_hack.input()

prompt_hack.py

import matplotlib.pyplot 
import time 
import threading 

# Super Hacky Way of Getting input() to work in Spyder with Matplotlib open 
# No efforts made towards thread saftey! 

prompt = False 
promptText = "" 
done = False 
waiting = False 
response = "" 

regular_input = input 

def threadfunc(): 
    global prompt 
    global done 
    global waiting 
    global response 

    while not done: 
     if prompt: 
      prompt = False 
      response = regular_input(promptText) 
      waiting = True 
     time.sleep(0.1) 

def input(text): 
    global waiting 
    global prompt 
    global promptText 

    promptText = text 
    prompt = True 

    while not waiting: 
     matplotlib.pyplot.pause(0.01) 
    waiting = False 

    return response 

def start(): 
    thread = threading.Thread(target = threadfunc) 
    thread.start() 

def finish(): 
    global done 
    done = True 
+0

谢谢你。这为我节省了很多麻烦。对于任何尝试将这个用于多次调用input()的人,请确保在start()调用中重置所有全局变量。 – JAustin