2016-05-14 282 views
2

您好我有一個小型的python gui界面,帶有兩個按鈕,啓動(啓動計數器)和停止(即設置停止計數器),計數器是一個無限循環,因爲我不希望它結束​​,除非點擊第二個按鈕。問題是第二個按鈕無法點擊,而第一個按鈕的功能仍在運行。 我讀過,我需要使用線程,我已經嘗試過,但我不完全瞭解如何做到這一點。請幫忙。Python,Tkinter:我如何防止使用線程tkinter gui mainloop崩潰

from Tkinter import * 
import threading 


class Threader(threading.Thread): 
    def run(self): 
     for _ in range(10): 
      print threading.current_thread().getName() 

    def main(self): 
     import itertools 
     for i in itertools.count(1, 1): 
      print i 

    def other(self): 
     print "Other" 

m = Threader(name="main") 
o = Threader(name="other") 

try: 
    '''From here on we are building the Gui''' 
    root = Tk() 

    '''Lets build the GUI''' 
    '''We need two frames to help sort shit, a left and a right vertical frame''' 
    leftFrame = Frame(root) 
    leftFrame.pack(side=LEFT) 
    rightFrame = Frame(root) 
    rightFrame.pack(side=RIGHT) 
    '''Widgets''' 
    '''Buttons''' 
    playButton = Button(leftFrame, text="Play", fg="blue", command=m.main) 
    stopButton = Button(rightFrame, text="Stop", fg="red", command=o.other) 
    playButton.pack(side=TOP) 
    stopButton.pack(side=BOTTOM) 

    root.mainloop() 
except Exception, e: 
    print e 

回答

3

下面是使用threading的簡短示例。我拿出你的other函數,我不知道你爲什麼在這裏使用itertools。我也是這樣做的,只需使用簡單的線程示例進行設置即可。

有幾件事情:

您設置使用threading.Thread作爲Threader基類,但你從來沒有真正初始化的基類。

無論何時使用線程,您通常需要定義run方法,然後使用start()來啓動線程。致電start()將致電run

你需要使用線程來防止你的GUI阻塞,因爲tkinter只是一個巨型循環中的一個線程。所以,只要你有一些長時間運行的進程,它會阻塞這個線程直到當前進程完成。這就是爲什麼它放在另一個線程。 Python有一個叫做GIL的東西,它阻止了並行(我編造了這個詞),因爲它一次只能使用一個線程。相反,它使用時間分片,它們之間的GIL「輪詢」來給出多個任務同時運行的外觀。對於真正的並行處理,你應該使用multiprocessing

在下面的代碼中我使用了self.daemon = True。設置線程是一個守護進程會殺了它,當你退出主程序(在這種情況下,Tk的GUI)

from tkinter import * 
import threading, time 

class Threader(threading.Thread): 

    def __init__(self, *args, **kwargs): 

     threading.Thread.__init__(self, *args, **kwargs) 
     self.daemon = True 
     self.start() 

    def run(self): 

     while True: 
      print("Look a while true loop that doesn't block the GUI!") 
      print("Current Thread: %s" % self.name) 
      time.sleep(1) 

if __name__ == '__main__': 

    root = Tk() 
    leftFrame = Frame(root) 
    leftFrame.pack(side=LEFT) 
    rightFrame = Frame(root) 
    rightFrame.pack(side=RIGHT) 
    playButton = Button(leftFrame, text="Play", fg="blue", 
     command= lambda: Threader(name='Play-Thread')) 
    stopButton = Button(rightFrame, text="Stop", fg="red", 
     command= lambda: Threader(name='Stop-Thread')) 
    playButton.pack(side=TOP) 
    stopButton.pack(side=BOTTOM) 
    root.mainloop() 
0

對於作爲計數器這樣簡單的事情,Tkinter的年代後()方法通常是更好的選擇。您可以使用實例變量將其設置爲打開和關閉。

class TimerTest(): 
    def __init__(self, root): 
     self.root=root 

     Button(root, text="Play", fg="blue", 
          command=self.startit).grid(row=1, column=0) 
     Button(root, text="Stop", fg="red", 
          command=self.stopit).grid(row=1, column=1) 

     self.is_running=True 
     self.count=IntVar() 
     Label(root, textvariable=self.count, 
       bg="lightblue").grid(row=0, column=0, columnspan=2, sticky="ew") 

    def startit(self): 
     self.is_running=True 
     self.increment_counter() 

    def increment_counter(self): 
     if self.is_running: 
      c=self.count.get() 
      c += 1 
      self.count.set(c) 
      root.after(1000, self.increment_counter) ## every second 

    def stopit(self): 
     self.is_running = False 

root = Tk() 
TT=TimerTest(root) 
root.mainloop()