You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Tkinter中每200ms更新显示ModbusTCP读取的PLC模拟输入值

ModbusTCP + Tkinter 实时模拟值显示与数字输出控制修复方案

关键问题分析

  • get_analog函数中的while True+time.sleep会阻塞Tkinter主线程,导致GUI无法响应操作。
  • 未使用Tkinter专用变量(StringVar/IntVar)绑定控件,普通变量无法触发GUI自动更新。
  • 按钮命令绑定的partial提前获取输入框值,点击时无法读取最新输入;脉冲函数中的time.sleep会阻塞主线程,导致GUI假死。
  • 控件命名冲突(如x_lvdt同时用于输入框和标签),导致控件被覆盖。
  • get_analog函数存在字符串拼接语法错误。

修正后的完整代码

import time
from threading import Thread, Lock
from pyModbusTCP.client import ModbusClient
from tkinter import *

# 初始化Modbus连接
c = ModbusClient(host='192.168.1.50', port=502, auto_open=True, debug=False)

window = Tk()
window.title('Stage Control')
window.geometry("600x400+10+20")

# 全局变量与锁
regs = []
regs_lock = Lock()

# 用于绑定模拟值显示的Tkinter变量
analog_x_var = StringVar(value="0")
analog_y_var = StringVar(value="0")

def polling_thread():
    """后台线程定期读取Modbus模拟输入"""
    global regs, regs_lock
    while True:
        reg_list = c.read_holding_registers(0, 2)  # 读取两个寄存器对应X、Y轴模拟值
        if reg_list:
            with regs_lock:
                regs = list(reg_list)
        time.sleep(1)

# 启动后台读取线程
tp = Thread(target=polling_thread, daemon=True)
tp.start()

def update_analog_display():
    """定期更新GUI上的模拟值显示"""
    with regs_lock:
        if len(regs) >= 1:
            analog_x_var.set(str(regs[0]))
        if len(regs) >= 2:
            analog_y_var.set(str(regs[1]))
    # 100ms后再次调用自身,实现循环更新
    window.after(100, update_analog_display)

def run_pulse(coil_num, duration):
    """线程执行脉冲输出,避免阻塞主线程"""
    try:
        duration = float(duration)
        c.write_single_coil(coil_num, True)
        time.sleep(duration)
        c.write_single_coil(coil_num, False)
        print(f"Coil {coil_num} pulse duration: {duration}s")
    except ValueError:
        print("输入的脉冲时长不是有效数字")

# 数字输出控制函数
def xreverse():
    Thread(target=run_pulse, args=(2, x_pulse_duration.get()), daemon=True).start()

def x():
    Thread(target=run_pulse, args=(2, x_pulse_duration.get()), daemon=True).start()

def yreverse():
    Thread(target=run_pulse, args=(0, y_pulse_duration.get()), daemon=True).start()

def y():
    Thread(target=run_pulse, args=(0, y_pulse_duration.get()), daemon=True).start()

# 控件创建
# 输入框:脉冲时长
x_pulse_duration = Entry(window, width=3, font=('Arial 24'))
x_pulse_duration.place(x=195, y=150)
x_pulse_duration.insert(0, "0.5")  # 设置默认值

y_pulse_duration = Entry(window, width=3, font=('Arial 24'))
y_pulse_duration.place(x=335, y=150)
y_pulse_duration.insert(0, "0.5")  # 设置默认值

# 模拟值显示输入框(只读)
x_lvdt_display = Entry(window, width=5, font=('Arial 16'), textvariable=analog_x_var, state="readonly")
x_lvdt_display.place(x=190, y=250)

y_lvdt_display = Entry(window, width=5, font=('Arial 16'), textvariable=analog_y_var, state="readonly")
y_lvdt_display.place(x=330, y=250)

# 按钮
move_x = Button(window, text="<", fg='blue', command=xreverse)
move_x.place(x=180, y=200)

move_x_reverse = Button(window, text=">", fg='blue', command=x)
move_x_reverse.place(x=220, y=200)

move_y = Button(window, text="<", fg='blue', command=yreverse)
move_y.place(x=320, y=200)

move_y_reverse = Button(window, text=">", fg='blue', command=y)
move_y_reverse.place(x=360, y=200)

# 标签
lbl = Label(window, text="pulse duration", fg='blue', font=("ARIAL", 10))
lbl.place(x=252, y=160)

xaxis = Label(window, text="X-AXIS", fg='blue', font=("ARIAL", 24))
xaxis.place(x=175, y=95)

yaxis = Label(window, text="Y-AXIS", fg='blue', font=("ARIAL", 24))
yaxis.place(x=325, y=95)

x_lvdt_label = Label(window, text="X LVDT", fg='blue', font=("ARIAL", 16))
x_lvdt_label.place(x=189, y=270)

y_lvdt_label = Label(window, text="Y LVDT", fg='blue', font=("ARIAL", 16))
y_lvdt_label.place(x=327, y=270)

# 启动模拟值更新循环
update_analog_display()

window.mainloop()

核心修改说明

  1. 保留后台读取线程:继续用独立线程读取Modbus寄存器,避免阻塞主线程。
  2. GUI更新改用after机制:移除阻塞循环,用window.after定期触发更新函数,确保GUI响应性。
  3. Tkinter变量绑定:使用StringVar绑定模拟值显示控件,值更新时自动刷新GUI。
  4. 脉冲输出线程化:将脉冲逻辑放到独立线程执行,避免time.sleep导致GUI假死;按钮直接绑定函数,点击时实时读取输入框最新值。
  5. 修复控件命名冲突:区分模拟值显示控件和说明标签的变量名,避免覆盖。
  6. 添加默认值与错误处理:给输入框设置默认脉冲时长,捕获数字格式错误,提升易用性。

内容的提问来源于stack exchange,提问作者jeffg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 00:09:52