Python - Tkinter (ttk) dynamically create a progressbar with its own value that changes

3.5k Views Asked by At

So basicly I'm trying to make multiple progressbar and each progressbars value runs from 0 to 100. But right now my functions acess the values from the others too and that looks weird. Hopefully someone can help me. Thanks :)

# -*- coding: utf-8 -*-
import tkinter.ttk as ttk
import tkinter as tk
from threading import Thread
import time

class Main(object):
    def __init__(self, master):
        self.master = master

        self.frame = tk.Frame(self.master, width=400, height=400)
        self.frame.pack(expand=True)

        self.button = tk.Button(self.frame, text="Add Bar", command=self.start_thread)
        self.button.pack(fill="y")

    def start_thread(self):
        self.t = Thread(target=self.add_bar)
        self.t.start()

    def add_bar(self):
        self.var = tk.IntVar()
        self.var.set(0)

        self.progessbar = ttk.Progressbar(self.frame, variable=self.var, orient=tk.HORIZONTAL, length=200)
        self.progessbar.pack()

        self.add_values(self.var)

    def add_values(self, var):
        self.variable = var
        for self.x in range(100):
            time.sleep(0.1)
            self.variable.set(self.x)


root = tk.Tk()
app = Main(root)
root.mainloop()
1

There are 1 best solutions below

0
On BEST ANSWER

In your original code, self.var is the only variable storing the progress of the loading bar. Since there is only one instance of the Main object, there is only one instance of self.var which is resulting in the problem you described. In order to fix this, I recommend creating a separate class for the loading bar as shown below:

# -*- coding: utf-8 -*-
import tkinter.ttk as ttk
import tkinter as tk
from threading import Thread
import time

class Main(object):
    def __init__(self, master):
        self.master = master

        self.frame = tk.Frame(self.master, width=400, height=400)
        self.frame.pack(expand=True)

        self.button = tk.Button(self.frame, text="Add Bar", command=lambda:self.createBar())
        self.button.pack(fill="y")

    def createBar(self):
        self.t = Thread(target=self.create)
        self.t.start()

    def create(self):
        newBar = LoadingBar(self.master, self.frame)

class LoadingBar(object):
    def __init__(self, master, frame):
        # Must use same Tkinter frame to add loading bars into
        self.master = master
        self.frame = frame
        self.add_bar()

    def start_thread(self):
        self.t = Thread(target=self.add_bar)
        self.t.start()

    def add_bar(self):
        self.var = tk.IntVar()
        self.var.set(0)

        self.progessbar = ttk.Progressbar(self.frame, variable=self.var, orient=tk.HORIZONTAL, length=200)
        self.progessbar.pack()

        self.add_values(self.var)

    def add_values(self, var):
        self.variable = var
        for self.x in range(100):
            time.sleep(0.1)
            self.variable.set(self.x)

root = tk.Tk()
app = Main(root)
root.mainloop()

Creating each instance of the loading bar in a separate thread allows for the desired effect.

Here is another approach (no new class):

# -*- coding: utf-8 -*-
import tkinter.ttk as ttk
import tkinter as tk
from threading import Thread
import time

class Main(object):
    def __init__(self, master):
        self.master = master

        self.frame = tk.Frame(self.master, width=400, height=400)
        self.frame.pack(expand=True)

        self.button = tk.Button(self.frame, text="Add Bar", command=self.start_thread)
        self.button.pack(fill="y")

    def start_thread(self):
        self.t = Thread(target=self.add_bar)
        self.t.start()

    def add_bar(self):
        var = tk.IntVar()
        var.set(0)

        progessbar = ttk.Progressbar(self.frame, variable=var, orient=tk.HORIZONTAL, length=200)
        progessbar.pack()

        self.add_values(var)

    def add_values(self, var):
        variable = var
        for x in range(100):
            time.sleep(0.1)
            variable.set(x)


root = tk.Tk()
app = Main(root)
root.mainloop()

Note: local variables are created for each thread as opposed to instance variables to avoid the overwriting of instance variables.