2014-09-26 108 views
0

請運行以下示例。Python Tkinter:使用多線程時進度條出現故障

我爲我的應用程序創建了一個進度條,並通過按下「打開」按鈕彈出一個進度條。但是,進度條不填滿,看來該腳本是在停止

bar.set(i) 

時功能ProgressBarLoop被調用。

from Tkinter import Tk, Frame, BOTH, Label, Toplevel, Canvas, Button 
import thread 
import time 

class ProgressBar: 
    def __init__(self, parent, width, height): 
     master = Toplevel(parent) 
     master.protocol('WM_DELETE_WINDOW', self.hide) 
     self.master = master 
     self.master.overrideredirect(True) 
     ws = self.master.winfo_screenwidth() 
     hs = self.master.winfo_screenheight() 
     w = (True and ws*0.2) or 0.2 
     h = (True and ws*0.15) or 0.15 
     x = (ws/2) - (w/2) 
     y = (hs/2) - (h/2) 
     self.master.geometry('%dx%d+%d+%d' % (width, height * 2.5, x, y)) 

     self.mode = 'percent' 
     self.ONOFF = 'on' 
     self.width = width 
     self.height = height 
     self.frame = None 
     self.canvas = None 
     self.progressBar = None 
     self.backgroundBar = None 
     self.progressformat = 'percent' 
     self.label = None 
     self.progress = 0 

     self.createWidget() 
     self.frame.pack() 
     self.set(0.0)     # initialize to 0% 
    def createWidget(self): 
     self.frame = Frame(self.master, borderwidth = 1, relief = 'sunken') 
     self.canvas = Canvas(self.frame) 
     self.backgroundBar = self.canvas.create_rectangle(0, 0, self.width, self.height, fill = 'darkblue') 
     self.progressBar = self.canvas.create_rectangle(0, 0, self.width, self.height, fill = 'blue') 
     self.setWidth() 
     self.setHeight() 
     self.label = Label(self.frame, text = 'Loading...', width = 20) 
     self.label.pack(side = 'top') # where text label should be packed 
     self.canvas.pack() 
    def setWidth(self, width = None): 
     if width is not None: 
     self.width = width 
     self.canvas.configure(width = self.width) 
     self.canvas.coords(self.backgroundBar, 0, 0, self.width, self.height) 
     self.setBar() # update progress bar 
    def setHeight(self, height = None): 
     if height is not None: 
     self.height = height 
     self.canvas.configure(height = self.height) 
     self.canvas.coords(self.backgroundBar, 0, 0, self.width, self.height) 
     self.setBar() # update progress bar 
    def set(self, value): 
     if self.ONOFF == 'off': # no need to set and redraw if hidden 
     return 
     if self.mode == 'percent': 
     self.progress = value 
     self.setBar() 
     return 
    def setBar(self): 
     self.canvas.coords(self.progressBar,0, 0, self.width * self.progress/100.0, self.height) 
     self.canvas.update_idletasks() 
    def hide(self): 
     if isinstance(self.master, Toplevel): 
     self.master.withdraw() 
     else: 
     self.frame.forget() 
     self.ONOFF = 'off' 
    def configure(self, **kw): 
     mode = None 
     for key,value in kw.items(): 
     if key=='mode': 
      mode = value 
     elif key=='progressformat': 
      self.progressformat = value 
     if mode: 
     self.mode = mode 
def ProgressBarLoop(window, bar): 
    bar.configure(mode = 'percent', progressformat = 'ratio') 
    while(True): 
     if not window.loading: 
     break 
     for i in range(101): 
     bar.set(i) 
     print "refreshed bar" 
     time.sleep(0.001) 
    bar.hide() 

