【问题标题】:Tkinter progress bar/window not responding when movedTkinter 进度条/窗口在移动时没有响应
【发布时间】:2011-02-01 21:12:48
【问题描述】:

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

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()

知道这里发生了什么以及为什么它变得无响应吗?它与正在使用的全局变量有什么关系吗?它在不移动时似乎“正常”,但绝对不一样。

【问题讨论】:

    标签: python progress-bar tkinter move


    【解决方案1】:

    some_fctother_fct 都睡眠 10 秒,因此应用程序在启动时将无响应至少 20 秒,因为这些睡眠都发生在调用 mainloop 之前。您是说即使在这 20 秒之后应用程序也没有响应,还是您要问为什么它在前 20 秒内没有响应?

    Tkinter 是单线程的,这意味着无论何时您进入睡眠状态,它都无法在睡眠期间为事件提供服务。正是这种对事件的服务定义了“响应性”。

    【讨论】:

    • 无响应来自仪表窗口。当程序运行时,您会看到仪表进度 10%、20%、30%... 等等。但是只要您单击进度窗口并尝试移动它,或单击不同的窗口(这两者同时程序正在运行并计数)进度条窗口变为空白且无响应。有时它会在 Windows XP 中的标题旁边显示“未响应”。我在那里睡觉,所以你可以看到它做一些事情。
    • 它是“无响应的”,因为它是“响应”的 mainloop。因此,在mainloop 运行之前,它几乎不会响应。你看到任何东西的原因是你的代码偶尔会调用update_idletasks,这会强制屏幕更新。在进入事件循环之前,不会发生任何事件处理(即窗口如何响应鼠标点击、移动、获得焦点等)。
    • 所以,如果我理解正确的话,为了正确看到进度条,程序应该在mainloop?如果是这样,一个程序是否有可能在mainloop 中以及完成其他任务(例如some_fctother fct)。我真的不明白原始的演示代码是如何解决这个问题的。 root.mainloop() 是否在 m.after() 命令之后和进入递归 _demo 代码之前立即运行?
    • 是的,为了正确查看进度条,mainloop 应该正在运行。原始演示代码之所以有效,是因为它将某些内容放入事件队列(使用after),然后立即启动事件循环。事件循环看到队列中的某些内容,将其拉出并运行该代码。在运行该代码的过程中,一个新作业(“递归”调用)被添加到事件队列中。起泡,冲洗,重复。它实际上不是递归的,它只是请求偶数循环在一段时间后再次调用它。
    • 在将主程序中的some_fct()other_fct() 调用修改为m.after(1000, lambda: do_stuff(m)) 其中do_stuff 调用some_fct()other_fct() 之后,我仍然遇到同样的问题。这不应该解决这个问题吗?
    【解决方案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()
    

    【讨论】:

      猜你喜欢
      • 2021-03-19
      • 2013-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-07
      • 2016-08-17
      相关资源
      最近更新 更多