5

我想根据Tkinter菜单命令创建一个大文本并通过进度条提供视觉支持。尽管进度条是在随后的耗时循环之前开始的,但进度条仅在创建并显示大文本后才会显示。

def menu_bar(self):
    self.create_menu.add_command(label="Create large file", 
    command=self.create_large_file)

def create_large_file(self):
    self.progressbar = ttk.Progressbar(self.master, mode='indeterminate')
    self.progressbar.pack()
    self.progressbar.start()
    self.text.delete(1.0, 'end')
    self.file_content = []

i = 0
while i < 2000000:
    line = lfd.input_string
    self.file_content.append(line + "\n")
    i += 1

self.file_content = ''.join(self.file_content)
self.text.insert(1.0, self.file_content) 
4

2 回答 2

7

我认为问题在于耗时的循环正在阻止tkinter事件循环mainloop()运行。换句话说,当您的工作密集型函数与 GUI 在同一线程中运行时,它会通过占用解释器来干扰它。

为了防止这种情况,您可以使用辅助线程来运行您的函数并在主线程中运行 GUI 及其进度条。为了让您了解如何执行此操作,这里有一个简单的示例,我从另一个(不相关的)进度条问题中的代码派生出来,以说明可以轻松地完成类似的事情。注意:通常建议不要让辅助线程直接访问主线程的tkinter对象。

from Tkinter import *
import ttk

import time
import threading

def foo():
    time.sleep(5) # simulate some work

def start_foo_thread(event):
    global foo_thread
    foo_thread = threading.Thread(target=foo)
    foo_thread.daemon = True
    progressbar.start()
    foo_thread.start()
    root.after(20, check_foo_thread)

def check_foo_thread():
    if foo_thread.is_alive():
        root.after(20, check_foo_thread)
    else:
        progressbar.stop()

root = Tk()
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
progressbar = ttk.Progressbar(mainframe, mode='indeterminate')
progressbar.grid(column=1, row=100, sticky=W)

ttk.Button(mainframe, text="Check",
           command=lambda:start_foo_thread(None)).grid(column=1, row=200,
                                                       sticky=E)

for child in mainframe.winfo_children():
    child.grid_configure(padx=5, pady=5)
root.bind('<Return>', start_foo_thread)

root.mainloop()
于 2013-05-07T05:28:36.760 回答
3

这是另一个相当简单的解决方案,不需要混合 Tkinter 和多线程。要使用它,需要能够update_idletasks()在耗时的功能期间多次调用进度条小部件的方法。

from Tkinter import *
import ttk

import time

def foo(progressbar):
    progressbar.start()
    for _ in range(50):
        time.sleep(.1) # simulate some work
        progressbar.step(10)
        progressbar.update_idletasks()
    progressbar.stop()

root = Tk()

mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
progressbar = ttk.Progressbar(mainframe, mode='indeterminate')
progressbar.grid(column=1, row=100, sticky=W)

ttk.Button(mainframe, text="Check",
           command=lambda:foo(progressbar)).grid(column=1, row=200, sticky=E)

for child in mainframe.winfo_children():
    child.grid_configure(padx=5, pady=5)
root.bind('<Return>', lambda event:foo(progressbar))

root.mainloop()
于 2013-05-11T14:20:19.043 回答