更新Tkinter列表框的更好方法

1 投票
2 回答
3486 浏览
提问于 2025-04-16 17:20

嗨,

首先,我写了一个程序,可以下载音乐,并在一个列表框中显示下载的百分比。

大概是这样的:

    from Tkinter import *
from urllib2 import *
admin = Tk()
Admin = Tk()

listbox = Listbox(admin, bg="PURPLE")
listbox.pack()

def fores():
    chunks = 10000
    dat = ''
    song = '3 rounds and a sound'
    url = 'http://bonton.sweetdarkness.net/music/Blind%20Pilot%20--%203%20Rounds%20and%20A%20Sound.mp3'
    down = urlopen(url)
    downso = 0
    tota = down.info().getheader('Content-Length').strip()
    tota = int(tota)

    while 1:
        a = down.read(chunks)
        downso += len(a)

        if not a:
            break
        dat += a
        percent = float(downso) / tota
        percent = round(percent*100, 1)



        listbox.insert(END, percent)
        listbox.update()
        listbox.delete(0, END)
        listbox.insert(END, percent)
        listbox.update()






button = Button(Admin, text='Download', command=fores)
button.pack()
button = Button(Admin, text='Download', command=fores)
button.pack()
mainloop()

我不打算展示原始程序,因为它的大小超过了帖子限制。

在我原来的程序中,如果我在下载一个mp3文件之前移动窗口,它的下载进度会少于3%,然后就停止了;而如果我关闭窗口,它又会重新开始下载。

有没有人知道这是为什么,或者有没有其他方法可以在Tkinter窗口上显示下载百分比?请帮帮我。

而且,update_idletasks并没有起作用。

2 个回答

-1

我使用了一个叫做ttk.Progressbar的进度条,你只需要把一个变量和它关联起来,然后更新这个变量就可以了。

http://docs.python.org/library/ttk.html#progressbar

http://www.tkdocs.com/tutorial/morewidgets.html#progressbar

2

显示字符串的合适组件是Label。你可以用configure方法在运行时更改文本:

self.progress = Label(...)
...
self.progress.configure(text="%s%% completed" % percent)

其次,你创建了两个根窗口——adminAdmin。而且奇怪的是,你把列表框放在一个窗口里,把按钮放在另一个窗口里。Tk并不是这样设计的。第三,你需要调用你那个(唯一的)根窗口的主循环方法(比如:Admin.mainloop)。

最后,关于你提到的update_idletasks不工作的情况——请你定义一下“没有工作”是什么意思。实际上,它会更新显示。它不会让你在程序运行时与窗口互动。

我根据以上评论对你的代码做了一些修改(只创建了一个根窗口,使用了Label而不是Listbox,并使用了update_idletasks,程序顺利完成,下载了歌曲)。

调用update的危险在于:如果你在下载的时候点击了“下载”按钮,会发生什么?下次调用update时,这个按钮的点击事件会被处理。在处理这个事件时,你会进入一个无限循环。在这个内部无限循环运行的时候,外部的循环就无法运行。这样一来,你就有效地冻结了第一次下载。

正确的解决方案涉及至少两种技术之一。第一,创建一个线程来进行下载,并定期将信息发送回主循环,以便更新进度条。第二,利用已经存在的无限循环——事件循环——通过使用after将任务放到事件队列中,一次读取一小块数据。

网上有这两种方法的示例。

撰写回答