Home > Software engineering >  Tkinter application does not close on root.destroy()
Tkinter application does not close on root.destroy()

Time:11-01

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
  • Related