EDIT: Possible solution below, can anyone confirm?

我在一个线程中运行tkinter,并尝试使用this answer中描述的 event_generate 技术来控制其他线程的行为 . 显然,一旦我启动了一个设置 tkinter.Tk 实例并启动其主循环的线程,就必须在尝试生成事件之前阻塞直到mainloop已启动 . 我试图这样做的方式如下(python 3.2代码):

import tkinter
import threading

mainloop_started = threading.Event()

def tkinter_thread_stuff():
    global root
    root = tkinter.Tk()

    #[code that binds events with root.bind]

    root.after(0, mainloop_started.set)
    root.mainloop()

th = threading.Thread(target=tkinter_thread_stuff)
th.start()

mainloop_started.wait()

#[code that sends events to root with event_generate]

换句话说,我使用 after 使tkinter mainloop设置 threading.Event ,名为 mainloop_started ,我的事件生成代码通过阻塞开始,直到设置此事件 . 然而,这似乎导致了竞争条件 - 有时在_2529788之后快速生成的事件永远不会被tkinter处理 . 如果我在 after 调用中加时间延迟(例如 root.after(1000, mainloop_started.set) ),则不会发生这种情况,所以看起来在tkinter调用 after 回调的点和它能够接收事件的点之间有一段时间 . 有没有人知道阻塞的正确方法,直到tkinter可以接收事件?

PS:在这样的线程中运行tkinter mainloop是否安全?我知道我不能直接干扰来自其他线程的root或其他tkinter东西,但除此之外,一切似乎都可以正常工作 . 我见过一些网站说tkinter只能在主线程中运行 .

EDIT: 我想我有一个解决方案 - 使用 after_idle 而不是 after . 这似乎确保在mainloop准备好处理事件之前不会调用回调,但任何人都可以确认这一点吗?无论如何,如果你不能保证在调用回调时完全设置tkinter(除非 after 在另一个事件处理程序中被调用,我猜), after 的重点是什么?

在这种情况下使用 afterafter_idle 的效果的具体示例,如果有人想要玩它:

import tkinter
import threading

#create a threading.Event for blocking until the mainloop is ready
mainloop_started = threading.Event()

#counter stores the number of times that tkinter receives the event
#<<increment>>, which is generated 1000 times
counter = 0

#delay in milliseconds before mainloop sets mainloop_started:
#if I set this to 0, the final value of counter varies between 0 and 1000, i.e.
#some of the <<increment>> events may not be processed as expected
#if I set it to 1 or a larger number, the final value of counter is always 1000,
#so all events are processed correctly, and I can get the
#same behaviour by replacing after with after_idle below
delay_ms = 0

def send_events():
    global root

    #wait until mainloop_started has been set
    mainloop_started.wait()

    #send 1000 <<increment>> events
    for i in range(1000):
        root.event_generate('<<increment>>', when='tail')

    #send a <<show>> event
    root.event_generate('<<show>>', when='tail')

#run send_events in a thread
th = threading.Thread(target=send_events)
th.start()

#start tkinter
root = tkinter.Tk()

#when root receives the event <<increment>>, increment counter
def increment(event):
    global counter
    counter += 1
root.bind('<<increment>>', increment)

#when root receives the event <<show>>, print the value of the counter
def show(event):
    print(counter)
root.bind('<<show>>', show)

#instruct mainloop to set mainloop_started
root.after(delay_ms, mainloop_started.set)

#using after_idle instead causes all events to be processed correctly
#root.after_idle(mainloop_started.set)

#finally, start the mainloop
root.mainloop()