tkinter loop and serial write

i have written a gui using tkinter and i need to send the values of the 2 scales in realtime to an arduino. i have verified that the arduino is working using another sketch which sends values to the arduino and these are received, i have added in the following code to my python code

while True:
    #command = raw_input("Enter level ")
    #if command == '1' :
    ser.write("c"+str (c1v.get()))
    ser.write(":d"+str (c2v.get()))

i have moved this inside and outside of the tkinter mainloop and get varied results from the gui not loading to the data only sending once the gui is closed. can someone tell me how to get the gui to run and as i move a scale the value is sent to the arduino in real time over serial.

heres the code :

from Tkinter import *
import serial

ser = serial.Serial('/dev/ttyAMA0', 9600)



master= Tk()
master.geometry('500x500+0+0')

def print_value(val):
    print ("c1="+str (c1v.get()))
    print ("c2="+str(c2v.get()))


c1v=DoubleVar()
c2v=DoubleVar()

c1 = Scale(master, from_=255, to=0, length =400,width =100, troughcolor = 'blue',command=print_value, variable =c1v)
c1.grid(row=1,column=1)
c2 = Scale(master, from_=255, to=0, length =400,width =100, troughcolor = 'blue',command=print_value, variable =c2v)
c2.grid(row=1,column=2)


def load_p1():
    pass
    lp1 = open("/home/pi/Desktop/IEP/test/preset_test.txt")
    val1, val2 = (x.split("=")[1] for x in lp1)
    c1.set(val1)
    c2.set(val2)
    lp1.close()

#
def record():

    save_path = '/home/pi/Desktop/IEP/test'
    name_of_file = ("preset_test")
    completeName = os.path.join(save_path, name_of_file+".txt")
    file1 = open(completeName , "w")
    toFile = ("c1="+str (c1.get())+ "\n""c2="+str(c2.get()))
    file1.write(toFile)
    file1.close()



rec=Button(master, text="Record",width=20, height=10, bg='Red', command=record)
rec.grid(row=2, column=1)

load=Button(master, text="Load",width=20, height=10, bg='gold',command=load_p1)
load.grid(row=2, column=2)


master.mainloop()

while True:
    #command = raw_input("Enter level ")
    #if command == '1' :
    ser.write("c"+str (c1v.get()))
    ser.write(":d"+str (c2v.get()))

Context

You use the Tkinter mainloop and a while-loop and now you want to put both together into one program.

while X:
    do_y()

and

master.mainloop()

Solutions

There are several solutions for you.

  1. split the loop and use after to let the GUI call you back:

    def do_y2():
        do_y()
        if X:
            master.after(123, do_y2) # after 123 milli seconds this is called again
    do_y2()
    master.mainloop()
    

    For a more detailed answer see this answer by Bryan Oakley

  2. use guiLoop by me.

    from guiLoop import guiLoop # https://gist.github.com/niccokunzmann/8673951#file-guiloop-py
    @guiLoop
    def do_y2():
        while X:
            do_y()
            yield 0.123 # give the GUI 123 milli seconds to do everything
    do_y2(master)
    master.mainloop()
    

    guiLoop uses the approach from 1. but allows you to use one or more while loops.

  3. use update to refresh the GUI.

    while X:
        do_y()
        master.update()
    

    This approach is an unusual one since it replaces the mainloop that is part os the most GUI frameworks like Tkinter. Note that with 1 and 2 you can have multiple loops, not just one as in 3.

  4. use a new thread of execution that executes your loop in parallel. ! This thread must not access master or any GUI elements directly because Tkinter can crash then!

    import threading
    def do_y_loop():
         while X:
             do_y()
    thread = threading.Thread(target = do_y_loop)
    thread.deamon = True # use this if your application does not close.
    thread.start()
    master.mainloop()
    
  5. start the mainloop in a new thread. As in 4. Tkinter can crash if you access the GUI from the thread.

    import threading
    thread = threading.Thread(target = master.mainloop)
    thread.deamon = True # use this if your application does not close.
    thread.start()
    while X:
        do_y()
    

    In both 4. and 5. communication between the GUI and the while-loop could/should go through global variables but never through tkinter methods.


For PyQT see these questions:

  • How to quit the program in while loop using push-button in PyQt
  • How to set push-button to keyboard interrupt in PyQt