import tkinter as tk from tkinter import ttk, scrolledtext, filedialog, messagebox import json class ResultsFrame(ttk.Frame): """Frame for displaying processing results with actions.""" def __init__(self, parent, export_command=None, **kwargs): """ Initialize the Results Frame. Args: parent: The parent widget. export_command: Callback function for the Export SCADA button. **kwargs: Additional arguments for ttk.Frame. """ super().__init__(parent, **kwargs) self._export_command = export_command # Store the command self._create_widgets() def _create_widgets(self): """Create the widgets for the results frame.""" # Create scrolled text widget self.results_text = scrolledtext.ScrolledText( self, wrap=tk.WORD, width=80, height=20, font=('Consolas', 10), state='disabled' # Start disabled ) self.results_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # Create button frame button_frame = ttk.Frame(self) button_frame.pack(fill=tk.X, padx=5, pady=(0, 5)) # Create buttons self.clear_button = ttk.Button( button_frame, text="Clear", command=self.clear ) self.clear_button.pack(side=tk.LEFT, padx=5) self.copy_button = ttk.Button( button_frame, text="Copy to Clipboard", command=self.copy_to_clipboard ) self.copy_button.pack(side=tk.LEFT, padx=5) self.save_button = ttk.Button( button_frame, text="Save to File", command=self.save_to_file ) self.save_button.pack(side=tk.LEFT, padx=5) # Check if the export command was provided before creating the button if self._export_command: self.export_button = ttk.Button( button_frame, text="Export SCADA", command=self._export_command # Use the stored command ) self.export_button.pack(side=tk.LEFT, padx=5) else: # Optional: Log a warning if command is missing, though it shouldn't be print("Warning: Export command not provided to ResultsFrame.") def set_results(self, results_data): """ Format and display the results data (typically a list of dicts). Args: results_data: The data to display. """ self.results_text.configure(state='normal') self.results_text.delete(1.0, tk.END) if not results_data: self.results_text.insert(tk.END, "No elements found or processed matching the criteria.") self.results_text.configure(state='disabled') return try: # Format results as JSON string formatted_json = json.dumps(results_data, indent=2) # Insert into text widget (handle large results if necessary) chunk_size = 20000 # Insert in chunks to avoid freezing on huge results if len(formatted_json) > chunk_size: # Optionally show a temporary message while inserting large data # print(f"Displaying large result ({len(results_data)} items) in chunks.") for i in range(0, len(formatted_json), chunk_size): chunk = formatted_json[i:i+chunk_size] self.results_text.insert(tk.END, chunk) # Periodically update UI to prevent freezing if i % (chunk_size * 10) == 0: # Update every 10 chunks self.update_idletasks() else: self.results_text.insert(tk.END, formatted_json) except TypeError as te: error_msg = f"Error formatting results: {te}\n\nData might not be JSON serializable.\nProblematic data structure: {str(results_data)[:500]}..." print(error_msg) self.results_text.insert(tk.END, error_msg) messagebox.showerror("Result Display Error", "Data could not be formatted as JSON. See logs for details.") except Exception as e: error_msg = f"Unexpected error displaying results: {e}" print(error_msg) self.results_text.insert(tk.END, error_msg) messagebox.showerror("Result Display Error", f"An unexpected error occurred: {e}") finally: self.results_text.configure(state='disabled') # Disable editing def get_results(self): """Get the current text content from the results widget.""" return self.results_text.get(1.0, tk.END).strip() def clear(self): """Clear the results text widget.""" self.results_text.configure(state='normal') self.results_text.delete(1.0, tk.END) self.results_text.configure(state='disabled') def copy_to_clipboard(self): """Copy the results text content to the clipboard.""" text = self.get_results() if not text or text == "No elements found or processed matching the criteria.": messagebox.showinfo("Info", "No results content to copy.", parent=self) return try: self.clipboard_clear() self.clipboard_append(text) messagebox.showinfo("Success", "Results copied to clipboard!", parent=self) except tk.TclError as e: # Handle potential clipboard access errors (common on some systems/VMs) messagebox.showerror("Clipboard Error", f"Could not access the system clipboard.\nError: {e}", parent=self) except Exception as e: messagebox.showerror("Clipboard Error", f"An unexpected error occurred:\n{e}", parent=self) def save_to_file(self): """Save the results text content to a file.""" text = self.get_results() if not text or text == "No elements found or processed matching the criteria.": messagebox.showinfo("Info", "No results content to save.", parent=self) return file_path = filedialog.asksaveasfilename( parent=self, title="Save Results As", defaultextension=".json", filetypes=[("JSON files", "*.json"), ("Text files", "*.txt"), ("All files", "*.*")] ) if file_path: try: with open(file_path, 'w', encoding='utf-8') as f: f.write(text) messagebox.showinfo("Success", f"Results saved to\n{file_path}", parent=self) except Exception as e: messagebox.showerror("Save Error", f"Error saving results to file:\n{str(e)}", parent=self) # Example usage (for testing) if __name__ == '__main__': root = tk.Tk() root.title("Results Frame Test") root.geometry("600x400") # Style for testing style = ttk.Style() style.theme_use('clam') style.configure('TFrame', background='#f0f0f0') style.configure('TLabel', background='#f0f0f0') # Create and pack the results frame results_frame = ResultsFrame(root) results_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # Test data test_data = [ {"id": "rect1", "type": "rectangle", "x": 10, "y": 20, "width": 50, "height": 30}, {"id": "circle1", "type": "circle", "cx": 100, "cy": 100, "r": 40} ] # Test buttons def show_results(): results_frame.set_results(test_data) ttk.Button(root, text="Show Test Results", command=show_results).pack(pady=5) root.mainloop()