0

I've looked around stackoverflow and am pretty sure this isn't a duplicate. I need to poll a queue every 1ms (or as quickly as possible), and this has to run in the same thread as my tkinter window otherwise I can't update my labels from the queue data. (someone correct me if i'm wrong here). Currently my code looks like this:

def newData():
    global gotNewData
    if q.get == 1: #if there is new data
        updateVariables() #call the function to update my labels with said data
        q.queue.clear() #clear the queue
        gotNewData = 0 #no new data to get
        q.put(gotNewData)
        MainPage.after(1, newData)
    else:
        MainPage.after(1, newData)

however when I run this code, my tkinter window freezes instantly. I commented out the line which calls the other function and it still freezes so i'm pretty sure it's this function which is causing the problem. Any help is greatly appreciated.

Nadim
  • 77
  • 1
  • 2
  • 9
  • Why do they need to run in the same thread though? – WiseDev Aug 08 '19 at 13:51
  • 1
    where do you add items in queue? The code looks like an infinite loop – FrainBr33z3 Aug 08 '19 at 13:52
  • Do note that `after` method takes a delay in milliseconds. You are scheduling 1000 events every second in your current setup. – Henry Yik Aug 08 '19 at 13:57
  • Are you sure it is `q.get` and not `q.get()`? Also no that is not correct that it has to run in the same thread. – Mike - SMT Aug 08 '19 at 14:03
  • @Mike - SMT my understanding of it was that tkinter was not threadsafe and therefore labels couldn't be updated by a function running in a different thread... – Nadim Aug 08 '19 at 14:06
  • No I believe you can just fine. I have been able to return data from threads. Tkinter itself I believe must be in the main thread though. – Mike - SMT Aug 08 '19 at 14:07
  • @FrainBr33z3 the data is added to the queue in a different function. I didn't include it because it seemed to work fine... – Nadim Aug 08 '19 at 14:07
  • @Mike-SMT 'To be more specific to your situation: you should avoid using threads with tkinter. You can use them, but you can't access widgets from these other threads.' from https://stackoverflow.com/questions/17847869/python-tkinter-label-redrawing-every-10-seconds – Nadim Aug 08 '19 at 14:10
  • 2
    Is there a reason you are checking every millisecond? Have you tried changing that to checking every 100 ms, or even 10ms? – Bryan Oakley Aug 08 '19 at 14:21
  • @BryanOakley I tried that and the window appeared for about a second and then froze again... I'm getting data every 13ms or so, sometimes faster so ideally it needs to keep up. – Nadim Aug 08 '19 at 14:58
  • If `q` is instance of `queue.Queue`, then `if q.get == 1:` should be changed to `if q.get(False) == 1:`. – acw1668 Aug 09 '19 at 01:22

1 Answers1

1

So what I would do if you must have threading is to use a StringVar() in the threaded function instead of having to work with a widget directly.

I feel like 1000 times a second is excessive. Maybe do 10 times a sec instead.

Take a look at this example and let me know if you have any questions.

import tkinter as tk
import threading
root = tk.Tk()

lbl = tk.Label(root, text="UPDATE ME")
lbl.pack()

q_value = tk.StringVar()

q = tk.Entry(root, textvariable=q_value)
q.pack()


def updateVariables(q):
    lbl.config(text=q)


def newData(q):
    if q.get() != '':
        updateVariables(q.get())
        root.after(100, lambda: newData(q))

    else:
        root.after(100, lambda: newData(q))
        print("not anything")


thread = threading.Thread(target=newData, args=(q_value, ))
thread.start()

root.mainloop()
Mike - SMT
  • 14,784
  • 4
  • 35
  • 79
  • unfortunately the data i'm receiving comes around every 10ms depending on how many packets are sent so no dice there... Am I wrong in thinking that calling updateVariables() from newData will start in the same thread? Because then surely I'm back to the problem that I shouldn't interact with tkinter widgets from another thread.... – Nadim Aug 08 '19 at 15:09
  • You are not updated a widget from a seperate thread. You are sending info to a function that is in the same thread as the widget and that function is updating the widget. Again you can use a varObject like StringVar for the label as well so you are not editing the widget but a StringVar. I see no problem with this and also if you are only receiving data ever 10ms then set your after to 10ms. There is no practical reason to run more operations then you need. – Mike - SMT Aug 08 '19 at 15:12
  • sorry for not understanding, I tried it and this works great. – Nadim Aug 09 '19 at 12:01