I've been developing a tkinter application for downloading a file from the internet and showing the progress of it on a progress bar (Progressbar).
Since the download process was different from the GUI, (and since tkinter runs the GUI the main thread on an infinite loop), I figured to make a different thread for the download process to carry on, and it would update the progress bar in the background
I would then go on to wait for the background process to end, and call the root.destroy() method to end the application, but the application doesn't close, on closing the application manually - it would return the following error
_tkinter.TclError: can't invoke "destroy" command: application has been destroyed
Here is the code for the
class download:
#Initializing the root
def __init__(self):
self.root = Tk()
#creating the progress bar and packing it to the root
def create_progress_bar(self):
self.progress_bar = Progressbar(self.root, orient = HORIZONTAL, length = 200, mode = 'determinate')
self.progress_bar.pack(pady=10)
#function to start the mainloop
def start_application(self):
self.root.mainloop()
#updating the progress bar from the background thread
def update_progress(self, count, blocksize, totalsize):
update = int(count * blocksize * 100 / totalsize)
self.progress_bar['value'] = update
self.root.update_idletasks()
#downloading the file from the background thread
def download_file(self, download_url):
#using the reporthook to get information about download progress
urllib.request.urlretrieve(download_url, 'a.jpg', reporthook=self.update_progress)
#closing the root function
def close(self):
self.root.destroy()
#handling the download
def handle_download(self, download_url):
#creating a different thread
self.process = Thread(target=self.download_file, args=(download_url,))
#creating the progress bar
self.create_progress_bar()
#starting the background thread
self.process.start()
#starting the GUI
self.start_application()
#waiting for the background thread to end
self.process.join()
#closing the application
self.close()
ob = download()
ob.handle_download('link')
For what it's worth, I did try to make things happen on the same thread, but the application could not respond to the updates. Any insight on the matter would be extremely welcome.
CodePudding user response:
Since your program does self.root.destroy()
only after self.root.mainloop()
has returned, it does not reach the point of destruction unless you're closing the application manually, and then it's pointless to call .destroy()
.
You could check from time to time whether the download thread is running, and destroy the window when not.
def watch(self):
if self.process.is_alive(): self.root.after(500, self.watch)
else: self.close()
#handling the download
def handle_download(self, download_url):
…
#starting the background thread
self.process.start()
self.watch()
…
#closing the application
#self.close() is too late here