pythontkinterbackuputility

Browse folder and file not working although it is suppose to work


Im trying to make a backup utility tool in python where the user can backup either folder or file but not both. The folder can backup folders but for the file, it does not backup the file. It will only produce a log file which state the starting time of backup process and the end time of backup process

import tkinter as tk
import tkinter.ttk as ttk
from tkinter import filedialog, messagebox
import shutil
import os
import time
import zipfile



def backup():
    source_folders = source_folder_text.get('1.0', 'end').strip().split('\n')

    dest_folder = dest_folder_text.get('1.0', 'end').strip()

    if not source_folders:
        messagebox.showerror('Error', 'Please specify at least one source folder.')
        return
    if not dest_folder:
        messagebox.showerror('Error', 'Please specify a destination folder.')
        return

    is_file_selected = False
    is_folder_selected = False

    for source_folder in source_folders:
        if os.path.isfile(source_folder):
            is_file_selected = True
        elif os.path.isdir(source_folder):
            is_folder_selected = True

    if is_file_selected and is_folder_selected:
        messagebox.showerror('Error', 'Please select either files or folders for backup, not both.')
        return

    try:
        if not os.path.exists(dest_folder):
            os.makedirs(dest_folder)


        timestamp = time.strftime('%Y%m%d-%H%M%S')
        backup_folder = os.path.join(dest_folder, f'backup-{timestamp}')

        if not os.path.exists(backup_folder):
            os.makedirs(backup_folder)

        log_file = os.path.join(backup_folder, 'backup.log')

        with open(log_file, 'w') as f:
            f.write(f'Backup started at {time.strftime("%Y-%m-%d %H:%M:%S")}\n')

        total_files = 0
        for source_folder in source_folders:
            for root, dirs, files in os.walk(source_folder):
                total_files += len(files)

        progress_window = tk.Toplevel()
        progress_window.title('Backup Progress')

        progress_label = tk.Label(progress_window, text='Backing up files...')
        progress_label.pack()

        progress_bar = tk.ttk.Progressbar(progress_window, orient='horizontal', length=300, mode='determinate', maximum=total_files)
        progress_bar.pack()

        current_file = 0
        for source_folder in source_folders:
            for root, dirs, files in os.walk(source_folder):
                for file in files:
                    source_file = os.path.join(root, file)
                    dest_file = os.path.join(backup_folder, os.path.relpath(source_file, source_folder))
                    dest_dir = os.path.dirname(dest_file)

                    if not os.path.exists(dest_dir):
                        os.makedirs(dest_dir)

                    if os.path.exists(dest_file) and os.path.getmtime(dest_file) >= os.path.getmtime(source_file):
                        # skip if destination file already exists and is newer than source file
                        with open(log_file, 'a') as f:
                            f.write(f'[SKIP] {source_file} (already exists)\n')
                    else:
                        shutil.copy2(source_file, dest_file)
                        with open(log_file, 'a') as f:
                            f.write(f'[COPY] {source_file} to {dest_file}\n')

                    current_file += 1
                    progress_bar['value'] = current_file
                    progress_label.config(text=f'Backing up {source_file}...')
                    progress_window.update()

        with open(log_file, 'a') as f:
            f.write(f'Backup completed at {time.strftime("%Y-%m-%d %H:%M:%S")}\n')

        if compress_var.get():
            progress_label.config(text='Compressing backup files...')
            zip_file = os.path.join(dest_folder, f'backup-{timestamp}.zip')
            with zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED) as zipf:
                for root, dirs, files in os.walk(backup_folder):
                    for file in files:
                        zipf.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), backup_folder))

            shutil.rmtree(backup_folder)

        messagebox.showinfo('Success', 'Backup completed successfully.')
        clear()
    except Exception as e:
        with open(log_file, 'a') as f:
            f.write(f'[ERROR] An error occurred during backup: {str(e)}\n')
        messagebox.showerror('Error', f'An error occurred during backup:\n{str(e)}')    

