当加载图标并且tk.mainloop在线程中时,Tkinter会锁定Python

bur*_*ito 7 python winapi tkinter green-threads

这是测试案例......

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)
Run Code Online (Sandbox Code Playgroud)

这段代码有效.取消注释t.iconbitmap行并锁定.以你喜欢的方式重新安排它; 它会锁定.

当存在图标时,如何防止tk.mainloop锁定GIL

目标是win32和Python 2.6.2.

cod*_*ape 23

我相信你不应该在不同的线程上执行主循环.AFAIK,主循环应该在创建小部件的同一个线程上执行.

我熟悉的GUI工具包(Tkinter,.NET Windows Forms)就是这样的:您只能从一个线程操作GUI.

在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()
Run Code Online (Sandbox Code Playgroud)

有额外的线程:

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)
Run Code Online (Sandbox Code Playgroud)

如果你需要从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)
Run Code Online (Sandbox Code Playgroud)

  • 嗯,你已经击中头部,它有效......但我没有提供足够的信息.我的理由是我希望能够为tkinter做一些事情,其中​​while循环是...对SO来说有点新,我应该接受你的答案并提出另一个更冗长的问题吗? (2认同)
  • 嗨,我已经用一个建议和代码示例更新了我的答案,以实现这一点.while循环现在使用请求/响应队列在tkinter线程上调用一些方法. (2认同)
  • 顺便说一句,对于生产代码,我建议你在一个类中封装Tkinter窗口,线程和队列.这是为了避免我们现在拥有的全局变量:request_queue,response_queue和t.你还需要围绕callable(*args,**kwargs)进行一些错误处理. (2认同)
  • 我会很高兴看到一个简单的"你应该使用队列",你已经远远超出了职责,谢谢你. (2认同)