1

I was trying to make a function run periodically. The purpose was to print serial data on a tkinter frame.

Initially this worked, using threads.

def readSerial():
    global val1
    ser_bytes = ser.readline()
    ser_bytes = ser_bytes.decode("utf-8")
    val1 = ser_bytes
    scrollbar.insert("end", val1)
    scrollbar.see("end") #autoscroll to the end of the scrollbar


t1 = continuous_threading.PeriodicThread(0.1, readSerial)

frame2 = tk.Frame(root, bg='#80c1ff') #remove color later
frame2.place(relx=0, rely=0.1, relheight=1, relwidth=1, anchor='nw')
scrollbar = scrolledtext.ScrolledText(frame2)
scrollbar.place(relx=0, rely=0, relheight=0.9, relwidth=1, anchor='nw')

t1.start()
root.mainloop()

However, i was experiencing error when i was closing my application. You can read more about this here: Closing my tkinter serial app, gives me an exception

So user AST suggested, i should use the after() function.

So i tried this:

I kept the function readSerial() exactly the same. I removed all the lines that involved threads (t1).

And finally this:

root.after(100, readSerial)
root.mainloop()

But this doesn't work as expected.

In my tkinter frame, only the first line of the serial is printed, then nothing else.

How can i make this work with after()? What is the proper way?

user1584421
  • 3,499
  • 11
  • 46
  • 86
  • Where is the code that uses `root.after()`, I think you forgot to use `root.after()` inside the function too, without that, the function will just be called once. Putting `root.after()` inside will basically create a interval loop. – Delrius Euphoria Feb 28 '21 at 19:31
  • @CoolCloud Thanks. I used the first example, on the selected answer, of this question. https://stackoverflow.com/questions/44085554/how-to-use-the-after-method-to-make-a-callback-run-periodically/44085555#44085555 . And he didn't use after() inside the function. – user1584421 Feb 28 '21 at 19:33
  • Notice in the first example, the function will just run once and stop. If you want it to run periodically, then use `root.after()` inside too. Though there is no guarantee for exact accuracies in periodicity, it might have slight delays. – Delrius Euphoria Feb 28 '21 at 19:34
  • Do you want to make an answer with the way to insert after() in the function, so i can see how it is meant to written, and you will get the points? – user1584421 Feb 28 '21 at 19:36
  • I am not familiar with reading serial. – Delrius Euphoria Feb 28 '21 at 19:38
  • I just write root.after(100, readSerial), as a last line inside readSerial()? – user1584421 Feb 28 '21 at 19:39

1 Answers1

3

You have to use after() inside the function so as to call it periodically, like:

def readSerial():
    global val1
    ser_bytes = ser.readline()
    ser_bytes = ser_bytes.decode("utf-8")
    val1 = ser_bytes
    scrollbar.insert("end", val1)
    scrollbar.see("end") #autoscroll to the end of the scrollbar
    root.after(100,readSerial) # 100 ms is 0.1 second, you can change that

.... # Same code but remove the t1

readSerial()
root.mainloop()

This will keep on repeating the function roughly every 100 milliseconds, there is no guarantee to call the function exactly at 100 millisecond, but it wont be called before 100 millisecond.

Delrius Euphoria
  • 14,910
  • 3
  • 15
  • 46
  • Sorry but it doesn't work. It displays two line of the serial data, then lags, then it displays a lot of lines all at once. – user1584421 Feb 28 '21 at 19:44
  • @user1584421 This is the answer to your question, _Tkinter: make function run periodically with after()_. The issue might be, its happening fast? Try increasing the time – Delrius Euphoria Feb 28 '21 at 19:51
  • You are correct, i used 500 instead of 100 and it works. But it is too slow. I am using 9600 baud rate. Do you happen to know the correct time value for my case? – user1584421 Feb 28 '21 at 20:08
  • @user1584421 Nope no idea, maybe ask a new question, it seems unrelated to this question. – Delrius Euphoria Feb 28 '21 at 20:09
  • How about scheduling the call with `after` at the end rather than starting, that way the function will get executed completely before scheduling a new call. – astqx Mar 01 '21 at 05:28
  • @AST I havent thought about it, I dont know when `after()` will reschedule the new task, at the end or at the point of execution of `after()`, anyway I think its worth a try. – Delrius Euphoria Mar 01 '21 at 05:44
  • @user1584421 Why dont you try the updated the answer now. – Delrius Euphoria Mar 01 '21 at 05:45
  • I can confirm this works now. Thank you Cool Cloud and AST! – user1584421 Mar 01 '21 at 21:18