Tkinter与Modbus Server结合:写入寄存器数据不持久问题求助
问题分析与解决方案:Modbus Server寄存器值无法持久保存
问题根源
- 重复创建Server实例:每次点击"Write Data to Server"按钮,都会在
print_entry()中新建ModbusServer对象,旧的Server实例被销毁,寄存器数据自然清零。 - 数据类型不匹配:输入框获取的是字符串,直接写入寄存器会导致类型错误;Port参数需要整数类型,字符串传入会引发启动异常。
- 冗余控件创建:每次点击都新建Label覆盖原有控件,属于无效重复操作,还会增加内存占用。
修复方案
- 将
ModbusServer实例设为全局变量,确保整个程序生命周期内只初始化一次。 - 首次点击按钮时初始化并启动Server,后续点击仅更新寄存器数据,不再重启Server。
- 将输入框的字符串值转换为整数后写入寄存器,Port参数也转换为整数类型。
- 提前创建用于显示的Label控件,点击按钮时仅更新其
text属性,避免重复创建。 - 绑定窗口关闭事件,确保关闭程序时停止Server,避免残留进程。
修改后的完整代码
from tkinter import * import tkinter as tk from pyModbusTCP.server import ModbusServer # 创建主窗口 root = tk.Tk() root.geometry("300x290") root.title("Resolve Batterie Profil") # 全局变量:Modbus Server实例 server = None # 描述标签区域 #------------------------------------------------------------------------------ IPFensterLabel = Label(root, text="Server IP", fg="black", bg="white") IPFensterLabel.place(y=0, x=0, width=100, height=30) PortFenster = Label(root, text="Port", fg="black", bg="white") PortFenster.place(y=30, x=0, width=100, height=30) SOCFenster = Label(root, text="SOC", fg="black", bg="white") SOCFenster.place(y=80, x=0, width=100, height=30) SOHFenster = Label(root, text="SOH", fg="black", bg="white") SOHFenster.place(y=110, x=0, width=100, height=30) EntladeFenster = Label(root, text="Entladeleistung", fg="black", bg="white") EntladeFenster.place(y=140, x=0, width=100, height=30) LadeFenster = Label(root, text="Ladeleistung", fg="black", bg="white") LadeFenster.place(y=170, x=0, width=100, height=30) # 输入框区域 #------------------------------------------------------------------------------ IPEntry = Entry(root, fg="black", bg="white") IPEntry.place(y=0, x=100, width=100, height=30) IPEntry.insert(0, "127.0.0.1") # 设置默认IP PortEntry = Entry(root, fg="black", bg="white") PortEntry.place(y=30, x=100, width=100, height=30) PortEntry.insert(0, "502") # 设置默认Modbus端口 SOCEntry = Entry(root, fg="black", bg="white") SOCEntry.place(y=80, x=100, width=100, height=30) SOHEntry = Entry(root, fg="black", bg="white") SOHEntry.place(y=110, x=100, width=100, height=30) EntladeEntry = Entry(root, fg="black", bg="white") EntladeEntry.place(y=140, x=100, width=100, height=30) LadeEntry = Entry(root, fg="black", bg="white") LadeEntry.place(y=170, x=100, width=100, height=30) # 提前创建数据显示标签,后续仅更新内容 #------------------------------------------------------------------------------ IPDisplayLabel = Label(root, fg="black", bg="white") IPDisplayLabel.place(y=0, x=200, width=100, height=30) PortDisplayLabel = Label(root, fg="black", bg="white") PortDisplayLabel.place(y=30, x=200, width=100, height=30) SOCDisplayLabel = Label(root, fg="black", bg="white") SOCDisplayLabel.place(y=80, x=200, width=100, height=30) SOHDisplayLabel = Label(root, fg="black", bg="white") SOHDisplayLabel.place(y=110, x=200, width=100, height=30) EntladeDisplayLabel = Label(root, fg="black", bg="white") EntladeDisplayLabel.place(y=140, x=200, width=100, height=30) LadeDisplayLabel = Label(root, fg="black", bg="white") LadeDisplayLabel.place(y=170, x=200, width=100, height=30) ConnectionFenster = Label(root, fg="black", bg="white") ConnectionFenster.place(y=200, x=90, width=120, height=30) # 核心功能函数 #------------------------------------------------------------------------------ def print_entry(): global server try: # 转换输入数据为正确类型 ip = IPEntry.get() port = int(PortEntry.get()) soc_val = int(SOCEntry.get()) soh_val = int(SOHEntry.get()) entlade_val = int(EntladeEntry.get()) lade_val = int(LadeEntry.get()) except ValueError: print("输入数据格式错误,请确保所有数值为整数") ConnectionFenster.config(text="输入错误") return # 首次启动或Server未运行时初始化Server if server is None or not server.is_run: server = ModbusServer(ip, port, no_block=True) try: server.start() print("Server启动成功") ConnectionFenster.config(text="已连接") except Exception as e: print(f"Server启动失败:{e}") ConnectionFenster.config(text="连接失败") return else: print("Server已在运行,直接更新寄存器") # 更新显示标签内容 IPDisplayLabel.config(text=ip) PortDisplayLabel.config(text=str(port)) SOCDisplayLabel.config(text=str(soc_val)) SOHDisplayLabel.config(text=str(soh_val)) EntladeDisplayLabel.config(text=str(entlade_val)) LadeDisplayLabel.config(text=str(lade_val)) # 写入Modbus寄存器 server.data_bank.set_input_registers(7, [soc_val]) server.data_bank.set_input_registers(8, [soh_val]) server.data_bank.set_input_registers(9, [entlade_val]) server.data_bank.set_input_registers(10, [lade_val]) # 调试输出 print("\n") print(f"Server运行状态:{server.is_run}") print(f"寄存器7当前值:{server.data_bank.get_input_registers(7)}") # 按钮区域 #------------------------------------------------------------------------------ Button2 = tk.Button(text="Write Data to Server", command=print_entry) Button2.place(y=230, x=90, width=120, height=30) def on_close(): # 关闭程序前停止Server if server and server.is_run: server.stop() print("Server已停止") root.destroy() QuitButton = tk.Button(text="Close Programm", command=on_close) QuitButton.place(y=260, x=90, width=120, height=30) # 绑定窗口关闭事件 root.protocol("WM_DELETE_WINDOW", on_close) root.mainloop()
关键修改说明
- 全局Server实例:确保整个程序只创建一个Server,避免重复初始化导致数据丢失。
- 数据类型校验与转换:解决输入类型不匹配问题,避免启动Server或写入寄存器时出错。
- 复用显示控件:提前创建Label,点击时仅更新内容,优化程序性能。
- 优雅关闭Server:绑定窗口关闭事件,确保程序退出时停止Server,避免残留进程。
内容的提问来源于stack exchange,提问作者Hotte
相关产品推荐
相关产品推荐

