2015-05-24 24 views
4

是有可能在python結束在從另一個函數的函數的循環? 這似乎並不工作結束在功能上的環從Python中的另一個功能

這裏是我的代碼:

from tkinter import* 
root = Tk() 
def loop(): 
    global superman 
    superman=False 
    while superman==False: 
     print("It's doing something") 
def endloop(): 
    global superman 
    superman=True 

btn_1 = Button(root, text="stop", command=endloop) 
btn_1.pack() 
btn_2 = Button(root, text="start", command=loop) 
btn_2.pack() 
+1

你沒有*電話*'endloop'。 – user2357112

+0

你是怎麼調用這兩個函數的?假設在線程中。 – bereal

+0

當然,但它實際上是在tkinter和一個按鈕調用endloop() –

回答

2

這裏的問題是,你的while循環只是不斷運行,這意味着沒有你的代碼的其餘部分還未獲得運行。這包括Tkinter的圖形用戶界面,這意味着你的程序沒有任何響應用戶事件,包括按鈕點擊,所以endloop不會被調用。

更一般地,你真的不能只運行一個永遠的功能,甚至達一秒鐘的一小部分,一個GUI程序中更多。一個單線程程序一次只能做一件事;如果它正在做的是永遠循環,那麼它就不會做其他任何事情。

因此,可以你做什麼

有兩種基本的選擇:

  1. 把循環在後臺線程。這意味着現在任何共享數據都必須顯式同步,這意味着循環無法觸及任何GUI小部件 - 但就您而言,事實證明這非常簡單。

  2. 分手的循環。讓它只執行一次迭代(或者說,如果它們非常快,則執行100次迭代),然後使用afterafter_idle來要求Tkinter調用另一個迭代(或100次迭代)和after的函數,等等,直到他們都完成了。

我會告訴你如何在這裏做的第一個。

import threading 
from tkinter import* 
root = Tk() 

def real_loop(): 
    while True: 
     with superman_lock: 
      if not superman: 
       return 
     print("It's doing something") 
def loop(): 
    global superman 
    global superman_lock 
    superman=False 
    superman_lock = threading.Lock() 
    thread = threading.Thread(target=real_loop, daemon=True) 
def endloop(): 
    global superman 
    with superman_lock: 
     superman=True 

btn_1 = Button(root, text="stop", command=endloop) 
btn_1.pack() 
btn_2 = Button(root, text="start", command=loop) 
btn_2.pack() 

對於其中僅共享數據是「停止」標誌,ConditionEvent通常比一個Lock更好的情況。 threading文檔解釋了不同種類的同步對象之間的差異,但不是真正的入門級別。關於monitors的維基百科文章可能是一個更好的學習起點,但是如果你能找到一個關於多線程的好教程(不一定是Python特有的; Python基本上與C pthreads庫,C++ Boost庫, Java stdlib等),那可能會更好。

有關更詳細的討論,請參閱Why your GUI app freezes

+0

感謝這個答案,奇怪的是我的主程序不會讓tkinter崩潰,但endloop不會終止循環。實際上,循環正在計算時間。 –

+3

@ThierryLincoln:請仔細閱讀。我沒有說它會讓Tkinter崩潰,而且不會。它會做什麼,使Tkinter停止響應事件。這意味着你的'endloop'函數永遠不會被調用。這就是爲什麼它不殺死循環。 – abarnert

1

假設你的loop()函數在後臺做了一些工作,將它放在一個單獨的線程中是個好主意。 使用線程可以與線程交互的事件。

此代碼是未經測試,但它應該給你一個想法,我是如何在某些情況下,解決這樣的東西:一旦

class Worker(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__() 

     self.run_event = threading.Event() 

    def run(self): 
     self.run_event.wait() 

     while self.run_event.is_set(): 
      print "It's doing something!" 

然後,您可以初始化線程,它會調用run()方法因爲它已啓動並將在第一行中等待設置run_event。

所以假設你有一個全局變量引用您的工作線程從按鈕觸發你的循環()方法是這樣的:

def loop(): 
    global worker_thread 
    worker_thread.run_event.set() 

通過設置run_event的self.run_event.wait()獲取通過並輸入工作循環。只要線程事件被設置,while循環就會運行。

而且你端環()可能是這個樣子:

dev endloop(): 
    global worker_thread 
    worker_thread.run_event.clear() 

只要你在你的線程清除run_event,while條件不再滿足,循環退出。

剛一說明: 此代碼是不完整的,也沒有測試,但可以給你一個想法如何可以做到。別忘了

import threading 

以及。

希望這有助於一點點。 格爾茨

+0

感謝居然回答我昨天開了一個線程對我的嚴重錯誤,我真的很困惑,現在,我要回饋這個方案明天:(: http://stackoverflow.com/questions/30413689/exception-with-tkinter -callback-because-loop-continue-in-the-the-background?noredirect = 1 –

+0

檢查你在這裏鏈接的其他stackoverflow問題,我只是在一分鐘前回答了我的想法;-) – p9teufel