Summary: In Python when I update a Tkinter label text from a spawned process, the label on the GUI is not updated, although the spawned process is executed. How can I make it update from the spawned process?
Im am working with Python 2.7.2 in Lubuntu 20.04
EDIT: I also tried with Python 3.8, had to install python3-tk extra, change a little syntax (parentheses after print command and replacing Tkinter with tkinter), but problem still there looking identical. END EDIT
Here is my sample code working standalone to try out:
from Tkinter import *
from multiprocessing import Process
# simple Label change invoked from "simple" button
def bnAction_sync():
print "bnAction_sync"
changeLabel()
print "bnAction_sync done"
# asynchronous label change invoked from Async button
def bnAction_async():
print "bnAction_async"
p = Process(target=changeLabel)
p.start()
print "bnAction_Async done"
def changeLabel():
print "change label"
lbl['text'] = "Text changed"
### Apr 19 2021:
### uncommenting the following line really updates label but makes program crash ###
# root.update_idletasks
print "change label done"
root = Tk()
btnSync = Button(root, text="simple", command=bnAction_sync)
btnSync.pack()
btnAsync = Button(root, text="async", command=bnAction_async)
btnAsync.pack()
lbl = Label(root, text="Initial text")
lbl.pack()
root.mainloop()
If I press the "simple" button, text is updated in label. All good.
But: If I press "async" button,
The reason I want to do it this way: Because I am starting a long running spawned process, after which I want to update the label. All other processes however should run in parallel. So I created a function f, containing the long running function and the label update function sequentially. I want to call f asynchronously. So in principle:
def longprocess():
code...
def updatelabel_after_longprocess():
code...
def f():
longprocess()
updatelabel_after_longprocess()
p = Process(target=f)
p.start()
do other things immediately
Somewhere I read, that refresh is suspended, while a script is still running. I tried some p.join inserts with no luck.
Please help, thanks!
It's unlikely that you'll be able to make updating your label from another process work. It might be possible, but it will be very complicated. What I'd suggest instead is that you make a thread that launches the expensive code in another process, and then waits to update the GUI until after the process is done:
from multiprocessing import Process
from threading import Thread
def longprocess():
# do whatever you need here, it can take a long time
def updatelabel_after_longprocess():
# do whatever GUI stuff you need here, this will be run in the main process
def thread_helper():
process = Process(target=longprocess)
process.start()
process.join() # this waits for the process to end
updatelabel_after_longprocess()
if __name__ == "__main__":
t = Thread(target=thread_helper)
t.start()
# do whatever else you have to do in parallel here
t.join()