tkinter:在脚本运行时单击其他按钮的能力

时间:2018-09-09 01:39:10

标签: python tkinter

我有一个简单的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()

function2仍在运行后,我希望能够单击function1

1 个答案:

答案 0 :(得分:3)

如果您触发了需要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)

这很简单,但是仅当您的代码纯粹是在做后台工作,而不接触任何tkinter小部件时,它才起作用。


这里唯一的问题是您必须def额外使用20个功能。您并不想重复太多,因为有80行重复的样板代码妨碍了您查看重要的代码,并且有20次机会在复制粘贴中犯下愚蠢的错误,这很难追踪,并且如果您以后决定要使用进程而不是线程,则必须更改20个位置,以便更好地并行化工作,或者将4个线程池与后台任务排队在一起。

您可以通过几种不同的方式解决该问题。有关更深入的说明,请参见this question,但总之,您可以让Python为您完成一些重复的工作。


您可以def使用单个助手功能:

def background(func):
    t = threading.Thread(target=func)
    t.start()

…然后是lambda 20个独立功能:

button1 = Button(frame, text='function 1', fg='black', command=lambda: background(function1))

或者,您可以使用partial部分应用该功能:

button1 = Button(frame, text='function 1', fg='black', command=functools.partial(background, function1))

或者,如果您不想在后台调用这些函数,则可以编写装饰器,并在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'

如果您不能使用线程(例如,由于后台工作涉及到您的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)

如果您可以找到一种解决方法,则此方法始终有效。您的实际工作可能不像sleep(60)那样容易地划分为0.1秒的块。