Package: threadom
Jupitestuses Threadom to perform smooth real-time test reportsThreadomThreadomis aPythonlibrary to perform Tkinter-compatible multithreading. It's part of thePyrustic Open Ecosystem.Installation|Reference|WebsiteOverviewIt is well known how difficult it is to implementmultithreadingin aTkinterapplication.Let's simulate a background task that lasts 3 seconds in a Tkinter app.importtkinterastkimporttimefromthreadomimportThreadomdeftask():"""This task lasts 3 seconds"""time.sleep(3)print("Task completed")defon_start(root):"""Function called when the button is clicked"""threadom=Threadom(root)threadom.run(task)# rootroot=tk.Tk()# labellabel=tk.Label(root,text="Multithreading Demo")label.pack()# buttoncommand=lambdaroot=root:on_start(root)button=tk.Button(root,text="Start",command=command)button.pack()# mainloop !root.mainloop()You can click the buttonStart5 times quickly:the user interface doesn't freeze at all;you don't even notice that tasks are running in background;you have the proof that tasks have been run when you read the output "Task completed".Now, let's send some data to the task function. This time, we care about the output of the task function and also we want it to raise an exception when the input data is an odd number.importtkinterastkimporttimeimportrandomfromthreadomimportThreadomdeftask(data):"""This task lasts 3 seconds and returns data*2 if all right.It raises an exception when data is an odd number."""time.sleep(3)if(data%2)!=0:# oops, odd number !raiseExceptionreturndata*2defon_start(root):"""Function called when the button is clicked"""# Random integerrandom_int=random.choice(range(10))# Threadom instancethreadom=Threadom(root)# Consumer callback, called when the task returnsconsumer=lambdaresult:print("Task result: ",result)# Upstream exception handler, called when an exception is raised while running the taskexception_handler=lambdaerror:print("Exception caught")# Run the taskthreadom.run(task,target_args=(random_int,),consumer=consumer,upstream_exception_handler=exception_handler)# Note: as you can guess, if the upstream_exception_handler parameter exists,# a downstream_exception_handler parameter should exists too.# The downstream_exception_handler is called when an exception is raised# while running the consumer handler.# rootroot=tk.Tk()# labellabel=tk.Label(root,text="Multithreading Demo")label.pack()# buttoncommand=lambdaroot=root:on_start(root)button=tk.Button(root,text="Start",command=command)button.pack()# mainloop !root.mainloop()This seems nice, but what if we have a long-running task that produces some data that should be immediately consumed by the GUI ? You know, a task we can't wait it returns.importtkinterastkimporttimefromthreadomimportThreadom,QueueTaildeftask(queue):"""This task puts a number in the queue every second."""forxinrange(10):time.sleep(1)queue.put(x)# use QueueTail to indicate the tail of the queuequeue.put(QueueTail)defdisplay_data(data,strvar):ifdataisQueueTail:data="Task completed"strvar.set(data)defon_start(root,strvar):"""Function called when the button is clicked"""# Threadom instancethreadom=Threadom(root)# Consumer callback, called when the task returnsconsumer=lambdadata:print("Task completed")# Get a queuequeue=threadom.q()# Run the taskthreadom.run(task,target_args=(queue,),consumer=consumer)# Consume the queueconsumer=lambdadata,strvar=strvar:display_data(data,strvar)threadom.consume(queue,consumer=consumer)# rootroot=tk.Tk()# labelstrvar=tk.StringVar(value="Multithreading Demo")label=tk.Label(root,textvariable=strvar)label.pack()# buttoncommand=lambdaroot=root,strvar=strvar:on_start(root,strvar)button=tk.Button(root,text="Start",command=command)button.pack()# mainloop !root.mainloop()You can pause the queue consuming process whenever you want, resume it, or stop it. TheThreadom.consumemethod returns aqid(queue ID). So you can dothreadom.pause(qid). Read thereferenceto get more details. I recommend you to use the classthreadom.QueueTailto indicate the tail of a queue.This library is part of thePyrustic Open Ecosystem. This is a work in progress. If you like it, adopt it, spread the words ;)InstallationPyrustic FrameworkandDresscodecome withThreadom, so you don't need to worry about the individual installation ofThreadomif you use one of these frameworks.First timeInstall for the first time:$pipinstallthreadomUpgradeTo upgradeThreadom:$pipinstallthreadom--upgrade--upgrade-strategyeager