0

I have to build an application that maintains a GUI while a serial reader is constantly running in the background. The serial reader updates variables that i need to show on my GUI. So far i have this:

# These variables are updated by the reader.
var1 = 0
var2 = 0
var3 = 0

#Serial reader
def readserial(self):
ser = serial.Serial(port='COM4', baudrate=9600, timeout=1)
while 1:
    b = ser.readline()
    if b.strip():
        #Function to set variables var1,var2,var3
        handle_input(b.decode('utf-8'))

#Simple GUI to show the variables updating live
root = Tk()
root.title("A simple GUI")

gui_var1 = IntVar()
gui_var1.set(var1)

gui_var2 = IntVar()
gui_var2.set(var2)

gui_var3 = IntVar()
gui_var3.set(var3)

root.label = Label(root, text="My Gui")
root.label.pack()

root.label1 = Label(root, textvariable=gui_var1)
root.label1.pack()

root.label2 = Label(root, textvariable=gui_var2)
root.label2.pack()

root.label3 = Label(root, textvariable=gui_var3)
root.label3.pack()

root.close_button = Button(root, text="Close", command=root.quit)
root.close_button.pack()

#Start GUI and Serial
root.mainloop()
readserial()

As it is now my gui opens and as soon as i close it the serial starts reading.

3
  • are you aware that mainloop() doesn't return until the root window is destroyed? Commented Nov 13, 2016 at 15:06
  • now you have to loops - while 1 and mainloop. They have to work at the same time. You can user threading module to run while 1 in separated thread. Or root.after(miliseconds, function_name) to run ser.readline() ... periodicaly (without while 1). after may not work if readline blocks program waiting for data. Commented Nov 13, 2016 at 15:21
  • I am aware that i'm dealing with two loops here. I guess my question can be summarized to "How do i make these two loops run at the same time?" Commented Nov 13, 2016 at 15:37

1 Answer 1

2

You can use root.after(miliseconds, function_name_without_brackets) to run function readserial periodically - without while 1.

Tested on Linux with virtual COM ports /dev/pts/5, /dev/pts/6.

import tkinter as tk
import serial 

# --- functions ---

def readserial():
    b = ser.readline()
    if b.strip():
         label['text'] = b.decode('utf-8').strip()
    # run again after 100ms (mainloop will do it)
    root.after(100, readserial)

# --- main ---

ser = serial.Serial(port='COM4', baudrate=9600, timeout=1)
#ser = serial.Serial(port='/dev/pts/6', baudrate=9600, timeout=1)

root = tk.Tk()

label = tk.Label(root)
label.pack()

button = tk.Button(root, text="Close", command=root.destroy)
button.pack()

# run readserial first time after 100ms (mainloop will do it)
root.after(100, readserial)

# start GUI
root.mainloop()
Sign up to request clarification or add additional context in comments.

2 Comments

I cannnot do it like this, the serial read function needs to run uninterrupted or the readings that are received are incomplete.
Of course you can do it this way. The serial port buffers data until you are ready to read it. If the buffer fills up to quickly then you might lose data, the solution is to read from the serial port more often.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.