svg-processor/gui/results_frame.py
2025-05-16 18:15:31 +04:00

197 lines
7.6 KiB
Python

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()