Tkinter加载图标时,tk.mainloop在线程中锁定Python
这是测试案例...
import Tkinter as tk
import thread
from time import sleep
if __name__ == '__main__':
t = tk.Tk()
thread.start_new_thread(t.mainloop, ())
# t.iconbitmap('icon.ico')
b = tk.Button(text='test', command=exit)
b.grid(row=0)
while 1:
sleep(1)
这段代码可以正常运行。如果你取消注释 t.iconbitmap 这一行,程序就会卡住。你可以随意调整代码的顺序,结果都是一样的,程序会卡住。
我该如何避免在有图标的情况下,tk.mainloop 锁住 全局解释器锁 呢?
目标是 win32 和 Python 2.6.2。
1 个回答
23
我认为你不应该在不同的线程上执行主循环。根据我所知道的,主循环应该在创建这个小部件的同一个线程上运行。
我熟悉的图形用户界面工具包(比如Tkinter和.NET Windows Forms)都是这样:你只能在一个线程中操作图形界面。
在Linux上,你的代码会引发一个异常:
self.tk.mainloop(n) RuntimeError: Calling Tcl from different appartment
以下其中一种方式可以工作(不需要额外的线程):
if __name__ == '__main__':
t = tk.Tk()
t.iconbitmap('icon.ico')
b = tk.Button(text='test', command=exit)
b.grid(row=0)
t.mainloop()
如果使用额外的线程:
def threadmain():
t = tk.Tk()
t.iconbitmap('icon.ico')
b = tk.Button(text='test', command=exit)
b.grid(row=0)
t.mainloop()
if __name__ == '__main__':
thread.start_new_thread(threadmain, ())
while 1:
sleep(1)
如果你需要在Tkinter线程外与Tkinter进行通信,我建议你设置一个定时器,定期检查一个队列来处理任务。
这里有一个例子:
import Tkinter as tk
import thread
from time import sleep
import Queue
request_queue = Queue.Queue()
result_queue = Queue.Queue()
def submit_to_tkinter(callable, *args, **kwargs):
request_queue.put((callable, args, kwargs))
return result_queue.get()
t = None
def threadmain():
global t
def timertick():
try:
callable, args, kwargs = request_queue.get_nowait()
except Queue.Empty:
pass
else:
print "something in queue"
retval = callable(*args, **kwargs)
result_queue.put(retval)
t.after(500, timertick)
t = tk.Tk()
t.configure(width=640, height=480)
b = tk.Button(text='test', name='button', command=exit)
b.place(x=0, y=0)
timertick()
t.mainloop()
def foo():
t.title("Hello world")
def bar(button_text):
t.children["button"].configure(text=button_text)
def get_button_text():
return t.children["button"]["text"]
if __name__ == '__main__':
thread.start_new_thread(threadmain, ())
trigger = 0
while 1:
trigger += 1
if trigger == 3:
submit_to_tkinter(foo)
if trigger == 5:
submit_to_tkinter(bar, "changed")
if trigger == 7:
print submit_to_tkinter(get_button_text)
sleep(1)