ZetaSync Python GUI
👤 Sharing: AI
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
import shutil
import threading
import time
class ZetaSyncApp:
def __init__(self, master):
self.master = master
master.title("ZetaSync - Your Ultimate File Synchronization Tool")
master.geometry("800x600")
master.configure(bg="#f0f0f0")
# Style Configuration
self.style = ttk.Style()
self.style.configure("TButton", padding=6, relief="raised", background="#4CAF50", foreground="white")
self.style.configure("TLabel", background="#f0f0f0", font=("Arial", 12))
self.style.configure("TEntry", padding=5, font=("Arial", 12))
self.style.configure("TProgressbar", thickness=20)
# Source Directory
self.source_label = ttk.Label(master, text="Source Directory:")
self.source_label.grid(row=0, column=0, padx=10, pady=10, sticky="w")
self.source_entry = ttk.Entry(master, width=60)
self.source_entry.grid(row=0, column=1, padx=10, pady=10, sticky="we")
self.source_button = ttk.Button(master, text="Browse", command=self.browse_source)
self.source_button.grid(row=0, column=2, padx=10, pady=10, sticky="e")
# Destination Directory
self.dest_label = ttk.Label(master, text="Destination Directory:")
self.dest_label.grid(row=1, column=0, padx=10, pady=10, sticky="w")
self.dest_entry = ttk.Entry(master, width=60)
self.dest_entry.grid(row=1, column=1, padx=10, pady=10, sticky="we")
self.dest_button = ttk.Button(master, text="Browse", command=self.browse_dest)
self.dest_button.grid(row=1, column=2, padx=10, pady=10, sticky="e")
# Synchronization Options
self.sync_label = ttk.Label(master, text="Synchronization Options:")
self.sync_label.grid(row=2, column=0, padx=10, pady=10, sticky="w")
self.one_way_var = tk.BooleanVar()
self.one_way_check = tk.Checkbutton(master, text="One-Way Sync (Source -> Destination)", variable=self.one_way_var, bg="#f0f0f0", font=("Arial", 12))
self.one_way_check.grid(row=2, column=1, padx=10, pady=5, sticky="w")
self.delete_var = tk.BooleanVar()
self.delete_check = tk.Checkbutton(master, text="Delete Extra Files in Destination", variable=self.delete_var, bg="#f0f0f0", font=("Arial", 12))
self.delete_check.grid(row=3, column=1, padx=10, pady=5, sticky="w")
# Exclude Patterns
self.exclude_label = ttk.Label(master, text="Exclude Patterns (comma-separated):")
self.exclude_label.grid(row=4, column=0, padx=10, pady=10, sticky="w")
self.exclude_entry = ttk.Entry(master, width=60)
self.exclude_entry.grid(row=4, column=1, padx=10, pady=10, sticky="we")
# Sync Button
self.sync_button = ttk.Button(master, text="Synchronize", command=self.start_sync)
self.sync_button.grid(row=5, column=1, padx=10, pady=20, sticky="e")
# Progress Bar
self.progress = ttk.Progressbar(master, orient="horizontal", length=600, mode="determinate")
self.progress.grid(row=6, column=0, columnspan=3, padx=10, pady=10, sticky="we")
# Status Label
self.status_label = ttk.Label(master, text="Status: Idle")
self.status_label.grid(row=7, column=0, columnspan=3, padx=10, pady=10, sticky="w")
# Details Button (Initially Hidden)
self.details_button = ttk.Button(master, text="Details", command=self.show_details)
self.details_button.grid(row=8, column=1, padx=10, pady=10, sticky="e")
self.details_button.grid_remove() #hide it initially
# Details Window (Will be created on demand)
self.details_window = None
def browse_source(self):
dirname = filedialog.askdirectory(title="Select Source Directory")
if dirname:
self.source_entry.delete(0, tk.END)
self.source_entry.insert(0, dirname)
def browse_dest(self):
dirname = filedialog.askdirectory(title="Select Destination Directory")
if dirname:
self.dest_entry.delete(0, tk.END)
self.dest_entry.insert(0, dirname)
def start_sync(self):
source = self.source_entry.get()
dest = self.dest_entry.get()
one_way = self.one_way_var.get()
delete = self.delete_var.get()
exclude_patterns = [p.strip() for p in self.exclude_entry.get().split(',') if p.strip()]
if not source or not dest:
messagebox.showerror("Error", "Please specify both source and destination directories.")
return
self.sync_button.config(state=tk.DISABLED)
self.status_label.config(text="Status: Synchronizing...")
self.progress["value"] = 0
self.master.update_idletasks()
# Start sync in a separate thread to avoid freezing the GUI
threading.Thread(target=self.synchronize, args=(source, dest, one_way, delete, exclude_patterns)).start()
def synchronize(self, source, dest, one_way, delete, exclude_patterns):
try:
file_list = self.get_file_list(source, exclude_patterns)
total_files = len(file_list)
self.progress["maximum"] = total_files
copied_files = 0 # Track number of files copied
for i, file_path in enumerate(file_list):
relative_path = os.path.relpath(file_path, source)
dest_path = os.path.join(dest, relative_path)
# Ensure destination directory exists
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
# Copy file if it doesn't exist or is newer in source
if not os.path.exists(dest_path) or os.path.getmtime(file_path) > os.path.getmtime(dest_path):
shutil.copy2(file_path, dest_path) # copy2 preserves metadata
copied_files += 1 # increment counter
self.progress["value"] = i + 1
self.status_label.config(text=f"Status: Synchronizing... ({i + 1}/{total_files} files)")
self.master.update_idletasks()
if delete and one_way:
self.delete_extra_files(source, dest, exclude_patterns)
self.status_label.config(text="Status: Synchronization Complete!")
self.details_button.grid() #Make the details button visible
messagebox.showinfo("Success", f"Synchronization complete! {copied_files} files copied.") # Include the number of files copied.
except Exception as e:
self.status_label.config(text="Status: Error during synchronization.")
messagebox.showerror("Error", f"An error occurred: {e}")
finally:
self.sync_button.config(state=tk.NORMAL)
def get_file_list(self, source, exclude_patterns):
file_list = []
for root, _, files in os.walk(source):
for file in files:
file_path = os.path.join(root, file)
relative_path = os.path.relpath(file_path, source)
if not any(pattern in relative_path for pattern in exclude_patterns):
file_list.append(file_path)
return file_list
def delete_extra_files(self, source, dest, exclude_patterns):
for root, _, files in os.walk(dest):
for file in files:
dest_file_path = os.path.join(root, file)
relative_path = os.path.relpath(dest_file_path, dest)
source_file_path = os.path.join(source, relative_path)
if not os.path.exists(source_file_path) and not any(pattern in relative_path for pattern in exclude_patterns):
try:
os.remove(dest_file_path)
print(f"Deleted extra file: {dest_file_path}")
except Exception as e:
print(f"Error deleting file {dest_file_path}: {e}")
def show_details(self):
if self.details_window is None or not tk.Toplevel.winfo_exists(self.details_window):
self.details_window = tk.Toplevel(self.master)
self.details_window.title("ZetaSync Details")
details_text = tk.Text(self.details_window, wrap=tk.WORD, height=20, width=80)
details_text.pack(padx=10, pady=10)
details_text.insert(tk.END, self.get_details_text())
details_text.config(state=tk.DISABLED) # Make it read-only
self.details_window.focus()
def get_details_text(self):
return """ZetaSync is a powerful and versatile file synchronization tool designed as an alternative to complex, paid software.
Key Features:
- One-Way and Two-Way Synchronization: Choose between one-way (source to destination) or two-way synchronization to keep your files consistent.
- Selective File Deletion: Option to delete extra files in the destination directory during one-way synchronization.
- Exclusion Patterns: Exclude specific files or directories from synchronization using comma-separated patterns (e.g., *.tmp, .git/).
- Real-Time Progress Tracking: Monitor the synchronization progress with a visual progress bar and status updates.
- Threaded Operation: Performs synchronization in a separate thread to prevent the GUI from freezing.
- Metadata Preservation: Uses shutil.copy2 to preserve file metadata during synchronization.
- Robust Error Handling: Handles potential errors during synchronization and provides informative error messages.
How to Use:
1. Select the source and destination directories.
2. Choose your synchronization options (one-way, delete extra files).
3. Specify any exclusion patterns.
4. Click the 'Synchronize' button to start the synchronization process.
ZetaSync offers a user-friendly interface and comprehensive features, making it an ideal solution for individuals and professionals who need reliable file synchronization without the cost and complexity of commercial software.
"""
root = tk.Tk()
app = ZetaSyncApp(root)
root.mainloop()
👁️ Viewed: 7
Comments