Wednesday, 19 January 2022

Threading reading a serial port in Python (with a GUI)

I want to trigger an event whenever there is data to be read from a serial port while running a GUI. The pySerial module apparently has experimental functionality for that, but it isn't particularly well documented (I couldn't find any useful examples in the API).

This question appears to deal with the same or at least very similar task, but doesn't provide instructions to replicate it or working code examples.

I came up with this code:

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()

Running it does indeed trigger the event, but only once. Why is that? Is there a "recommended" way to do this as by using the functionality of pySerial itself?

Alternatively, I would also run the function to read and process data on an event like you can with GUI elements. If that is the better solution, how would that be done?

Related question (unanswered), probably makes this question a duplicate

Edit: Here is an example derived from the answer below that seems to work. What are the things not there necessary for?

import tkinter as tk

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

app = tk.Tk()
button = tk.Button(text="Click me!")
button.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"""
        app.after(0, updateButtonData, data)

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

# Initiate serial port
serial_port = Serial("/dev/ttyACM1")
# set the port in the class too?
SerialReaderProtocolRaw.port = serial_port

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

app.mainloop()


from Threading reading a serial port in Python (with a GUI)

No comments:

Post a Comment