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

发布于 2024-07-29 07:18:19 字数 541 浏览 7 评论 0原文

这是测试用例...

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 锁定 GIL

目标是win32和Python 2.6.2。

Here's the test case...

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)

This code works. Uncomment the t.iconbitmap line and it locks. Re-arrange it any way you like; it will lock.

How do I prevent tk.mainloop locking the GIL when there is an icon present?

The target is win32 and Python 2.6.2.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

邮友 2024-08-05 07:18:19

我相信你不应该在不同的线程上执行主循环。 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()

使用额外线程:

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)

I believe you should not execute the main loop on a different thread. AFAIK, the main loop should be executed on the same thread that created the widget.

The GUI toolkits that I am familiar with (Tkinter, .NET Windows Forms) are that way: You can manipulate the GUI from one thread only.

On Linux, your code raises an exception:

self.tk.mainloop(n)
RuntimeError: Calling Tcl from different appartment

One of the following will work (no extra threads):

if __name__ == '__main__':
    t = tk.Tk()
    t.iconbitmap('icon.ico')

    b = tk.Button(text='test', command=exit)
    b.grid(row=0)

    t.mainloop()

With extra thread:

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)

If you need to do communicate with tkinter from outside the tkinter thread, I suggest you set up a timer that checks a queue for work.

Here is an example:

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文