如何推送实时Pandas DataFrame并适配索引至Tkinter pandastable表格?
修复MQTT实时数据更新Pandastable表格的问题
我看了你这段代码,发现几个关键问题导致实时数据无法正常更新到表格里,还有一些逻辑上的疏漏,下面一步步帮你梳理和修复:
首先列出代码里的核心问题:
- 没有初始化
self.dataframe,导致update方法里访问self.dataframe.loc[len(self.dataframe)]时会报错 update方法定义了data参数,但启动线程时没传参,而且缺少循环获取MQTT数据的逻辑,现在只执行一次就结束- 构造实时DataFrame的方式错误:一维数组转DataFrame会把每个元素拆成单独一行,而不是一行完整数据
self.stop变量没有在初始化时定义,点击STOP按钮前访问会触发AttributeError- 缺少实时数据写入CSV的逻辑,无法实现历史数据的持久化
- 注释掉了MQTT相关的导入,实际运行时需要恢复并处理数据接收
修复后的完整代码
import tkinter as tk import pandas as pd from pandastable import Table, TableModel from threading import Thread import time import datetime import numpy as np # 恢复你的MQTT导入模块,比如: # import mqtt_cloud_rec # import tkintermqtt # 确保CSV文件存在,不存在则创建空表(避免首次运行报错) try: prevframe = pd.read_csv('mqttresult.csv') except FileNotFoundError: # 初始化空DataFrame,和你的列名对应 cols = ['Date','Time','power state','Motor state','Mode','Voltage','Current','Power Factor','KW','KWH','total Runtime'] prevframe = pd.DataFrame(columns=cols) prevframe.to_csv('mqttresult.csv', index=False) class TestApp(tk.Frame): """Basic test frame for the table""" def __init__(self, parent=None): self.parent = parent tk.Frame.__init__(self) self.main = self.master self.main.geometry('800x600+200+100') self.main.title('Mqtt Result Table') # 初始化停止标志和数据帧 self.stop = False self.dataframe = prevframe.copy() # 复制历史数据到实例变量 f = tk.Frame(self.main) f.pack(fill=tk.BOTH,expand=1) # 创建表格并绑定数据帧 self.table = Table(f, dataframe=self.dataframe, showtoolbar=True ) self.table.show() # 按钮布局 self.startbutton = tk.Button(self.main,text='START',command=self.start) self.startbutton.pack(side=tk.TOP,fill=tk.X) self.stopbutton = tk.Button(self.main,text='STOP',command=self.set_stop) self.stopbutton.pack(side=tk.TOP,fill=tk.X) def update_table(self): """循环获取MQTT数据并更新表格""" while not self.stop: # --- 这里替换成你实际获取MQTT数据的逻辑 --- # 示例:模拟MQTT接收的列表数据(和你的列对应,除了Date/Time) # 实际应该调用你的MQTT订阅方法,比如: # self.data = mqtt_cloud_rec.get_latest_data() self.data = ["ON", "RUNNING", "AUTO", "220V", "5A", "0.95", "1.1KW", "12.5KWH", "450H"] # 构造当前时间 date_today = str(datetime.date.today()) time_today = time.strftime("%H:%M:%S") # 构造完整的一行数据:时间 + Mqtt数据 new_row = [date_today, time_today] + self.data # 将新行添加到DataFrame self.dataframe.loc[len(self.dataframe)] = new_row # 更新表格数据并刷新 self.table.model.df = self.dataframe self.table.redraw() # 将更新后的数据写入CSV,持久化历史数据 self.dataframe.to_csv('mqttresult.csv', index=False) # 控制更新频率,避免太频繁 time.sleep(1) def start(self): """启动实时更新线程""" if not hasattr(self, 'update_thread') or not self.update_thread.is_alive(): self.stop = False self.update_thread = Thread(target=self.update_table, daemon=True) self.update_thread.start() def set_stop(self): """停止实时更新""" self.stop = True if __name__ == "__main__": app = TestApp() app.mainloop()
关键修改点说明:
初始化处理:
- 增加了CSV文件不存在时的初始化逻辑,避免首次运行报错
- 在
__init__里初始化self.stop = False和self.dataframe = prevframe.copy(),确保实例变量存在
更新逻辑重构:
- 把原
update方法改成update_table,去掉多余的data参数,改成循环获取数据直到self.stop为True - 修复了DataFrame行添加的逻辑:直接用列表构造一行数据,避免数组转置的错误
- 把原
线程管理:
- 启动线程时判断线程是否存活,避免重复启动多个线程
- 设置线程为
daemon=True,确保关闭窗口时线程自动退出
数据持久化:
- 每次更新表格后,把完整的DataFrame写入CSV,保证历史数据不会丢失
MQTT数据接入:
- 预留了MQTT数据获取的位置,你只需要把模拟数据的部分替换成实际从MQTT订阅接收的列表数据即可
内容的提问来源于stack exchange,提问作者isrj5
相关产品推荐
相关产品推荐