class Application(Frame): 
    def __init__(self, parent): 
     Frame.__init__(self, parent) 
     self.pack(fill = BOTH, expand = True) 
     parent.geometry('%dx%d+%d+%d' % (100, 100, 0, 0)) 
     Button(parent, text = "Open", command = self.onOpen).pack() 
    def onOpen(self, event = None): 
     self.loading = True 
     bar = ProgressBar(self, width=150, height=18) 
     thread.start_new_thread(ProgressBarLoop, (self, bar)) 
     while(True): 
     pass 
root = Tk() 
Application(root) 
root.mainloop() 

編輯:

試圖端午的回答後,它的工作原理,但我得到了以下錯誤:

Exception in thread Thread-1: 
Traceback (most recent call last): 
    File "/mnt/sdev/tools/lib/python2.7/threading.py", line 551, in __bootstrap_inner 
    self.run() 
    File "/mnt/sdev/tools/lib/python2.7/threading.py", line 504, in run 
    self.__target(*self.__args, **self.__kwargs) 
    File "/home/jun/eclipse/connScript/src/root/nested/test.py", line 88, in ProgressBarLoop 
    bar.set(i) 
    File "/home/jun/eclipse/connScript/src/root/nested/test.py", line 61, in set 
    self.setBar() 
    File "/home/jun/eclipse/connScript/src/root/nested/test.py", line 64, in setBar 
    self.canvas.coords(self.progressBar,0, 0, self.width * self.progress/100.0, self.height) 
    File "/mnt/sdev/tools/lib/python2.7/lib-tk/Tkinter.py", line 2178, in coords 
    self.tk.call((self._w, 'coords') + args))) 
RuntimeError: main thread is not in main loop 

回答

0

的問題是,你正在運行一個無限循環啓動後立即線程:

def onOpen(self, event = None): 
    self.loading = True 
    bar = ProgressBar(self, width=150, height=18) 
    thread.start_new_thread(ProgressBarLoop, (self, bar)) 
    while(True): # Infinite loop 
     pass 

因此,控制從不返回到Tk事件循環,因此進度條永遠不會更新。代碼應該工作正常,如果你刪除循環。

此外,你應該使用,而不是thread模塊threading模塊,如建議在Python docs

Note The thread module has been renamed to _thread in Python 3. The 2to3 tool will automatically adapt imports when converting your sources to Python 3; however, you should consider using the high-level threading module instead.

把它完全,這裏的onOpen應該是什麼樣子:

def onOpen(self, event = None): 
    self.loading = True 
    bar = ProgressBar(self, width=150, height=18) 
    t = threading.Thread(target=ProgressBarLoop, args=(self, bar)) 
    t.start() 

編輯:

試圖從多個線程更新tkinter小部件有點挑剔。當我在三個不同的系統上嘗試這個代碼時,我最終得到了三個不同的結果。在螺紋方法避免循環有助於避免任何錯誤:

def ProgressBarLoop(window, bar, i=0): 
    bar.configure(mode = 'percent', progressformat = 'ratio') 
    if not window.loading: 
     bar.hide() 
     return 
    bar.set(i) 
    print "refreshed bar" 
    i+=1 
    if i == 101: 
     # Reset the counter 
     i = 0 
    window.root.after(1, ProgressBarLoop, window, bar, i) 

但實際上,如果我們不使用ProgressBarLoop一個無限循環無論如何,真的沒有必要使用一個單獨的線程在所有。這個版本的ProgressBarLoop可以在主線程中調用,並且GUI不會被任何明顯的時間阻塞。

+0

嗯,我得到一個錯誤,當我關閉窗口RuntimeError:主線程不在主循環中 – 2014-09-26 18:30:42

+0

@JamestheGreat你運行上面包含的確切示例代碼,除了我的'onOpen'方法嗎?我一直無法重現該錯誤。在線程中使用'Tk'在安全方面有點棘手,但只要你的主線程在'root.mainloop()'中,它就會預期它的行爲正常。 – dano 2014-09-26 18:53:25

+0

我在Python 2.7和Linux上運行。 – 2014-09-26 19:19:50