Python 如何阻止直到tkinter事件可以处理

Python 如何阻止直到tkinter事件可以处理,python,multithreading,tkinter,Python,Multithreading,Tkinter,编辑:下面可能的解决方案,有人能确认吗? 我在一个线程中运行tkinter,并试图使用中描述的event\u generate技术来控制其他线程的行为。显然,一旦我启动了一个线程,该线程设置了一个tkinter.Tk实例并启动了它的mainloop,那么在尝试生成事件之前,有必要阻止它,直到mainloop启动。我尝试这样做的方式如下(python 3.2代码): 换句话说,我正在使用after使tkinter mainloop设置threading.Event调用mainloop\u star

编辑:下面可能的解决方案,有人能确认吗?

我在一个线程中运行tkinter,并试图使用中描述的
event\u generate
技术来控制其他线程的行为。显然,一旦我启动了一个线程,该线程设置了一个
tkinter.Tk
实例并启动了它的mainloop,那么在尝试生成事件之前,有必要阻止它,直到mainloop启动。我尝试这样做的方式如下(python 3.2代码):

换句话说,我正在使用
after
使tkinter mainloop设置
threading.Event
调用
mainloop\u start
,并且我的事件生成代码通过阻塞开始,直到设置此事件为止。然而,这似乎会导致竞争条件-有时在
mainloop\u启动后快速生成的事件。wait()
从不由tkinter处理。如果我在
after
调用(例如
root.after(1000,mainloop\u start.set)
)中设置时间延迟,则不会发生这种情况,因此tkinter调用
after
回调后的
与它能够接收事件的点之间似乎有一段时间。在tkinter能够接收事件之前,是否有人知道正确的阻塞方式

PS:在这样的线程中运行tkinter mainloop安全吗?我知道我不能直接干扰其他线程中的root或其他tkinter内容,但除此之外,一切似乎都正常。我看到一些网站说tkinter只能在主线程中运行

编辑:我想我有一个解决方案-在空闲后使用
而不是
之后使用
。这似乎可以确保在mainloop准备好处理事件之前不会调用回调,但是有人能确认这一点吗?无论如何,如果您不能保证在调用回调时tkinter将被完全设置(我想,除非在另一个事件处理程序中调用
after
),那么
after
有什么意义

在这种情况下,如果有人想玩弄它,使用
after
after\u 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()
导入tkinter
导入线程
#创建一个threading.Event以进行阻塞,直到mainloop就绪
mainloop_start=threading.Event()
#计数器存储tkinter接收事件的次数
#,生成1000次
计数器=0
#mainloop设置mainloop\u启动前的延迟(毫秒):
#如果我将其设置为0,计数器的最终值在0和1000之间变化,即。
#某些事件可能未按预期处理
#如果我将其设置为1或更大的数字,计数器的最终值总是1000,
#所以所有事件都被正确处理,我可以得到
#用下面的after_idle(后怠速)进行更换,可获得相同的性能
延迟时间=0
def发送_事件():
全局根
#等待设置mainloop_启动
mainloop_已启动。等待()
#发送1000个事件
对于范围(1000)内的i:
root.event_generate(“”,when='tail')
#发送事件
root.event_generate(“”,when='tail')
#在线程中运行send_事件
th=线程。线程(目标=发送事件)
th.start()
#启动tkinter
root=tkinter.Tk()
#当root接收到事件时,递增计数器
def增量(事件):
全局计数器
计数器+=1
root.bind(“”,增量)
#当root收到事件时,打印计数器的值
def显示(事件):
打印(计数器)
root.bind(“”,show)
#指示mainloop设置mainloop\u已启动
root.after(delay_ms,mainloop_start.set)
#改用after_idle可正确处理所有事件
#root.after\u idle(mainloop\u start.set)
#最后,启动主循环
root.mainloop()

除了主线程,我不敢在其他地方运行tkinter相关的任何东西,我尝试过,但它在我的一台计算机上崩溃了。然后我决定在次线程中生成事件_是安全的,但当我需要创建顶级时,即使这样也给我带来了麻烦。“event_generate”inside“after_idle”似乎有效,但我不确定,所以我改变了计划(用轮询代替事件)@Aivar你的评论太主观了。是否可以从线程调用
.event\u generate
?这是在散布恐惧。除了主线程,我不敢在其他地方运行任何与tkinter相关的东西,我尝试过,但它在我的一台计算机上崩溃了。然后我决定在次线程中生成事件_是安全的,但当我需要创建顶级时,即使这样也给我带来了麻烦。“event_generate”inside“after_idle”似乎有效,但我不确定,所以我改变了计划(用轮询代替事件)@Aivar你的评论太主观了。是否可以从线程调用
.event\u generate
?这是散布恐惧。
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()