在运行GUI时,只要有数据要从串行端口读取,我就想触发一个事件。pySerial
模块显然具有实验功能,但它并没有特别好的文档记录(我在API中找不到任何有用的示例(。
这个问题似乎处理相同或至少非常相似的任务,但没有提供复制它的说明或工作代码示例。
我想出了这个代码:
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
本身的功能来做到这一点?
或者,我也可以运行该函数来读取和处理事件上的数据,就像使用GUI元素一样。如果这是更好的解决方案,该如何做到?
相关问题(未回答(,可能会使此问题成为重复的
编辑:下面是一个从下面的答案中得出的最小示例,每当数据被读取到传入数据时,它都会更改标签的文本:
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时,您主要关心的是线程安全
为了实现这一点,我们可以使用.fafter((方法,该方法对任何给定的tk小部件执行回调。
您请求的另一部分是使用Threaded串行读取器
这可以通过使用ReaderThread和Protocol来实现。
您可以选择两种协议:
- 原始数据读取器协议,在数据到来时读取数据
- 线路读取器协议,使我们能够读取数据线路
这是一个工作代码示例,上面提到了两个协议,所以您可以选择适合您的协议。请记住,所有来自串行端口的数据都只是原始字节。
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'nr'
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()