Python Forum
Synchronizing tkinter with serial baud rate
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Synchronizing tkinter with serial baud rate
#3
Thanks. I tried your approach, but it does not work.

I get no error, but i see nothing printed on tkinter. Full code here:

import tkinter as tk
import tkinter.ttk as ttk
import serial.tools.list_ports #for a list of all the COM ports
from tkinter import scrolledtext
from time import sleep


#to be used on our canvas
HEIGHT = 800
WIDTH = 800

#hardcoded baud rate
baudRate = 9600

# this is the global variable that will hold the serial object value
ser = None #initial  value. will change at 'on_select()'

after_id = None

#this is the global variable that will hold the value from the dropdown for the sensor select
dropdown_value = None



# --- functions ---

#the following two functtions are for the seria port selection, on frame 1

#this function populates the combobox on frame1, with all the serial ports of the system
def serial_ports():
    return serial.tools.list_ports.comports()


#when the user selects one serial port from the combobox, this function will execute
def on_select(event=None):
    global ser
    COMPort = cb.get()
    string_separator = "-"
    COMPort = COMPort.split(string_separator, 1)[0] #remove everything after '-' character
    COMPort = COMPort[:-1] #remove last character of the string (which is a space)
    ser = serial.Serial(port = COMPort, baudrate=9600, timeout=0)
    #readSerial() #start reading shit. DELETE. later to be placed in a button
    # get selection from event    
    #print("event.widget:", event.widget.get())
    # or get selection directly from combobox
    #print("comboboxes: ", cb.get())

    #ser = Serial(serialPort , baudRate, timeout=0, writeTimeout=0) #ensure non-blocking



def readSerial():
    if reading_serial:
        while (ser_bytes := ser.readline()):
            try:
                text.insert("end", ser_bytes.decode("utf-8"))
                if vsb.get()[1]==1.0: #if the scrollbar is down to the bottom, then autoscroll
                   text.see("end")
            except UnicodeExceptionError:
                print("Oops")
        root.after(50,readSerial)




# this function is triggered, when a value is selected from the dropdown
def dropdown_selection(*args):    
   global dropdown_value
   dropdown_value = clicked.get()
   button_single['state'] = 'normal' #when a selection from the dropdown happens, change the state of the 'Measure This Sensor' button to normal


# this function is triggered, when button 'Measure all Sensors' is pressed, on frame 2
def measure_all():    
   global reading_serial
   reading_serial = True
   button_stop['state']='normal' #make the 'Stop Measurement' button accessible
   ser.write("rf".encode()) #Send string 'rf to arduino', which means Measure all Sensors
   sleep(0.05) # 50 milliseconds
   readSerial() #Start Reading data


# this function is triggered, when button 'Measure this Sensor' is pressed, on frame 2
def measure_single(): 
   print(dropdown_value)   
   global stop_
   stop_=False
   button_stop['state']='normal'
   string_to_send = 'r' + ' ' + str(dropdown_value)#CHANGE
   ser.write(string_to_send.encode()) #Send string 'rf to arduino', which means Measure all Sensors!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   readSerial()


# this function is triggered, when button 'STOP measurement(s)' is pressed, on frame 2
def stop_measurement():    
    global reading_serial
    reading_serial=False
    button_stop['state']='disabled'
    ser.write("c".encode())
    root.after_cancel(after_id) #do i need after_id to be global?
# --- functions ---



# --- main ---
root = tk.Tk() #here we create our tkinter window
root.title("Sensor Interface")

#we use canvas as a placeholder, to get our initial screen size (we have defined HEIGHT and WIDTH)
canvas = tk.Canvas(root, height=HEIGHT, width=WIDTH)
canvas.pack()

#we use frames to organize all the widgets in the screen

'''
relheight, relwidth - Height and width as a float between 0.0 and 1.0, as a fraction of the height and width of the parent widget.
relx, rely - Horizontal and vertical offset as a float between 0.0 and 1.0, as a fraction of the height and width of the parent widget.
'''

# --- frame 1 ---
frame1 = tk.Frame(root)
frame1.place(relx=0, rely=0.05, relheight=0.03, relwidth=1, anchor='nw') #we use relheight and relwidth to fill whatever the parent is - in this case- root

label0 = tk.Label(frame1, text="Select the COM port that the device is plugged in: ")
label0.config(font=("TkDefaultFont", 8))
label0.place(relx = 0.1, rely=0.3, relwidth=0.3, relheight=0.5)


cb = ttk.Combobox(frame1, values=serial_ports())
cb.place(relx=0.5, rely=0.5, anchor='center')
# assign function to combobox, that will run when an item is selected from the dropdown
cb.bind('<<ComboboxSelected>>', on_select)
# --- frame 1 ---



