1 背景
在使用tkinter进行GUI编程时,有时会碰到界面未响应的情况,如下:
2 解决办法
原因很有可能是执行的某个动作阻塞了线程,可通过使用threading.Thread解决(本例中是点击了Go菜单下的开始选项导致的界面卡死)
2.1 解决方法1 - 使用线程封装该动作
修改前代码:
class MenuGo(EMenu):
LABEL_GO = "Go"
LABEL_START = "开始"
LABEL_END = "终止"
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master=master, cnf=cnf, **kw)
self.go = None
master.add_cascade(label=self.LABEL_GO, menu=self)
self.add_command(label=self.LABEL_START, command=self.start)
self.add_command(label=self.LABEL_END, command=self.end)
def start(self):
self.go = True
while self.go:
self.stdout("biu biu biu!", with_time=" - ")
time.sleep(1)
def end(self):
self.go = False
self.stdout("zi zi zi!", with_time=" - ")
修改后代码:
class MenuGo(EMenu):
LABEL_GO = "Go"
LABEL_START = "开始"
LABEL_END = "终止"
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master=master, cnf=cnf, **kw)
self.go = None
master.add_cascade(label=self.LABEL_GO, menu=self)
self.add_command(label=self.LABEL_START, command=self.start)
self.add_command(label=self.LABEL_END, command=self.end)
def start(self):
def run():
self.go = True
while self.go:
self.stdout("biu biu biu!", with_time=" - ")
time.sleep(1)
t = Thread(target=run)
t.start()
def end(self):
self.go = False
self.stdout("zi zi zi!", with_time=" - ")
2.2 解决方法2 - 使用装饰器类
from functools import wraps
from threading import Thread
class ThreadRun(object):
def __init__(self):
pass
def __call__(self, f):
@wraps(f)
def wrapped_f(*args, **kw):
t = Thread(
target=f,
args=args,
kwargs=kw
)
t.start()
return wrapped_f
class MenuGo(EMenu):
LABEL_GO = "Go"
LABEL_START = "开始"
LABEL_END = "终止"
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master=master, cnf=cnf, **kw)
self.go = None
master.add_cascade(label=self.LABEL_GO, menu=self)
self.add_command(label=self.LABEL_START, command=self.start)
self.add_command(label=self.LABEL_END, command=self.end)
@ThreadRun()
def start(self):
self.go = True
while self.go:
self.stdout("biu biu biu!", with_time=" - ")
time.sleep(1)
def end(self):
self.go = False
self.stdout("zi zi zi!", with_time=" - ")
2.3 解决方法3 - 继承类,添加装饰器函数
from tkinter import Menu
from tkinter import DISABLED, NORMAL
from threading import Thread
from functools import wraps
class EMenu(Menu):
def __init__(self, *args, **kw):
self.stdout = kw["stdout"] if "stdout" in kw else print
self.stderr = kw["stderr"] if "stderr" in kw else print
for key in ["stdout", "stderr"]:
if key in kw:
del kw[key]
super().__init__(*args, **kw)
def thread_run(label_name):
def run_decorator(f):
@wraps(f)
def wrapped_f(self, *args, **kw):
def call():
self.entryconfig(label_name, state=DISABLED) # 设置菜单选项禁用
f(self, *args, **kw)
self.entryconfig(label_name, state=NORMAL) # 设置菜单选项启用
t = Thread(target=call)
t.start()
return wrapped_f
return run_decorator
class MenuGo(EMenu):
LABEL_GO = "Go"
LABEL_START = "开始"
LABEL_END = "终止"
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master=master, cnf=cnf, **kw)
self.go = None
master.add_cascade(label=self.LABEL_GO, menu=self)
self.add_command(label=self.LABEL_START, command=self.start)
self.add_command(label=self.LABEL_END, command=self.end)
@EMenu.thread_run(LABEL_START)
def start(self):
self.go = True
while self.go:
self.stdout("biu biu biu!", with_time=" - ")
time.sleep(1)
def end(self):
self.go = False
self.stdout("zi zi zi!", with_time=" - ")
3 源码在github上
https://github.com/TheUncleWhoGrowsBeans/tkGo