197 lines
7.6 KiB
Python
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() |