Morten Nissov
Morten Nissov

Reputation: 452

Reading serial input and printing to Tkinter GUI

I'm trying to make a Tkinter based GUI for an Arduino printing sensors value and responding to user input.

The code I'm trying to use to eliminate while loops is this, which doesn't print any sensor information, the only output is "Trying.../dev/ttyACM0" followed by the tkinter window opening. import serial import time from Tkinter import *

connected = False
write_to_file_path = "output.txt"
output_file = open(write_to_file_path, "w+")

locations=['/dev/ttyACM0','/dev/ttyACM1','/dev/ttyACM2','/dev/ttyACM3']

for device in locations:
    try:
        print "Trying...",device
        ser = serial.Serial(device, 9600)
        break
    except:
        print "Failed to connect on",device

## loop until the arduino tells us it is ready
while not connected:
    serin = ser.read()
    connected = True

time.sleep(0.1)
ser.flushInput()
time.sleep(1)

def serialRead():
    if ser.inWaiting():
        line = ser.readline()
        data = line.decode("utf-8").split('\t')
        print(data)
        output_file.write(line)
        root.after(1000, serialRead)



root = Tk()
root.title("Temperature Control")
serialRead()
root.mainloop()

This, on the other hand, works perfectly with the exception of not having a tkinter window. But it removes old input from the buffer and reads in the new input.

import serial
import time

connected = False
write_to_file_path = "output.txt"
output_file = open(write_to_file_path, "w+")

serial_port = '/dev/ttyACM0'
baud_rate = 9600
ser = serial.Serial(serial_port, baud_rate, timeout=5)
time.sleep(0.1)
ser.flushInput()
time.sleep(1)

while True:
    if ser.inWaiting():
        line = ser.readline()
        data = line.decode("utf-8").split('\t') #ser.readline returns a binary, convert to string
        print data[0] + '\t' + data[1]
        output_file.write(line)

This was inspired by a different stackoverflow post from a while ago: Run an infinite loop in the backgroung in Tkinter

I've seen some example using threading but I don't know much about python nor threading so I'd really like to get it to work with root.after() if that's possible. I've also tried the example using root.after, all of which are pretty similar to the one I linked, and I couldn't get any of them working. Am I doing anything obviously wrong or in a way that's much more difficult than it needs to be? I would appreciate it greatly if someone would point me in the right direction.

Upvotes: 1

Views: 3954

Answers (1)

jtwilson
jtwilson

Reputation: 325

I made a UI in TK for reading data from a GPS receiver and I had difficulties getting root.mainloop() to run, so instead I put a callback inside the TK app that ends up calling root.update() rather than mainloop.

The code looks something like this:

class App:

def __init__(self, master):
    self.sats = tk.StringVar()
    self.satsnum = tk.Label(self.frame, textvariable=self.sats, bg="blue")
    self.satsnum.pack()

def update_display(self, master):

    while source:

        self.sats.set(n_sats)

        if n_sats < 10:
            satsbgcolor = 'red'
        else:
            satsbgcolor = 'green'
        self.satsnum.configure(bg = satsbgcolor)

        master.update()
        time.sleep(1)


with serial_link.get_base_args_driver(args) as driver:
    with Handler(Framer(driver.read, driver.write, verbose=True)) as source:
        root = tk.Tk()
        app = App(root)
        app.update_display(root)

Note, the time.sleep(1) is necessary on MacOS as tk.update() will leak memory if the update is called too fast.

Upvotes: 2

Related Questions