diff --git a/dashboard_gui.py b/dashboard_gui.py index 5c0cc96..6cc01c7 100644 --- a/dashboard_gui.py +++ b/dashboard_gui.py @@ -1711,6 +1711,10 @@ class DashboardGUI: self.process_btn = ttk.Button(form_frame, text="🚀 Start Processing", command=self._run_process) self.process_btn.grid(row=1, column=0, sticky=tk.W, pady=(20, 0)) + # Cancel button (initially hidden/disabled) + self.cancel_btn = tk.Button(form_frame, text="✖ Cancel", command=self._cancel_process, state="disabled") + self.cancel_btn.grid(row=1, column=0, sticky=tk.E, pady=(20, 0)) + # Progress bar self.progress_var = tk.DoubleVar() self.progress_bar = ttk.Progressbar(form_frame, variable=self.progress_var, @@ -1993,42 +1997,33 @@ class DashboardGUI: messagebox.showerror("Process", "Please enter a valid positive integer for limit.", parent=self.root) return + # Allow cancellation across worker thread + self._process_stop_event = threading.Event() + def worker(): try: # Disable the button and initialize progress self.process_btn.config(state="disabled", text="⏳ Processing...") + self.cancel_btn.config(state="normal") self.progress_var.set(0) self.progress_status_var.set("Starting processing...") self.status_label.config(text="Processing...") - # Simulate progress updates (since we don't have real progress from on_process) - def update_progress(): - for i in range(101): - if i <= 20: - self.progress_status_var.set("Initializing face detection...") - elif i <= 40: - self.progress_status_var.set("Loading photos...") - elif i <= 60: - self.progress_status_var.set("Detecting faces...") - elif i <= 80: - self.progress_status_var.set("Processing face encodings...") - elif i <= 95: - self.progress_status_var.set("Saving results...") - else: - self.progress_status_var.set("Finalizing...") - - self.progress_var.set(i) - self.root.update_idletasks() - time.sleep(0.05) # Small delay to show progress + # Prepare real progress callback to be invoked per photo + def progress_callback(index, total, filename): + try: + percent = int((index / max(total, 1)) * 100) + def _update(): + self.progress_var.set(percent) + self.progress_status_var.set(f"Processing {index}/{total}: {filename}") + self.root.after(0, _update) + except Exception: + pass - # Start progress updates in a separate thread - progress_thread = threading.Thread(target=update_progress, daemon=True) - progress_thread.start() + # Run the actual processing with real progress updates and stop event + result = self.on_process(limit_value, progress_callback, self._process_stop_event) - # Run the actual processing - result = self.on_process(limit_value) - - # Ensure progress reaches 100% + # Ensure progress reaches 100% at the end self.progress_var.set(100) self.progress_status_var.set("Processing completed successfully!") @@ -2043,7 +2038,23 @@ class DashboardGUI: finally: # Re-enable the button regardless of success or failure self.process_btn.config(state="normal", text="🚀 Start Processing") + self.cancel_btn.config(state="disabled") + # Clear stop event + self._process_stop_event = None threading.Thread(target=worker, daemon=True).start() + def _cancel_process(self): + """Signal the running process to stop, if any.""" + try: + if getattr(self, "_process_stop_event", None) is not None: + self._process_stop_event.set() + # Inform the user via UI immediately + def _update(): + self.progress_status_var.set("Cancelling...") + self.cancel_btn.config(state="disabled") + self.root.after(0, _update) + except Exception: + pass +