树莓派下Pytransitions状态机切换时如何同步更新Tkinter全屏文本
问题根源
你遇到的问题核心是Tkinter的事件循环机制和状态切换的触发时机不匹配,具体有两点:
- 你在
win.mainloop()启动之前就一次性执行了所有状态切换动作:SBS.System_Ready()、SBS.Picture_Taken()、SBS.User_Verified()是同步顺序执行的,三个状态的on_enter回调瞬间跑完,最后回到A状态,文本改了三次但此时Tkinter界面还没开始渲染,就算渲染了也会被后面调度的10秒倒计时、rdy函数的文本覆盖,自然看不到状态对应的内容。 - 状态切换没有和实际的业务触发逻辑绑定,你现在是直接手动连调三个切换,没有等待对应的触发条件(比如传感器校准完成、拍照完成、验证完成这些事件)。
另外你代码里还存在intvar.set = xxx的用法错误,set是StringVar的方法,正确写法是intvar.set(xxx)。
修复方案
把所有状态切换的触发放到Tkinter的事件回调中,和实际业务节点绑定即可,修改后的完整代码如下:
from transitions import Machine from tkinter import * import time win = Tk() intvar = StringVar() cntdwn = 10 class Identification(object): def on_enter_A(self): print("We've just entered State A") update_window('System Started') def on_enter_B(self): print("We've just entered State B") update_window('Please look into the camera') # 此处模拟3秒后拍照完成,实际使用可以替换为摄像头捕获成功的回调 win.after(3000, SBS.Picture_Taken) def on_enter_C(self): print("We've just entered State C") update_window('Face and readings authorized') # 此处模拟3秒后验证完成,实际使用可以替换为人脸验证成功的回调 win.after(3000, SBS.User_Verified) def update_window(msg): intvar.set(msg) def rdy(): label.pack(padx=10, pady=10) # 传感器校准完成后触发第一次状态切换,从A进入B SBS.System_Ready() def cntdwn_update(secs): if secs: intvar.set(f"Greetings,booting system & calibrating sensors {secs}") win.after(1000, cntdwn_update, secs-1) else: rdy() SBS = Identification() states = ['A', 'B', 'C'] transitions = [ { 'trigger': 'System_Ready', 'source': 'A', 'dest': 'B' }, { 'trigger': 'Picture_Taken', 'source': 'B', 'dest': 'C' }, { 'trigger': 'User_Verified', 'source': 'C', 'dest': 'A' } ] machine = Machine(SBS, states=states, transitions = transitions, initial='A') win.geometry("650x250") win.attributes('-fullscreen', True) intvar.set(f"Greetings, calibrating sensors {cntdwn}") # label绑定textvariable,后续只需要改intvar就能自动更新文本 label= Label(win, textvariable=intvar, font=('Times New Roman bold',50)) label.pack(padx=10, pady=10) win.after(1000, cntdwn_update, cntdwn-1) win.mainloop()
主要修改点
- 删掉了
mainloop之前手动触发的三个状态切换语句,所有状态切换都绑定到对应的业务事件回调中 - 校准完成的
rdy函数里触发第一次状态切换进入B状态 - 每个状态的
on_enter回调里模拟了后续触发逻辑,你可以根据实际的硬件回调替换对应逻辑即可
内容的提问来源于stack exchange,提问作者Suslik
相关产品推荐
相关产品推荐

