Search code examples
pythonpython-3.xmultithreadingprogress-barcustomtkinter

can't update label in realtime in customtkinter even with the thread?


I want to update self.error_label in realtime but I do not know what's going wrong it is not updating in realtime, Even though I have added Thread, seems like it is not working. I see Download 100% when it is fully downloaded, not in between like 20%, 48% and so on.

import customtkinter
from pytube import YouTube
import re
from threading import Thread


class App(customtkinter.CTk):
    def __init__(self):
        super().__init__()
        self._set_appearance_mode("System")

        self.geometry("720x480")
        self.title('PyTube')
        
        # Progress bar
        self.progressbar = customtkinter.CTkProgressBar(self, width=500)
        self.progressbar.set(0)
        self.progressbar.pack()

        #Error label
        self.error_label = customtkinter.CTkLabel(self, text='Welcome!')    #<----------------
        self.error_label.pack()

        #User input box
        ................

      
    
    def progress_callback(self, stream, chunk, bytes_remaining):
        total_size = stream.filesize
        bytes_downloaded = total_size - bytes_remaining
        percentage = bytes_downloaded / total_size * 100
        self.error_label.configure(text=f'Download {percentage}%')
    
        
    def youtube_video_download(self, link, res):
        yt = YouTube(link, on_progress_callback=self.progress_callback)
        vid = yt.streams.filter(progressive=True, resolution=res).first()
        vid.download()

        
    def Download(self):
        if self.link_validate(self.user_input.get()):
            if self.tabview.get() == 'Video':
                if self.video_quality.get() != '':
                    Thread(target=self.youtube_video_download, args=(self.user_input.get(), self.video_quality.get(), )).start()
                else:
                    self.error('Video quality is not selected!', 'red')
            


if __name__=="__main__":
    app = App()
    app.mainloop()

Solution

  • I've updated your code slightly so that it works when copy pasting:

    import customtkinter
    import pytube.request
    from pytube import YouTube
    
    
    class App(customtkinter.CTk):
        def __init__(self):
            super().__init__()
            self._set_appearance_mode("System")
    
            # change chunk size to 25 kilobytes to have working progressbar even on small files
            pytube.request.default_range_size = 25600
    
            self.geometry("720x480")
            self.title('PyTube')
    
            # Progress bar
            self.progressbar = customtkinter.CTkProgressBar(self, width=500)
            self.progressbar.set(0)
            self.progressbar.pack()
    
            # Error label
            self.error_label = customtkinter.CTkLabel(
                self, text='Welcome!')  # <----------------
            self.error_label.pack()
    
            self.button = customtkinter.CTkButton(self, command=self.Download)
            self.button.pack()
    
        def progress_callback(self, stream, chunk, bytes_remaining):
            total_size = stream.filesize
            bytes_downloaded = total_size - bytes_remaining
            percentage = bytes_downloaded / total_size * 100
            self.error_label.configure(text=f'Download {percentage}%')
            self.error_label.update()
    
        def youtube_video_download(self, link):
            yt = YouTube(link, on_progress_callback=self.progress_callback)
            vid = yt.streams.filter(progressive=True).first()
            vid.download()
    
        def Download(self):
            self.youtube_video_download(
                "https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    
    
    if __name__ == "__main__":
        app = App()
        app.mainloop()
    


    The changes that are important are these:

    • You need to update the error label to display the new value from within the progress_callback so the value gets updated while downloading and not after it's done so add self.error_label.update() to the bottom of the progress_callback

    • change the chunk size of pytube to something smaller as the default chunk size can be too big for a smooth progress bar and sometimes you wont even really see the progress if you dont decrease it (optional but suggested if you want a smooth animation of the progressbar filling):

    import pytube.request
    pytube.request.default_range_size = 25600