Search code examples
pythonpython-3.xmultithreadingasynchronouscustomtkinter

Is there any solution to make work downloading process and tkinter gui at same time


I have a method like this, to download some info from an FTP server:

def download_sniffers_data(self, date_start, date_end, sniffers_list, gui):
    ftp = FTP
    is_date_found = False
    ip_address = x
    user_id = x
    psswd = x
    self.gui = gui

    try:
        ftp = FTP(ip_address)
        ftp.login(user=user_id, passwd=psswd)
        dir_names = ftp.nlst()
        print('connecting to server')

        """
        if not os.path.isdir(os.getcwd() + '/output'):
            os.mkdir(os.getcwd() + '/output')
        """
        output_dir_name = self.make_date_dir()

        for s in sniffers_list:
            print(f'searching for sniffer "{s}"')
            self.gui.change_lbl()
            if s in dir_names:
                ftp.cwd('/' + s + '/backup')
                folders_list = ftp.nlst()

                for folder in folders_list:
                    try:
                        date_temp = datetime.strptime(f'{folder[4: 6]}/{folder[2: 4]}/{folder[0:2]}',
                                                      '%d/%m/%y')
                        if date_start.date() <= date_temp.date() <= date_end.date():
                            ftp.cwd('/' + s + '/backup' + '/' + folder)
                            files_list = ftp.nlst()
                            print('searching for files')
                            for file in files_list:
                                try:
                                    file_array = file.split('-')
                                    string1 = f'{file_array[1][-2:]}.{file_array[1][2:4]}.{file_array[1][:2]} {file_array[2][:2]}:{file_array[2][2:]}'
                                    date_file = datetime.strptime(string1, '%d.%m.%y %H:%M')

                                    if date_start <= date_file <= date_end:
                                        is_date_found = True
                                        print(f'downloading file {file}')
                                        path = os.getcwd() + '/dir_output' + '/' + output_dir_name + '/' + 'S' + s.strip(
                                            'sni')
                                        if not os.path.isdir(path):
                                            os.mkdir(path)
                                        if not os.path.isfile(f'{path}/{file}'):
                                            with open(f'{path}/{file}', 'wb') as localfile:
                                                ftp.retrbinary(f'RETR {file}', localfile.write, 1024)

                                except ValueError:
                                    pass
                    except ValueError:
                        pass
                if not is_date_found:
                    self.data_not_found_msgs.append(
                        f'sniffer number: "{s}" no data found between the specified dates')
                is_date_found = False
            else:
                self.data_not_found_msgs.append(f'sniffer number: "{s}" not found in the server')
        gui.change_lbl()

    except ConnectionError:
        print('unable to connect to server')
        exit()
        """
    """"""
    except FileNotFoundError: 
        print('file not found')
        exit()
        """

        """"
    except ftplib.all_errors:
        print('FTP connection error')
        exit()
        """
    finally:
        for msg in self.data_not_found_msgs:
            print(msg)
        ftp.close()

And a simple GUI:

class App(customtkinter.CTk):
    def __init__(self, snidatadl, date_start, date_end, sniffers_list):
        super().__init__()
        self.lbl_download = customtkinter.CTkLabel(master=self, font=("Helvetica", 16), text="")
        self.geometry("600x500")
        self.title("StaMes GUI")
        self.snidatadl = snidatadl
        self.date_start = date_start
        self.date_end = date_end
        self.sniffers_list = sniffers_list
        self.create_widgets()

    def create_widgets(self):

    # DATE FROM
    decotarion1_label = customtkinter.CTkLabel(master=self, text="FROM: ", font=("Helvetica", 16))
    decotarion1_label.place(x=100, y=50)
    date1_label = customtkinter.CTkLabel(master=self, text=self.date_start)
    date1_label.place(x=160, y=50)

    # DATE TO
    decotarion2_label = customtkinter.CTkLabel(master=self, text="TO: ", font=("Helvetica", 16))
    decotarion2_label.place(x=350, y=50)
    date2_label = customtkinter.CTkLabel(master=self, text=self.date_end)
    date2_label.place(x=380, y=50)

    # TABLE SNIFFERS
    scrollable_frame = customtkinter.CTkScrollableFrame(master=self, width=400, height=300)
    scrollable_frame.place(x=80, y=100)

    i = 0
    for x in self.sniffers_list:
        print(x)
        name = customtkinter.CTkLabel(master=scrollable_frame, text=x)
        name.grid(row=i, column=0, padx=20)
        i = i + 1

    # BUTTON
    run_btn = customtkinter.CTkButton(master=self, text="RUN", command=lambda: self.run())
    run_btn.place(x=350, y=420)

    # LABEL
    self.lbl_download.place(x=100, y=420)

def run(self):
    threading.Thread(target= self.snidatadl.download_sniffers_data(self.date_start, self.date_end, self.sniffers_list, self))

def change_lbl(self):
    self.lbl_download.configure(text="DOWNLOADING...")

The idea, is to click on the button "RUN" and call the fuction to download some info, but I need to update the GUI while is running the download.

I have already try the asyncio, and i read about threading, which you think it could be the better idea to fix it? I already try it with a minimal reproducible project, but still not working.

Screenshot


Solution

  • I fixed it with 1 sentence. Adding

    self.gui.update()
    

    after calling the method change_lbl()