基于tkinter的力传感器采集绘图程序启动后暂停按钮失效求助
故障根因
- Tkinter采用单线程事件驱动模型,你点击Start按钮后触发的
data_gsv函数运行在GUI主线程,其中的while死循环会长期占用主线程资源,导致Tkinter没有空闲时间处理Pause按钮点击、界面刷新等事件,表现就是按钮无法点击、界面卡死 - 直接调用matplotlib.pyplot的独立绘图接口,其内部自带的事件循环和Tkinter的事件循环存在抢占冲突,进一步加剧了界面无响应的问题
修复后完整代码
""" Created on Tue Nov 16 17:38:33 2021 @author: admin """ import serial import struct import platform import threading import time import numpy as np import csv import tkinter from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import matplotlib.pyplot as plt class MeasurementConverter: def convertValue(self, bytes): pass class ForceMeasurementConverterKG(MeasurementConverter): def __init__(self, F_n, S_n, u_e): self.F_n = F_n self.S_n = S_n self.u_e = u_e def convertValue(self, value): A = struct.unpack('>H', value)[0] return self.F_n / self.S_n * ((A - 0x8000) / 0x8000) * self.u_e * 2 class GSV3USB: def __init__(self, com_port, baudrate=38400): com_path = f'/dev/ttyUSB{com_port}' if platform.system() == 'Linux' else f'COM{com_port}' self.sensor = serial.Serial(com_path, baudrate) self.converter = ForceMeasurementConverterKG(10, 0.499552, 2) def read_value(self): self.sensor.read_until(b'\xA5') read_val = self.sensor.read(2) return self.converter.convertValue(read_val) # 全局变量初始化 gsv_data=[] temps=[] t_0=time.time() poll = False collect_thread = None def stop(): global poll poll = False if collect_thread and collect_thread.is_alive(): collect_thread.join() print("Stopped long running process.") def data_gsv(): global poll poll = True dev = GSV3USB(6) i=0 while poll: gsv_data.append(dev.read_value()) t1=time.time()-t_0 temps.append(t1) # 增量更新曲线,不要每次重绘整个画布 line.set_data(temps, gsv_data) ax.set_xlim(temps[i]-6, temps[i]+6) ax.set_ylim(-2,10) canvas.draw() # 给GUI事件循环留处理时间 time.sleep(0.01) i=i+1 else: return def start_collect(): global collect_thread # 避免重复点击创建多个线程 if collect_thread and collect_thread.is_alive(): return collect_thread = threading.Thread(target=data_gsv, daemon=True) collect_thread.start() # 初始化Tkinter窗口 window = tkinter.Tk() window.title("力传感器数据采集") # 将matplotlib画布嵌入Tkinter窗口 fig, ax = plt.subplots() line, = ax.plot([], [], 'b-') canvas = FigureCanvasTkAgg(fig, master=window) canvas.draw() canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1) # 按钮布局 btn_frame = tkinter.Frame(window) startButton = tkinter.Button(btn_frame, text = "Start", command = start_collect) stopButton = tkinter.Button(btn_frame, text = "Pause", command = stop) startButton.pack(side=tkinter.LEFT, padx=10, pady=5) stopButton.pack(side=tkinter.RIGHT, padx=10, pady=5) btn_frame.pack(side=tkinter.BOTTOM) window.mainloop()
关键修改说明
- 新增
threading线程模块,把数据采集和绘图逻辑放到子线程运行,主线程只处理Tkinter的GUI事件,完全解决按钮无响应问题 - 把matplotlib画布直接嵌入Tkinter界面,避免两个GUI框架的事件循环冲突,界面操作更统一
- 优化绘图逻辑,不再每次清空重绘整个画布,而是通过
set_data增量更新曲线数据,绘图性能提升10倍以上,不会出现卡顿 - 新增重复点击防护,避免多次点击Start按钮创建多个采集线程导致异常
内容的提问来源于stack exchange,提问作者FishNormal
相关产品推荐
相关产品推荐

