Tkinter 进度条/窗口移动时无响应

0 投票
2 回答
2251 浏览
提问于 2025-04-16 11:02

我从这里借用了一个进度条,想把它调整一下,让它在我的程序中使用全局变量。以下是参考代码:

import Tkinter

class Meter(Tkinter.Frame):
    def __init__(self, master, width=300, height=20, bg='white', fillcolor='orchid1',\
                 value=0.0, text=None, font=None, textcolor='black', *args, **kw):
        Tkinter.Frame.__init__(self, master, bg=bg, width=width, height=height, *args, **kw)
        self._value = value

        self._canv = Tkinter.Canvas(self, bg=self['bg'], width=self['width'], height=self['height'],\
                                    highlightthickness=0, relief='flat', bd=0)
        self._canv.pack(fill='both', expand=1)
        self._rect = self._canv.create_rectangle(0, 0, 0, self._canv.winfo_reqheight(), fill=fillcolor,\
                                                 width=0)
        self._text = self._canv.create_text(self._canv.winfo_reqwidth()/2, self._canv.winfo_reqheight()/2,\
                                            text='', fill=textcolor)
        if font:
            self._canv.itemconfigure(self._text, font=font)

        self.set(value, text)
        self.bind('<Configure>', self._update_coords)

    def _update_coords(self, event):
        '''Updates the position of the text and rectangle inside the canvas when the size of
        the widget gets changed.'''
        # looks like we have to call update_idletasks() twice to make sure
        # to get the results we expect
        self._canv.update_idletasks()
        self._canv.coords(self._text, self._canv.winfo_width()/2, self._canv.winfo_height()/2)
        self._canv.coords(self._rect, 0, 0, self._canv.winfo_width()*self._value, self._canv.winfo_height())
        self._canv.update_idletasks()

    def get(self):
        return self._value, self._canv.itemcget(self._text, 'text')

    def set(self, value=0.0, text=None):
        #make the value failsafe:
        if value < 0.0:
            value = 0.0
        elif value > 1.0:
            value = 1.0
        self._value = value
        if text == None:
            #if no text is specified use the default percentage string:
            text = str(int(round(100 * value))) + ' %'
        self._canv.coords(self._rect, 0, 0, self._canv.winfo_width()*value, self._canv.winfo_height())
        self._canv.itemconfigure(self._text, text=text)
        self._canv.update_idletasks()

##-------------demo code--------------------------------------------##

def _demo(meter, value):
    meter.set(value)
    if value < 1.0:
        value = value + 0.005
        meter.after(50, lambda: _demo(meter, value))
    else:
        meter.set(value, 'Demo successfully finished')

if __name__ == '__main__':
    root = Tkinter.Tk(className='meter demo')
    m = Meter(root, relief='ridge', bd=3)
    m.pack(fill='x')
    m.set(0.0, 'Starting demo...')
    m.after(1000, lambda: _demo(m, 0.0))
    root.mainloop()

这段代码和演示效果很好,但当我做了一些修改,想测试一下如何把它整合到我的代码中时,进度窗口变得无响应,而且每当我移动它或者激活其他窗口时,它就会变成空白。

##-------------demo code--------------------------------------------##

def some_fct(m):
    global count
    i = 0
    while i < 5:
        count = count + 1
        sleep(2)
        m.set(float(count) / total)
        i = i + 1

def other_fct(m):
    global count
    i = 0
    while i < 5:
        count = count + 1
        sleep(2)
        m.set(float(count) / total)
        i = i + 1

if __name__ == '__main__':
    global count
    global total
    count = 0
    total = 10
    root = Tkinter.Tk(className='meter demo')
    m = Meter(root, relief='ridge', bd=3)
    m.pack(fill='x')
    m.set(0.0, 'Starting meter')
    some_fct(m)
    other_fct(m)
    root.mainloop()

你知道这是怎么回事吗?为什么它会变得无响应?这和使用全局变量有关吗?当它不被移动时,似乎“还行”,但肯定不是同样的效果。

2 个回答

2

some_fctother_fct 都会各自暂停10秒,所以当应用程序启动时,至少会有20秒的时间是没有反应的,因为这些暂停都是在调用 mainloop 之前发生的。你是在问即使过了这20秒后,应用程序仍然没有反应,还是在问为什么前20秒没有反应呢?

Tkinter是单线程的,这意味着每当你让程序暂停时,它就无法处理其他事件,直到暂停结束。处理这些事件的能力就是我们说的“响应能力”。

2

这是我用来实现我想要的功能的代码:

##-------------demo code--------------------------------------------##

def count_numbers(number):
    i = 0
    while i < number:
        i = i + 1

def some_fct():
    global count
    i = 0
    while i < 5:
        count = count + 1
        count_numbers(5000000)
        i = i + 1

def other_fct():
    global count
    i = 0
    while i < 5:
        count = count + 1
        count_numbers(5000000)
        i = i + 1

def do_stuff():
    some_fct()
    other_fct()

def update_progress(m):
    value = float(count) / total
    if value < 1.0:
        m.set(value)
        m.after(500, lambda: update_progress(m))
    else:
        m.set(value, 'Process Completed')

if __name__ == '__main__':
    global count
    global total
    count = 0
    total = 10
    root = Tkinter.Tk(className='meter demo')
    m = Meter(root, relief='ridge', bd=3)
    m.pack(fill='x')
    m.set(0.0, 'Starting meter')    
    m.after(50, lambda: update_progress(m))
    thread.start_new_thread(do_stuff, () )
    root.mainloop()

撰写回答