如何让Tkinter程序等待上一行代码执行完成后再执行后续逻辑?
问题根因
Tkinter基于单线程事件循环运行,该问题由两个核心特性导致:
window.after()为异步非阻塞方法:调用后仅将待执行回调注册到事件队列,立刻返回执行后续代码,不会等待回调执行完成。原有for循环会在几毫秒内完成3次请求,连续注册3个1秒后恢复背景的回调,所有UI修改被挤压在事件队列中,视觉上仅生效一次。requests.get()为同步阻塞方法:执行时会卡住Tkinter事件循环,修改的标签文本、背景色无法及时渲染,直到请求完成才会更新,因此看不到中间状态变化。
修改方案
把同步循环逻辑改成异步递归调度,每次请求、状态展示完整结束后,再触发下一次请求,同时将请求逻辑放到子线程避免阻塞UI:
# import gpiozero # import picamera import json import requests import tkinter as tk import threading # 新增导入,用于异步执行请求 with open("config.json") as file: config = json.load(file) ENDPOINT = config["ENDPOINT"] USUARIO = config["USUARIO"] ESTACION = config["ESTACION"] TIEMPO_ESPERA = config["TIEMPO_ESPERA"] PIN_RELE = config["PIN_RELE"] PATH_SALIDA = ENDPOINT + "Salida.getTicket/" + ESTACION + "/" + USUARIO + "/" barcode = "" # RELAY = gpiozero.OutputDevice(PIN_RELE, active_high=True, initial_value=False) # CAMERA = picamera.PiCamera() def check_scan_barcode(event=None): info_label.config(text = "Wait...") global barcode # 声明全局变量存储扫码结果 barcode = barcode_entry.get() barcode_entry.delete(0, "end") # 启动异步请求,计数器从0开始 run_check(0) # 新增异步校验递归函数 def run_check(count): # 3次请求都完成就恢复初始状态 if count >= 3: info_label.config(text = "Scan barcode") normal() return # 请求放子线程执行,避免阻塞UI def do_request(): response = get_request(ENDPOINT + barcode) # UI修改必须放回主线程执行,用after调度 if response["data"] == "True": window.after(0, success) window.after(0, open_barrier, barcode) else: window.after(0, error) # 1秒后(背景展示完成)触发下一次校验 window.after(1000, run_check, count + 1) threading.Thread(target=do_request, daemon=True).start() def get_request(url): try: response = requests.get(url, timeout=5) # 加超时避免卡住 response.raise_for_status() return response.json() except: # 请求异常统一返回错误 return {"data": "False"} def normal(): window.configure(bg="white") info_label.configure(bg="white") def success(): window.configure(bg="green") info_label.configure(bg="green") def error(): window.configure(bg="red") info_label.configure(bg="red") def open_barrier(barcode): # CAMERA.capture(f"/home/pi/Pictures{barcode}.jpg") # RELAY.on() # window.after(TIEMPO_ESPERA, RELAY.off) pass window = tk.Tk() # window.attributes('-fullscreen', True) info_label = tk.Label(window, text= "Scan barcode.", font=("Arial", 40)) info_label.pack() barcode_entry = tk.Entry(window, width=50) barcode_entry.bind('<Return>', check_scan_barcode) barcode_entry.pack(expand=True) barcode_entry.focus() window.mainloop()
逻辑说明
- 用
run_check递归函数替代原来的for循环,每次请求完成、状态展示1秒后才会触发下一次请求,保证执行顺序严格按预期走 - 所有网络请求放在子线程执行,不会卡住UI渲染,修改UI的操作统一用
window.after(0, 回调)调度回主线程执行,符合Tkinter的线程安全要求 - 去掉了
success和error里的after调度,统一在递归逻辑里控制下一次执行的时机,避免多个回调冲突
内容的提问来源于stack exchange,提问作者Bowie
相关产品推荐
相关产品推荐