# --- frame 2 ---
frame2 = tk.Frame(root, bd=5) #REMOVED THIS bg='#80c1ff' (i used it to see the borders of the frame)
frame2.place(relx=0, rely=0.1, relheight=0.07, relwidth=1, anchor='nw')

#Button for 'Measure All Sensors'
#it will be enabled initially
button_all = tk.Button(frame2, text="Measure all Sensors", bg='#80c1ff', fg='red', state='normal', command=measure_all)  #bg='gray'
button_all.place(relx=0.2, rely=0.5, anchor='center')

#label
label1 = tk.Label(frame2, text="OR, select a single sensor to measure: ")
label1.config(font=("TkDefaultFont", 9))
label1.place(relx = 0.32, rely=0.3, relwidth=0.3, relheight=0.4)

#dropdown
#OPTIONS = [0,1,2,3,4,5,6,7]
OPTIONS = list(range(8)) #[0,1,2,3,4,5,6,7]
clicked = tk.StringVar(master=frame2) # Always pass the `master` keyword argument, in order to run the function when we select from the dropdown
clicked.set(OPTIONS[0]) # default value
clicked.trace("w", dropdown_selection) #When a value from the dropdown is selected, function dropdown_selection() is executed
drop = tk.OptionMenu(frame2, clicked, *OPTIONS)
drop.place(relx = 0.65, rely=0.25, relwidth=0.08, relheight=0.6)

#Button for 'Measure Single Sensor'
#this will be disabled initially, and will be enabled when an item from the dropdown is selected
button_single = tk.Button(frame2, text="Measure this Sensor", bg='#80c1ff', fg='red', state='disabled', command=measure_single) #bg='gray'
button_single.place(relx = 0.85, rely=0.5, anchor='center')
# --- frame 2 ---


# --- frame 3 ---
frame3 = tk.Frame(root, bd=5) #REMOVED THIS bg='#80c1ff' (i used it to see the borders of the frame)
frame3.place(relx=0, rely=0.2, relheight=0.07, relwidth=1, anchor='nw')

#Button for 'STOP Measurement(s)'
#this will be disabled initially, and will be enabled only when a measurement is ongoing
button_stop = tk.Button(frame3, text="STOP measurement(s)", bg='#80c1ff', fg='red', state='disabled', command=stop_measurement)
button_stop.place(relx=0.5, rely=0.5, anchor='center')
# --- frame 3 ---



# --- frame 4 ---
frame4 = tk.Frame(root, bd=5)
frame4.place(relx=0, rely=0.3, relheight=0.09, relwidth=1, anchor='nw')

label2 = tk.Label(frame4, text="Select a sensor to plot data: ")
label2.place(relx = 0.1, rely=0.3, relwidth=0.3, relheight=0.5)

clickedForPlotting = tk.StringVar()
clickedForPlotting.set(OPTIONS[0]) # default value
dropPlot = tk.OptionMenu(frame4, clickedForPlotting, *OPTIONS)
dropPlot.place(relx=0.5, rely=0.5, anchor='center')

#CHANGE LATER
#dropDownButton = tk.Button(frame4, text="Plot sensor data", bg='#80c1ff', fg='red', command=single_Sensor) #bg='gray'
#dropDownButton.place(relx = 0.85, rely=0.5, anchor='center')
# --- frame 4 ---


#frame 5 will be the save to txt file


#frame 6 will be the area with the text field
# --- frame 6 ---
frame6 = tk.Frame(root, bg='#80c1ff') #remove color later
frame6.place(relx=0.0, rely=0.4, relheight=1, relwidth=1, anchor='nw')

text_frame=tk.Frame(frame6)
text_frame.place(relx=0, rely=0, relheight=0.6, relwidth=1, anchor='nw')
text=tk.Text(text_frame)
text.place(relx=0, rely=0, relheight=1, relwidth=1, anchor='nw')
vsb=tk.Scrollbar(text_frame)
vsb.pack(side='right',fill='y')
text.config(yscrollcommand=vsb.set)
vsb.config(command=text.yview)
# --- frame 6 ---




reading_serial=False # 'Stop' flag. True when no measuring is happening

root.mainloop() #here we run our app




#Any code after mainloop() will be halted as long as the window stays alive, so placing the code after mainloop() would execute it after the window is closed.

ser.close()
# --- main ---
By the way, do youi happen to understand why this behaviour is happening on my original question?
Reply


Messages In This Thread
RE: Synchronizing tkinter with serial baud rate - by xbit - Apr-26-2021, 12:50 AM

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020