Home > Mobile >  Threading and Tkinter - How to use the threading module with my simple example?
Threading and Tkinter - How to use the threading module with my simple example?

Time:05-07

I don't understand how to use the threading module properly. In this example I have two tkinter widgets, a button and a progress bar. The progress bar (configured in indeterminate mode) has to be active when the user pushes the button, and when the task is completed, the progress bar has to be stopped.

import tkinter as tk
from tkinter import ttk
import threading, ipaddress

class MainWindow:
    def __init__(self):
        self.parent=tk.Tk()
        self.parent.geometry("786x524 370 100")
        self.parent.title("Test")
        self.parent.configure(background="#f0f0f0")
        self.parent.minsize(786, 524)

        self.ProBar=ttk.Progressbar(self.parent, mode="indeterminate")
        self.ProBar.pack(padx=(40, 40), pady=(40, 40), fill=tk.BOTH)

        self.StartButton=ttk.Button(self.parent, text="Start", command=self.MyHeavyTask)
        self.StartButton.pack(padx=(40, 40), pady=(40, 40), fill=tk.BOTH)

        self.parent.mainloop()

    # my start function:
    def Start(self):
        self.ProBar.start(4)
        self.MyHeavyTask()
        self.ProBar.stop()

    # my real start function. it's just an example, it needs time to be completed:
    def MyHeavyTask(self):
        ls=[]
        obj=ipaddress.ip_network("10.0.0.0/8")
        for obj in list(obj.hosts()):
            print(obj.exploded)

# start my test:
if __name__=="__main__":
    app=MainWindow()

This code has an issue, it can't run the function "MyHeavyTask" and at the same time keep active the progress bar widget. to solve it, I tried to put "MyHeavyTask" in an indipendent thread changing the line 17 with this one:

self.StartButton=ttk.Button(self.parent, text="Start",
                            command=threading.Thread(target=self.MyHeavyTask).start())

unfortunately this solution doesn't work. when I press the button, nothig happens…why? What is the right way to use the threading module in my example?

CodePudding user response:

You can add a method to the class

def Get_Input(self):
    message = input(">")
    if message:
        send_message(message)

and add in init class

threading.Thread(target=self.Get_Input, args=(,)).start()

Please note : If you passing one argument, you need to use

threading.Thread(target=self.Get_Input, args=(var1,)).start()

Unlike common sense :)

CodePudding user response:

Here a runnable example similar to the code in your question, that shows a way to run a background task and keep a ttk.Progressbar active simultaneously. It does this by using the universal after() widget method to repeatedly schedule calls to a method that checks whether the background task is running and updates the progress bar if it is. It also disables and re-enables the Start button appropriately so the task can't be start again while it's running.

Note I strongly suggest you read and start following the PEP 8 - Style Guide for Python Code.

from random import randint
import tkinter as tk
from tkinter import ttk
import threading
from time import sleep


class MainWindow:
    def __init__(self):
        self.parent = tk.Tk()
        self.parent.geometry("786x524 370 100")
        self.parent.title("Test")
        self.parent.configure(background="#f0f0f0")
        self.parent.minsize(786, 524)

        self.task = threading.Thread(target=self.my_heavy_task)

        self.pro_bar = ttk.Progressbar(self.parent, mode="indeterminate")
        self.pro_bar.pack(padx=(40, 40), pady=(40, 40), fill=tk.BOTH)

        self.start_btn = ttk.Button(self.parent, text="Start", command=self.start)
        self.start_btn.pack(padx=(40, 40), pady=(40, 40), fill=tk.BOTH)

        self.parent.mainloop()

    def check_thread(self):
        if self.task.is_alive():
            self.pro_bar.step()  # Update progressbar.
            self.parent.after(20, self.check_thread)  # Call again after delay.
        else:
            self.pro_bar.stop()
            self.start_btn.config(state=tk.ACTIVE)

    def start(self):
        """Start heavy background task."""
        self.start_btn.config(state=tk.DISABLED)
        self.task.start()
        self.pro_bar.start()
        self.check_thread()  # Start checking thread.

    def my_heavy_task(self):
        """Slow background task."""
        for obj in (randint(0, 99) for _ in range(6)):
            print(obj)
            sleep(.5)


if __name__=="__main__":
    app = MainWindow()

  • Related