ttk progress bar freezing
Question:
I want a progress bar that shows the user the download progress. When updating the GUI and downloading at the same time the progress bar freezes, and I understand why but I don’t know how to solve it. I tried multithreading using this post:
Tkinter: How to use threads to preventing main event loop from “freezing” and using The Basics of Python Multithreading and Queues as a guid to help me fit it to my needs. The problem is that which way I try to achieve my goal, I always seem to make a mistake when changing it to do what I need it to do.
The most basic version of my code (without multithreading):
from Tkinter import *
import ttk
from urllib import URLopener # Downloading files
# Make frame to tell user what file is getting downloaded
self.Progressmsg = Label(self, text="TempValue")
self.Progressmsg.pack(pady=(10,0))
# Make progress bar to show user download progress
self.Progressbar = ttk.Progressbar(self, mode="determinate", orient='horizontal', lengt=280, maximum=len(self.AllClasses))
self.Progressbar.pack(padx=10, pady=10)
self.Progressbar["value"] = 0
def DownloadFile(Class):
# Update progress message
self.Progressmsg["text"] = "Downloading {0}.ics...".format(Class)
# Download each file from saxion website
CalFile = URLopener()
CalFile.retrieve("http://[school website]/ical/group/{0}.ics".format(Class), "Data/{0}.ics".format(Class))
# Update progress bar
self.Progressbar["value"] += 1
for Study in self.Parameters["Classes"]:
for Class in Study:
DownloadFile(Class)
Notes: In this code AllClasses
is a list of different classes from which a calendar file has to be downloaded.
The code itself is part of a fairly large class which I didn’t include. This is why I am using self.[variablename]
When this code runs the progressbar doesn’t load or update, all the files download properly and when they are downloaded the progress bar updates everything at once. My question is: how do I solve this problem in my case?
Answers:
Try this:
# Update progress bar
self.Progressbar["value"] += 1
self.Progressbar.update_idletasks()
If it does not work then use self.Progressbar.update()
instead.
The GUI won’t reflect your changes if there is something else to do (like downloading the next file) unless you call update_idletasks()
or update()
.
Here’s a package you can use which by itself takes care of multithreading issues and also has a control panel to start, stop/resume or terminate the task as needed:
Installation:
pip install progresspanel
Usage:
import tkinter as tk
from progresspanel import Progresspanel
from time import sleep
root = tk.Tk()
panel = Progresspanel(root, title="Sample Task")
panel.pack()
def sample_task():
total = 5
panel.set_total(total)
for i in range(total):
panel.update(i)
print("Running iteration: {}".format(i))
sleep(1)
panel.set_task(sample_task)
root.mainloop()
For your case, you can easily wrap the for loop in a method with proper set_total() and update() as in the example, and pass it to the Progresspanel to start/stop/terminate the task without freezing the GUI.
I want a progress bar that shows the user the download progress. When updating the GUI and downloading at the same time the progress bar freezes, and I understand why but I don’t know how to solve it. I tried multithreading using this post:
Tkinter: How to use threads to preventing main event loop from “freezing” and using The Basics of Python Multithreading and Queues as a guid to help me fit it to my needs. The problem is that which way I try to achieve my goal, I always seem to make a mistake when changing it to do what I need it to do.
The most basic version of my code (without multithreading):
from Tkinter import *
import ttk
from urllib import URLopener # Downloading files
# Make frame to tell user what file is getting downloaded
self.Progressmsg = Label(self, text="TempValue")
self.Progressmsg.pack(pady=(10,0))
# Make progress bar to show user download progress
self.Progressbar = ttk.Progressbar(self, mode="determinate", orient='horizontal', lengt=280, maximum=len(self.AllClasses))
self.Progressbar.pack(padx=10, pady=10)
self.Progressbar["value"] = 0
def DownloadFile(Class):
# Update progress message
self.Progressmsg["text"] = "Downloading {0}.ics...".format(Class)
# Download each file from saxion website
CalFile = URLopener()
CalFile.retrieve("http://[school website]/ical/group/{0}.ics".format(Class), "Data/{0}.ics".format(Class))
# Update progress bar
self.Progressbar["value"] += 1
for Study in self.Parameters["Classes"]:
for Class in Study:
DownloadFile(Class)
Notes: In this code AllClasses
is a list of different classes from which a calendar file has to be downloaded.
The code itself is part of a fairly large class which I didn’t include. This is why I am using self.[variablename]
When this code runs the progressbar doesn’t load or update, all the files download properly and when they are downloaded the progress bar updates everything at once. My question is: how do I solve this problem in my case?
Try this:
# Update progress bar
self.Progressbar["value"] += 1
self.Progressbar.update_idletasks()
If it does not work then use self.Progressbar.update()
instead.
The GUI won’t reflect your changes if there is something else to do (like downloading the next file) unless you call update_idletasks()
or update()
.
Here’s a package you can use which by itself takes care of multithreading issues and also has a control panel to start, stop/resume or terminate the task as needed:
Installation:
pip install progresspanel
Usage:
import tkinter as tk
from progresspanel import Progresspanel
from time import sleep
root = tk.Tk()
panel = Progresspanel(root, title="Sample Task")
panel.pack()
def sample_task():
total = 5
panel.set_total(total)
for i in range(total):
panel.update(i)
print("Running iteration: {}".format(i))
sleep(1)
panel.set_task(sample_task)
root.mainloop()
For your case, you can easily wrap the for loop in a method with proper set_total() and update() as in the example, and pass it to the Progresspanel to start/stop/terminate the task without freezing the GUI.