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

PySimpleGUI文本元素仅首次更新求助:STM32串口通信场景

PySimpleGUI文本元素仅首次更新串口数据的问题解决

我用以下程序向STM32板发送数据,并将收到的字符串更新到PySimpleGUI的文本元素中。数据能正常发送到板卡,板卡也能正确回复(控制台可以每次打印接收数据),文本元素首次接收数据时也能正常更新,但首次传输后,虽然程序还能打印接收数据,文本元素却不再显示内容(甚至内容消失);如果用另一个元素显示每次传输更新的计数器,计数器却能正常显示。

import PySimpleGUI as sg
import serial
from serial.tools.list_ports import comports

def monitoraSerial():
  valoresExibir = ''
  while True:
    try:
      while interfaceSerial.in_waiting > 0:
        charRecebido = interfaceSerial.read().decode("ascii")
        if charRecebido == '\n':
          window['-VALORES-'].update(valoresExibir)
          valoresExibir = ''
        else:
          valoresExibir += charRecebido
        #window['-VALORES-'].update(interfaceSerial.readline().decode(encoding="ascii")[0:-1])
    except:
      break

window.start_thread(monitoraSerial, '-ENCERRASERIAL-')

while True:
  event, values = window.read()
  if event in (sg.WIN_CLOSED, 'Cancel'):
    break

接收数据格式为"p: %d, i: %d, d: %d, f: %d\n"。

我已经尝试了以下操作:

  • 强制设置visible属性为True;
  • 调用refresh和finalize方法;
  • 使用readline方法;

期望'-VALORES-'元素在每次传输时都能更新,但实际仅首次传输时更新,后续内容消失。请问应如何修改?


问题原因与解决方法

核心问题在于子线程中直接操作PySimpleGUI元素不是线程安全的,PySimpleGUI的GUI更新必须在主线程中完成。你的子线程monitoraSerial直接调用window['-VALORES-'].update(),首次可能侥幸成功,但后续会因为线程冲突导致元素更新失效。

修改思路是让子线程把收到的数据发送到主线程,由主线程负责更新GUI元素,具体实现如下:

修改后的完整代码

import PySimpleGUI as sg
import serial
from serial.tools.list_ports import comports

def monitoraSerial(window):
    valoresExibir = ''
    while True:
        try:
            while interfaceSerial.in_waiting > 0:
                charRecebido = interfaceSerial.read().decode("ascii")
                if charRecebido == '\n':
                    # 将数据发送给主线程,触发自定义事件
                    window.write_event_value('-SERIAL_DATA-', valoresExibir)
                    valoresExibir = ''
                else:
                    valoresExibir += charRecebido
        except Exception as e:
            print(f"串口读取异常: {e}")
            break

# 初始化GUI窗口(示例,可根据需求调整布局)
layout = [
    [sg.Text("串口数据:"), sg.Text("", key='-VALORES-', size=(30, 1))],
    [sg.Button("Cancel")]
]
window = sg.Window("串口监控", layout, finalize=True)

# 初始化串口(根据实际端口、波特率修改)
interfaceSerial = serial.Serial('COM3', 9600, timeout=1)

# 启动子线程,传入window对象用于线程通信
window.start_thread(lambda: monitoraSerial(window), '-ENCERRASERIAL-')

while True:
    event, values = window.read()
    if event in (sg.WIN_CLOSED, 'Cancel'):
        if interfaceSerial.is_open:
            interfaceSerial.close()
        break
    # 处理子线程发来的串口数据事件
    elif event == '-SERIAL_DATA-':
        # 在主线程安全更新GUI元素
        window['-VALORES-'].update(values[event])

window.close()

补充说明

  1. 原代码缺失window和interfaceSerial的初始化逻辑,修改后的代码补充了示例内容,需根据实际硬件配置调整串口参数;
  2. 子线程通过window.write_event_value()发送自定义事件,这是PySimpleGUI官方推荐的线程间通信方式,能确保GUI更新的线程安全性;
  3. 如果想用readline简化代码,子线程可改为:
def monitoraSerial(window):
    while True:
        try:
            line = interfaceSerial.readline().decode("ascii").strip()
            if line:
                window.write_event_value('-SERIAL_DATA-', line)
        except Exception as e:
            print(f"串口读取异常: {e}")
            break

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 10:12:49