import tkinter as tk from tkinter import filedialog, messagebox from typing import Optional, Dict, Any class DataFileSelectorDialog(tk.Toplevel): """ A modal dialog window for selecting a data file and its type. This dialog prompts the user to browse for a file and specify whether it contains a "Vector" or a "Matrix". """ def __init__(self, master: tk.Tk, initial_dir: str): """ Initializes the dialog. Args: master: The parent tk.Tk window. initial_dir: The initial directory for the file browser. """ super().__init__(master) self.title("Select Data File") self.transient(master) # Keep dialog on top of the main window self.grab_set() # Make the dialog modal # --- Internal state --- self._file_path = tk.StringVar() self._data_type = tk.StringVar(value="Vector") # Default selection self._initial_dir = initial_dir self.result: Optional[Dict[str, Any]] = None self._create_widgets() self._center_window() # This call blocks until the window is destroyed self.wait_window(self) def _create_widgets(self) -> None: """Creates and arranges the widgets in the dialog.""" main_frame = tk.Frame(self, padx=15, pady=15) main_frame.pack(expand=True, fill=tk.BOTH) # --- File Path Selection --- file_frame = tk.Frame(main_frame) file_frame.pack(fill=tk.X, pady=(0, 10)) lbl_file = tk.Label(file_frame, text="File Path:") lbl_file.pack(side=tk.LEFT, padx=(0, 5)) entry_file = tk.Entry(file_frame, textvariable=self._file_path, width=40) entry_file.pack(side=tk.LEFT, fill=tk.X, expand=True) btn_browse = tk.Button(file_frame, text="Browse...", command=self._browse_file) btn_browse.pack(side=tk.LEFT, padx=(5, 0)) # --- Data Type Selection --- type_frame = tk.LabelFrame(main_frame, text="Data Type", padx=10, pady=10) type_frame.pack(fill=tk.X, pady=5) rb_vector = tk.Radiobutton( type_frame, text="Vector (Single Column)", variable=self._data_type, value="Vector" ) rb_vector.pack(anchor=tk.W) rb_matrix = tk.Radiobutton( type_frame, text="Matrix (Header + Data)", variable=self._data_type, value="Matrix" ) rb_matrix.pack(anchor=tk.W) # --- Action Buttons --- button_frame = tk.Frame(main_frame) button_frame.pack(fill=tk.X, pady=(15, 0)) btn_load = tk.Button(button_frame, text="Load", command=self._load_data) btn_load.pack(side=tk.RIGHT) btn_cancel = tk.Button(button_frame, text="Cancel", command=self._cancel) btn_cancel.pack(side=tk.RIGHT, padx=(0, 10)) def _center_window(self) -> None: """Centers the dialog on the screen.""" self.update_idletasks() # Ensure window size is calculated # Get screen and window dimensions screen_width = self.winfo_screenwidth() screen_height = self.winfo_screenheight() window_width = self.winfo_width() window_height = self.winfo_height() # Calculate position pos_x = (screen_width // 2) - (window_width // 2) pos_y = (screen_height // 2) - (window_height // 2) self.geometry(f"{window_width}x{window_height}+{pos_x}+{pos_y}") self.resizable(False, False) def _browse_file(self) -> None: """Opens the system's file browser dialog.""" filename = filedialog.askopenfilename( initialdir=self._initial_dir, title="Select a CSV data file", filetypes=(("CSV files", "*.csv"), ("All files", "*.*")) ) if filename: self._file_path.set(filename) def _load_data(self) -> None: """Validates the inputs and sets the result.""" file_path = self._file_path.get() if not file_path: messagebox.showwarning("Input Missing", "Please select a file path.", parent=self) return self.result = { "file_path": file_path, "data_type": self._data_type.get() } self.destroy() def _cancel(self) -> None: """Closes the dialog without setting a result.""" self.result = None self.destroy()