在 Python 中线程读取串行端口(使用 GUI)

2023-12-25

我想在运行 GUI 时每当有数据要从串行端口读取时触发一个事件。这pySerial模块显然具有实验性功能,但没有特别详细的记录(我找不到任何有用的示例在 API 中 https://pyserial.readthedocs.io/en/latest/pyserial_api.html#module-serial.threaded).

这个问题 https://stackoverflow.com/questions/4169219/constantly-reading-from-a-serial-port-with-a-background-thread似乎处理相同或至少非常相似的任务,但不提供复制它的说明或工作代码示例。

我想出了这个代码:

import tkinter as tk
import serial
import threading

# Create GUI window
window = tk.Tk()

# Initialize the port
myPort = serial.Serial('/dev/ttyUSB0')

# Function to call whenever there is data to be read
def readFunc(port):
    port.readline()
    print('Line read')

# Configure threading
t1 = threading.Thread(target = readFunc, args=[myPort])
t1.start()

# Main loop of the window
window.mainloop()

运行它确实会触发该事件,但只会触发一次。这是为什么?是否有“推荐”的方法来执行此操作,例如使用以下功能pySerial itself?

或者,我也会运行该函数来读取和处理事件数据,就像使用 GUI 元素一样。如果这是更好的解决方案,该怎么做?

相关问题 https://stackoverflow.com/questions/26600753/event-handler-in-python-serial-port-data-received(未回答),可能会使这个问题重复

编辑:这是从下面的答案中得出的一个最小示例,每当将数据读取到传入数据时,就会更改标签的文本:

import tkinter as tk

from serial import Serial
from serial.threaded import ReaderThread, Protocol

app = tk.Tk()
label = tk.Label(text="A Label")
label.pack()

class SerialReaderProtocolRaw(Protocol):
    port = None

    def connection_made(self, transport):
        """Called when reader thread is started"""
        print("Connected, ready to receive data...")

    def data_received(self, data):
        """Called with snippets received from the serial port"""
        updateLabelData(data)

def updateLabelData(data):
    data = data.decode("utf-8")
    label['text']=data
    app.update_idletasks()

# Initiate serial port
serial_port = Serial("/dev/ttyACM0")

# Initiate ReaderThread
reader = ReaderThread(serial_port, SerialReaderProtocolRaw)
# Start reader
reader.start()

app.mainloop()

当您从另一个正在运行的线程更新 GUI 时,您主要关心的是线程安全。
为了实现这一点,我们可以使用 .after() 方法,该方法为任何给定的 tk 小部件执行回调。

您的请求的另一部分是使用线程串行读取器。
这可以通过使用来实现读者话题 https://pyserial.readthedocs.io/en/latest/pyserial_api.html#serial.threaded.ReaderThread附有协议。

您可以选择两种协议:

  • 原始数据读取器协议 https://pyserial.readthedocs.io/en/latest/pyserial_api.html#serial.threaded.Protocol,在数据到来时读取数据
  • 行读取器协议 https://pyserial.readthedocs.io/en/latest/pyserial_api.html#serial.threaded.LineReader,这使我们能够读取数据行

这是工作代码示例,其中包含上面提到的两种协议,因此您可以选择适合您的一种。请记住,来自串行端口的所有数据都只是原始字节。

import tkinter as tk

from serial import Serial
from serial.threaded import ReaderThread, Protocol, LineReader


class SerialReaderProtocolRaw(Protocol):
    tk_listener = None

    def connection_made(self, transport):
        """Called when reader thread is started"""
        if self.tk_listener is None:
            raise Exception("tk_listener must be set before connecting to the socket!")
        print("Connected, ready to receive data...")

    def data_received(self, data):
        """Called with snippets received from the serial port"""
        self.tk_listener.after(0, self.tk_listener.on_data, data.decode())


class SerialReaderProtocolLine(LineReader):
    tk_listener = None
    TERMINATOR = b'\n\r'

    def connection_made(self, transport):
        """Called when reader thread is started"""
        if self.tk_listener is None:
            raise Exception("tk_listener must be set before connecting to the socket!")
        super().connection_made(transport)
        print("Connected, ready to receive data...")

    def handle_line(self, line):
        """New line waiting to be processed"""
        # Execute our callback in tk
        self.tk_listener.after(0, self.tk_listener.on_data, line)


class MainFrame(tk.Frame):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.listbox = tk.Listbox(self)
        self.listbox.pack()
        self.pack()

    def on_data(self, data):
        print("Called from tk Thread:", data)
        self.listbox.insert(tk.END, data)


if __name__ == '__main__':
    app = tk.Tk()

    main_frame = MainFrame()
    # Set listener to our reader
    SerialReaderProtocolLine.tk_listener = main_frame
    # Initiate serial port
    serial_port = Serial("/dev/ttyUSB0")
    # Initiate ReaderThread
    reader = ReaderThread(serial_port, SerialReaderProtocolLine)
    # Start reader
    reader.start()

    app.mainloop()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Python 中线程读取串行端口(使用 GUI) 的相关文章

随机推荐

  • 使用堆内存的高效 2 遍

    我有一个算法 需要对文件数据进行两次传递 该文件可能是stdin或流 如 因为这是一个命令行工具 不幸的是 据我所知 这使我排除了mmap 我需要第一遍中的信息才能在第二遍上执行写入操作 这是因为我需要对第二遍特定密码的第一遍所有字节进行求
  • CkRest.AddHeader 函数不使用 Chilkat C++ 添加标头(使用 fullRequestBinary PUT 的“Content-MD5”标头)

    我们正在使用 Chilkat 9 5 0 80 C 库 我们无法将某个 HTTP 标头添加到请求中 Content MD5 当我们像这样添加这个标头时 m ckRest AddHeader Content MD5 any value her
  • Google 身份验证/Gapi:未捕获类型错误:无法读取 null 的属性“postMessage”

    祝大家有美好的一天 我正在使用创建一个网络应用程序Angular js 1 5 谷歌身份验证 gapi and 用户界面路由器 我的主要目标是初始化谷歌用户首先 使用解决方法从主要抽象状态 stateProvider state app u
  • 设置网格列/行的最小、最大和默认长度

    我知道新的minmax https developer mozilla org en US docs Web CSS minmax允许指定网格列的最小和最大宽度的函数 然而 当使用此函数时 我不清楚列的 默认 宽度是什么 以及如何指定它 在
  • 在Android上过滤目录中的文件

    在我的应用程序中 我从图库中的文件夹中获取图像并将其保存到数组列表中 现在我只想提取扩展名为 jpg 的文件 我该怎么做 保存到数组列表的代码是 private List
  • 使用 DQL 查询返回外键

    我有一个像这样的 InvoiceItem 实体 Entity class InvoiceItem ManyToOne targetEntity Invoice inversedBy items JoinColumn name invoice
  • grails 2.3:测试应用程序无法识别测试

    我正在使用最近发布的 grails 2 3 0 不幸的是 测试应用程序无法识别测试 这是我为产生问题所做的事情 首先 创建一个新应用程序并创建一个控制器 grails create app firstApp cd firstApp grai
  • glClearColor() 将 iPhone 的渲染利用率推至 27%

    寻找答案的结果是这个问题 https stackoverflow com questions 872560 512x512 texture causing huge gpu stress on iphone despite tiling 我
  • Python:使用minidom搜索具有特定文本的节点

    我目前面临的 XML 看起来像这样
  • 存储来自 Google Places API 的数据

    如果这个问题听起来有点傻 请不要介意 我正在尝试了解有关 Google Places API 的更多信息 我有兴趣做一个类似于airbnb com的实现 检查顶部的搜索功能 它在自动建议中显示 powered by google 在我们的地
  • 2 个给定数字之间的双精度数密度

    重要编辑 最初的问题是关于获取双精度数和分数的密度 当我得到双精度数而不是分数的答案时 我正在改变主题以结束这个问题 原问题的另一半是here https stackoverflow com questions 48245279 densi
  • Win32 防止窗口“折断”

    如何为我的应用程序禁用 Windows 7 的捕捉功能 以编程方式 或者有什么方法可以检测应用程序是否已被捕捉 并专门调用API函数来取消捕捉 调用 SetWindowPos 或 ShowWindow 无法正确取消对齐 SW MAXIMIZ
  • 使用python中的struct模块打包和解包可变长度数组/字符串

    我试图掌握 Python 3 中二进制数据的打包和解包 它实际上并不难理解 除了一个问题 如果我有一个可变长度的文本字符串并且想要以最优雅的方式打包和解包它怎么办 据我从手册中可以看出 我只能直接解压固定大小的字符串 在这种情况下 是否有任
  • MS Access VBA 捕获 SQL Server 连接错误

    我在获取 Access 2010 VBA 来捕获与 SQL Server 2008 连接以链接表的错误时遇到问题 我收到错误并弹出窗口 可能是来自 ODBC 驱动程序 我想抑制这些并自己处理错误 我知道 DAO errors 和 ADO e
  • Jquery 添加值以选择选项

    我已经用谷歌搜索这个选项很多天了 但找不到解决方案 我想要的是 我有两个选择框 第一个选择框有国家 地区名称 第二个选择框为空 我想要的是 当我从第一个选择框中选择任何国家 即英国 时 应该运行 php 查询以从表中获取所有城市名称 然后使
  • 构建 APK - 错误 - app:transformClassesWithDexForDebug

    我在这里看了很多线程 但仍然没有找到有效的解决方案 当我想构建 APK 时 出现以下主要错误 错误 任务执行失败 app transformClassesWithDexForDebug com android build api trans
  • Chartjs 拆分两位数数字

    我已经多次看到这个问题 但我找不到适合我的解决方案 我将 Django 变量传递到 Chartjs 中进行绘图 所有单位数字都是正确的 但它会将两位数变成单位数 就像 11 是 1 1 23 是 2 3 我尝试了很多不同的方法 但无法弄清楚
  • 由于 StackOverflowError,无法完成 Web 应用程序 [/app] 的注释扫描

    我正在使用 STS eclipse 插件 和 maven 开发 Spring MVC 应用程序 为了创建项目 我按照 STS 向导创建了一个新的 Spring MVC 项目 之后 我向其他项目和库添加了一些依赖项 然而 当我现在尝试将项目部
  • -bash:__git_ps1:找不到命令

    我尝试安装 Ruby 2 0 我的命令行出现了问题 现在如下所示 bash git ps1 command not found 11 58 28 whatever whatever 我不知道如何摆脱 git ps1 command not
  • 在 Python 中线程读取串行端口(使用 GUI)

    我想在运行 GUI 时每当有数据要从串行端口读取时触发一个事件 这pySerial模块显然具有实验性功能 但没有特别详细的记录 我找不到任何有用的示例在 API 中 https pyserial readthedocs io en late