我有一个简单的 Tkinter gui,上面有大约 20 个按钮。当我单击一个按钮时,脚本会运行大约 5 分钟。在此期间,我必须等到脚本停止运行才能单击其他按钮。有没有办法设置窗口,以便我可以在第一个单击的脚本运行时单击其他按钮?
from Tkinter import *
import Tkinter as tk
import time
def function1():
time.sleep(60)
print 'function1'
def function2():
time.sleep(60)
print 'function2'
root = Tk()
w = 450 # width for the Tk root
h = 500# height for the Tk root
frame = Frame(root, width=w,height =h)
button1=Button(frame, text = 'function 1',fg='black',command=function1).grid(row=1,column=1)
button2=Button(frame, text = 'function 2',fg='black',command=function2).grid(row=1,column=2)
frame.pack()
root.mainloop()
Run Code Online (Sandbox Code Playgroud)
我希望能够在仍在运行function2后点击function1
如果触发需要 1 分钟运行的回调,则 1 分钟内不会返回主循环,因此 GUI 无法响应任何内容。
对此有两种常见的解决方案。
第一种是使用后台线程:
def function1():
time.sleep(60)
print 'function1'
def function1_background():
t = threading.Thread(target=function1)
t.start()
button1 = Button(frame, text='function 1', fg='black', command=function1_background)
Run Code Online (Sandbox Code Playgroud)
这很简单,但它仅在您的代码纯粹执行后台工作时才有效,不涉及任何 tkinter 小部件。
这里唯一的问题是你必须有def20 个额外的功能。你不想重复那么多——那是 80 行重复的样板代码,这些代码妨碍了查看重要代码的方式,还有 20 次机会在复制粘贴中犯下一个很难追踪的愚蠢错误,并且如果您以后决定使用进程而不是线程,那么您必须更改 20 个地方,以便工作可以更好地并行化,或者 4 个线程池与排队的后台任务。
您可以通过几种不同的方式解决该问题。请参阅此问题以获得更深入的解释,但简而言之,您可以让 Python 为您完成一些重复性工作。
您可以def使用单个辅助函数:
def background(func):
t = threading.Thread(target=func)
t.start()
Run Code Online (Sandbox Code Playgroud)
...然后是lambda20 个单独的函数:
button1 = Button(frame, text='function 1', fg='black', command=lambda: background(function1))
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用partial以下方法部分应用该功能:
button1 = Button(frame, text='function 1', fg='black', command=functools.partial(background, function1))
Run Code Online (Sandbox Code Playgroud)
或者,如果您不想在后台之外调用函数,您可以编写一个装饰器并将其应用于每个函数def:
def background(func):
@functools.wraps(func)
def wrapper():
t = threading.Thread(target=func)
t.start()
return wrapper
@background
def function1():
time.sleep(60)
print 'function1'
Run Code Online (Sandbox Code Playgroud)
如果你不能使用线程(例如,因为后台工作涉及摆弄你的 tkinter 小部件),另一种方法是重构你的代码,这样,而不是一个需要 1 分钟的单一任务,它是一堆单独的任务,每个都需要几分之一秒并安排下一部分:
def function1(count=60):
if count > 0:
time.sleep(0.1)
frame.after(0, function1, count-0.1)
else:
print 'function1'
button1 = Button(frame, text='function 1', fg='black', command=function1)
Run Code Online (Sandbox Code Playgroud)
这总是有效的,如果你能找到一种方法来做到这一点。您的实际工作可能不像 a 那样容易分成 0.1 秒的块sleep(60)。