2016-01-22 73 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