import os import threading import logging import customtkinter as ctk from tkinter import filedialog, messagebox import download_sharepoint # Din eksisterende kerne-logik import requests # --- Global Stop Flag --- stop_event = threading.Event() # --- Logging Handler for GUI --- class TextboxHandler(logging.Handler): def __init__(self, textbox): super().__init__() self.textbox = textbox def emit(self, record): msg = self.format(record) self.textbox.after(0, self.append_msg, msg) def append_msg(self, msg): self.textbox.configure(state="normal") self.textbox.insert("end", msg + "\n") self.textbox.see("end") self.textbox.configure(state="disabled") # --- Main App --- class SharepointApp(ctk.CTk): def __init__(self): super().__init__() self.title("SharePoint Download Tool - UX") self.geometry("1000x850") # Gjort lidt bredere og højere for at give plads ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") self.grid_columnconfigure(1, weight=1) self.grid_rowconfigure(0, weight=1) # Sidebar self.sidebar_frame = ctk.CTkFrame(self, width=350, corner_radius=0) self.sidebar_frame.grid(row=0, column=0, sticky="nsew") self.sidebar_frame.grid_rowconfigure(25, weight=1) self.logo_label = ctk.CTkLabel(self.sidebar_frame, text="Indstillinger", font=ctk.CTkFont(size=20, weight="bold")) self.logo_label.grid(row=0, column=0, padx=20, pady=(20, 10)) self.entries = {} fields = [ ("TENANT_ID", "Tenant ID"), ("CLIENT_ID", "Client ID"), ("CLIENT_SECRET", "Client Secret"), ("SITE_URL", "Site URL"), ("DOCUMENT_LIBRARY", "Library Navn"), ("FOLDERS_TO_DOWNLOAD", "Mapper (komma-sep)"), ("LOCAL_PATH", "Lokal Sti"), ("ENABLE_HASH_VALIDATION", "Valider Hash (True/False)"), ("HASH_THRESHOLD_MB", "Hash Grænse (MB)") ] for i, (key, label) in enumerate(fields): lbl = ctk.CTkLabel(self.sidebar_frame, text=label) lbl.grid(row=i*2+1, column=0, padx=20, pady=(5, 0), sticky="w") entry = ctk.CTkEntry(self.sidebar_frame, width=280) if key == "CLIENT_SECRET": entry.configure(show="*") entry.grid(row=i*2+2, column=0, padx=20, pady=(0, 5)) self.entries[key] = entry self.browse_button = ctk.CTkButton(self.sidebar_frame, text="Vælg Mappe", command=self.browse_folder, height=32) self.browse_button.grid(row=20, column=0, padx=20, pady=10) self.save_button = ctk.CTkButton(self.sidebar_frame, text="Gem Indstillinger", command=self.save_settings, fg_color="transparent", border_width=2) self.save_button.grid(row=21, column=0, padx=20, pady=10) # Main side self.main_frame = ctk.CTkFrame(self, corner_radius=0, fg_color="transparent") self.main_frame.grid(row=0, column=1, sticky="nsew", padx=20, pady=20) self.main_frame.grid_rowconfigure(1, weight=1) self.main_frame.grid_columnconfigure(0, weight=1) self.status_label = ctk.CTkLabel(self.main_frame, text="Status: Klar", font=ctk.CTkFont(size=16)) self.status_label.grid(row=0, column=0, pady=(0, 10), sticky="w") self.log_textbox = ctk.CTkTextbox(self.main_frame, state="disabled") self.log_textbox.grid(row=1, column=0, sticky="nsew") # Buttons frame self.btn_frame = ctk.CTkFrame(self.main_frame, fg_color="transparent") self.btn_frame.grid(row=2, column=0, pady=(20, 0), sticky="ew") self.btn_frame.grid_columnconfigure(0, weight=1) self.start_button = ctk.CTkButton(self.btn_frame, text="Start Synkronisering", command=self.start_sync_thread, height=50, font=ctk.CTkFont(size=16, weight="bold")) self.start_button.grid(row=0, column=0, padx=(0, 10), sticky="ew") self.stop_button = ctk.CTkButton(self.btn_frame, text="Stop", command=self.stop_sync, height=50, fg_color="#d32f2f", hover_color="#b71c1c", state="disabled") self.stop_button.grid(row=0, column=1, sticky="ew") self.load_settings() self.setup_logging() def setup_logging(self): handler = TextboxHandler(self.log_textbox) handler.setFormatter(logging.Formatter('%(asctime)s: %(message)s', datefmt='%H:%M:%S')) download_sharepoint.logger.addHandler(handler) def browse_folder(self): path = filedialog.askdirectory() if path: self.entries["LOCAL_PATH"].delete(0, "end") self.entries["LOCAL_PATH"].insert(0, path) def load_settings(self): if os.path.exists("connection_info.txt"): config = download_sharepoint.load_config("connection_info.txt") for key, entry in self.entries.items(): val = config.get(key, "") entry.insert(0, val) def save_settings(self): config_lines = [f'{k} = "{v.get()}"' for k, v in self.entries.items()] with open("connection_info.txt", "w", encoding="utf-8") as f: f.write("\n".join(config_lines)) def stop_sync(self): stop_event.set() self.stop_button.configure(state="disabled", text="Stopper...") download_sharepoint.logger.warning("Stop-signal sendt. Venter på at tråde afbryder...") def start_sync_thread(self): self.save_settings() stop_event.clear() self.start_button.configure(state="disabled") self.stop_button.configure(state="normal", text="Stop") self.status_label.configure(text="Status: Synkroniserer...", text_color="orange") thread = threading.Thread(target=self.run_sync, daemon=True) thread.start() def run_sync(self): try: config = download_sharepoint.load_config("connection_info.txt") download_sharepoint.main(config=config, stop_event=stop_event) if stop_event.is_set(): self.status_label.configure(text="Status: Afbrudt", text_color="red") else: self.status_label.configure(text="Status: Gennemført!", text_color="green") except InterruptedError: self.status_label.configure(text="Status: Afbrudt", text_color="red") except Exception as e: self.status_label.configure(text="Status: Fejl!", text_color="red") messagebox.showerror("Fejl", str(e)) finally: self.start_button.configure(state="normal") self.stop_button.configure(state="disabled", text="Stop") if __name__ == "__main__": app = SharepointApp() app.mainloop()