Tkinter+Bleak异步开发问题:无法实时回传设备RSSI至GUI
问题修复方案:Bleak实时回传RSSI到Tkinter GUI
核心问题分析
- 异步线程执行逻辑错误:你继承
threading.Thread后将run定义为async函数,但threading.Thread不支持异步协程调度,导致扫描回调无法实时触发,仅在扫描结束后一次性返回结果。 - RSSI值获取方式错误:
advertisement_data是AdvertisementData对象而非元组,用索引[5]取值是错误的,需直接访问对象属性。
修复后的线程代码
import threading import asyncio from bleak import BleakScanner, BLEDevice, AdvertisementData class MonThread(threading.Thread): def __init__(self, win): super().__init__() self.win = win self._stop_event = threading.Event() # 用于控制扫描启停 async def callback(self, device: BLEDevice, advertisement_data: AdvertisementData): # 过滤指定MAC地址的设备 if device.address == "AB:7E:48:E7:EE:1F": print('Name : ', advertisement_data.local_name) # 正确获取设备名称 rssi_value = advertisement_data.rssi # 正确获取RSSI数值 print('Signal : ', rssi_value) # 发送事件通知GUI更新 self.win.event_generate("<<UpdateRSSI>>", x=rssi_value) async def scan_loop(self): scanner = BleakScanner(self.callback) await scanner.start() # 持续扫描直到收到停止信号 while not self._stop_event.is_set(): await asyncio.sleep(0.1) await scanner.stop() def run(self): # 在子线程中启动独立的asyncio事件循环 loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(self.scan_loop()) def stop(self): # 触发停止信号,终止扫描循环 self._stop_event.set()
修复后的GUI代码
import tkinter as tk def lance_thread(): global scan_thread scan_thread = MonThread(root) scan_thread.start() def update_rssi(e): # 更新RSSI显示标签 message2.config(text=str(e.x)) root = tk.Tk() root.title("Analyzer") root.geometry('600x400+100+100') root.resizable(True, True) root.config(relief=tk.RAISED, bd=3) b = tk.Button(text="Scan", command=lance_thread) b.pack() frameH = tk.Frame(root, width=200, height=80, background="bisque", relief=tk.GROOVE, bd=6, padx=20, pady=20) frameH.pack(anchor="nw", padx=10, pady=10, fill="x") message = tk.Label(frameH, text="RSSI : ", height=2, padx=50, background="bisque") message.grid(row=0, column=1) message2 = tk.Label(frameH, text="---", height=2) message2.grid(row=0, column=9) root.bind("<<UpdateRSSI>>", update_rssi) # 窗口关闭时停止扫描线程,避免资源泄漏 def on_closing(): if 'scan_thread' in globals() and scan_thread.is_alive(): scan_thread.stop() scan_thread.join() root.destroy() root.protocol("WM_DELETE_WINDOW", on_closing) root.mainloop()
关键修复说明
- 异步协程正确调度:在子线程中创建独立的asyncio事件循环,确保扫描回调能实时触发并推送RSSI值。
- 属性化获取设备数据:通过
advertisement_data.rssi和advertisement_data.local_name直接访问设备参数,避免类型错误。 - 可控扫描循环:添加停止事件,支持主动终止扫描,避免程序退出时线程残留。
- 语义化事件命名:将事件名改为
<<UpdateRSSI>>,更贴合实时更新的业务逻辑。
内容的提问来源于stack exchange,提问作者François HEBERT
相关产品推荐
相关产品推荐

