Tkinter冷冻 - 线程?进度条?

时间:2014-07-18 23:24:33

标签: multithreading python-2.7 tkinter freeze

我编写了一些代码,用于创建在更改json文件时(通过其他程序)更新的progressbars。这个想法是这个代码将与一个更大的项目结合起来,为用户提供有关json文件的信息。

我的问题:如果我激活其中一个进度条,则整个GUI会冻结。那个进度条工作得很好,但我不能启动其他任何一个。

我的计划:我已经阅读了tkinter和python,我相信我想要的是每个进度条在不同的线程上运行。我试过了;但它仍然冻结。 Quit按钮也无法正常工作。有什么建议?或者更简单的方法来解决这个问题?

这是我的代码(抱歉长度):

import threading
import time
from Tkinter import *
import json
import Queue
from time import sleep

master = Tk()

#somewhere accessible to both:
callback_queue = Queue.Queue()

#see (thread_working.py) for debugging help



######ProgressBar Code (my_progressbar.py)


class Meter(Frame):
    #make a progress bar widget
    def __init__(self, master, fillcolor = 'darkblue', text='', value=0.0, **kw):
        Frame.__init__(self, master, bg='lightgray', width=350, height=20)
        self.configure(**kw)

        self._c = Canvas(self, bg=self['bg'], width=self['width'], height=self['height'], highlightthickness=0, relief='flat', bd=0)
        self._c.pack(fill='x', expand=1)
        self._r = self._c.create_rectangle(0,0,0, int(self['height']), fill=fillcolor, width=0)
        self._t = self._c.create_text(int(self['width'])/2, int(self['height'])/2, text='')

        self.set(value)

    def set(self, value=0.0):
        text = str(int(round(100 * value))) + ' %'
        self._c.coords(self._r, 0, 0, int(self['width']) * value,int(self['height']))
        self._c.itemconfigure(self._t, text=text)
        self.update()


progbarlock = False # start with the prograssbar marked as not occupied


class guibuild:


    def __init__(self):
        guibuild.progbarlock = False
        self.progbar = Meter(theframe) #makes the progressbar object
        self.progbar.set(0.0) #sets the initial value to 0
        self.progbar.pack(side=LEFT)

        self.mybutton = Button(theframe, text="My Button", command = self.interval).pack(side = LEFT)

    def stop_progbar(self):
        self.progbar.stop()

    def interval(self):
        if guibuild.progbarlock == False:
            counter = 0
            #sleep(5) #slow down the loop

            guibuild.progbarlock = True

            i = float(0) #i is going to be the value set on the progressbar

            while i <= 1.0: #will stop at 100%

                the_file = open("sample.json")
                data = json.load(the_file)
                curr = data["curr_line"]
                total = data["total_lines"]

                if counter == total:
                    print "stop" #for debug purposes
                    self.stop_progbar
                    #pass
                elif curr == counter:
                    #print "curr = counter" #debug
                    pass
                elif curr == counter+1:
                    i += 1.0/total
                    #print i #debug
                    self.progbar.set(i) #apply the new value of i to the progressbar
                    print "the progressbar should reflect", str(int(round(i*100))) +"%progress right now"
                    print "the counter will increase"
                    counter += 1
                    #print counter #debug purposes
                    self.stop_progbar
                    #print "test"
                else:
                    print "something is wrong - running.json is not available"

                time.sleep(5)


            guibuild.progbarlock = False



##########################################################################################################
def create_bar():
    guibuild()

######Make the actual GUI
#master = Tk()

global theframe #makes the frame object global
theframe = Frame(master)
theframe.pack()

frame2 = Frame(master)
frame2.pack(side=BOTTOM)


quitbutton = Button(frame2, text="Quit", fg = "darkred", command = master.quit).pack(side=LEFT) 
#original command was theframe.quit, original location was theframe (vs master)


##############Threading Stuff#####################

beginbutton = Button(theframe, text="Make Bar", command =create_bar).pack(side = BOTTOM)

def my_thread(func_to_call_from_main_thread):

    callback_queue.put(guibuild)
    #this must be here and below


def from_main_thread_nonblocking():
    while True:
        try:
            callback = callback_queue.get(True) #doesn't block #was false
        except Queue.Empty: #raised when queue is empty
            break
        callback()

 #this allows for it to be activated several times
threading.Thread(target=guibuild).start()

while True:
    master.mainloop()
    master.destroy()
    from_main_thread_nonblocking()
master.destroy()

sample.json看起来像这样:

{
  "curr_line": 1, 
  "total_lines": 5
}

编辑:我修复了这个问题,但发现了一个新错误。一旦修复了bug,就会发布更正后的代码,以防有人来寻找答案并找到答案。

1 个答案:

答案 0 :(得分:0)

我修复了所有错误,并希望为将来的搜索者分享这个答案。正如@BryanOakley所说,Tkinter不适用于线程。我研究了一些,并决定删除所有的while循环并使用Tkinter after()方法。下面是我的代码的修改部分。

class guibuild:
    def __init__(self):
        self.master = master
        guibuild.progbarlock = False
        self.progbar = Meter(theframe) #makes the progressbar object
        self.progbar.set(0.0) #sets the initial value to 0
        self.progbar.pack(side=LEFT)
        self.counter = 0
        self.i = float(0) #i is the value set to the progressbar

        self.mybutton = Button(theframe, text="My Button", command = self.interval).pack(side = LEFT)

    def stop_progbar(self):
        self.progbar.stop()

    def interval(self):
        if guibuild.progbarlock == False:

            guibuild.progbarlock = True

            the_file = open("sample_running.json")
            data = json.load(the_file)
            curr = data["curr_line"]
            command = data["curr_line_text"]
            total = data["total_lines"]


            print self.counter

            if self.i == 1.0:
                self.stop_progbar
                print "100% - process is done"
                self.master.after_cancel(self.interval)
            elif self.counter == total:
                print "stop" #for debug purposes
                self.i = 1.0
                self.master.after(5000, self.interval)
            elif curr == self.counter:
                print self.counter
                print self.i
                self.master.after(5000, self.interval)
            elif curr == self.counter+1:
                self.i += 1.0/total
                print self.i #debug
                self.progbar.set(self.i) #apply the new value of i to the progressbar
                print "the progressbar should reflect", str(int(round(self.i*100))) +"%progress right now"
                print "the counter will increase"
                self.counter += 1
                print self.counter #debug purposes
                self.stop_progbar
                self.master.after(5000, self.interval)
            else:
                print "something is wrong - running.json is not available"
                self.master.after(5000, self.interval)

            guibuild.progbarlock = False

请注意,self.master.after()的调用需要在每个if语句之后发生 - 这样self.master.after_cancel()在调用时才会起作用。干杯!