def browse_source_folder():
    folder = filedialog.askdirectory()
    if folder:
        source_folder_text.insert('end', folder + '\n')

def browse_source_file():
    file = filedialog.askopenfilename()
    if file:
        source_folder_text.insert('end', file + '\n')

def browse_dest_folder():
    folder = filedialog.askdirectory()
    if folder:
        dest_folder_text.delete('1.0', 'end')
        dest_folder_text.insert('1.0', folder)

def clear():
    source_folder_text.delete('1.0', 'end')
    dest_folder_text.delete('1.0', 'end')
    compress_var.set(False)


# Create the main window
root = tk.Tk()
root.title('Backup Tool')
root.geometry('820x400')
root.config(bg='#FFFFFF')

# Create the source folder section
source_folder_label = tk.Label(root, text='Source folders or files:', font=('Helvetica', 12, 'bold'), bg='#FFFFFF')
source_folder_label.pack(pady=10)
source_folder_text = tk.Text(root, height=5)
source_folder_text.pack()
select_folder_or_file_label = tk.Label(root, text='Select either a folder or a file for backup, but not both.', font=('TkDefaultFont', 10), fg='red', bg='#FFFFFF')
select_folder_or_file_label.pack(pady=5)
source_folder_button_frame = tk.Frame(root, bg='#FFFFFF')
source_folder_button_frame.pack(pady=5)
browse_source_folder_button = tk.Button(source_folder_button_frame, text='Browse Folder', command=browse_source_folder, font=('Helvetica', 10))
browse_source_folder_button.pack(side='left', padx=10)
browse_source_file_button = tk.Button(source_folder_button_frame, text='Browse File', command=browse_source_file, font=('Helvetica', 10))
browse_source_file_button.pack(side='left')

# Create the destination folder section
dest_folder_frame = tk.Frame(root, bg='#FFFFFF')
dest_folder_frame.pack(pady=10)
dest_folder_label = tk.Label(dest_folder_frame, text='Destination folder:', font=('Helvetica', 12, 'bold'), bg='#FFFFFF')
dest_folder_label.pack(side='left', padx=10)
dest_folder_text = tk.Text(dest_folder_frame, height=1, font=('Helvetica', 10))
dest_folder_text.pack(side='left')
browse_dest_folder_button = tk.Button(dest_folder_frame, text='Browse', command=browse_dest_folder, font=('Helvetica', 10))
browse_dest_folder_button.pack(side='left', padx=10)

# Create the backup and compression section
compress_checkbox_frame = tk.Frame(root, bg='#FFFFFF')
compress_checkbox_frame.pack(pady=10)
compress_var = tk.BooleanVar()
compress_checkbox = tk.Checkbutton(compress_checkbox_frame, text='Compress backup files', variable=compress_var, bg='#FFFFFF')
compress_checkbox.pack(side='left', padx=10)
clear_button = tk.Button(compress_checkbox_frame, text='Clear', command=clear, font=('Helvetica', 10))
clear_button.pack(side='left')
backup_button = tk.Button(root, text='Backup', command=backup, font=('Helvetica', 12, 'bold'))
backup_button.pack(pady=10)

# Start the main event loop
root.mainloop()

I tried modify it but im stuck


Solution

  • The reason why only your folders get backed up, but not you files is because of this line for root, dirs, files in os.walk(source_folder) when you back up. This works as expected when source_folder is a folder, but when source_folder is a file, os.walk doesn't return anything for you to loop over.

    So what you should do is perform a check if you are backing up files or folders. You can do this by using your is_file_selected and is_folder selected. Then the code you have now for backing up your folders goes under is_folder_selected, and for when files are selected you may simply just loop over them.

    # Check if we are backing up files or folders
    if is_folders_selected:
        for source_folder in source_folders:
            for root, dirs, files in os.walk(source_folder):
                for file in files:
                    # The same code as before
                    # ...
    if is_files_selected:
        for source_file in source_folders:
            # Then use almost the same code as in folders