# C:/radian_plugins/test_component/adapter.py import tkinter as tk from tkinter import ttk from typing import Optional from pathlib import Path # This import works because RADIAN was installed in editable mode. from radian.components.base_component import BaseComponent, ConfigType from radian.utils import logger # Each component should have its own logger instance. log = logger.get_logger(__name__) class RadianAdapter(BaseComponent): """ A dummy component adapter for testing the RADIAN loading mechanism. This class implements the full BaseComponent contract. """ def __init__(self): log.info("TestComponent's adapter is being initialized...") # The call to super().__init__() is important as it sets up # the initial configuration by calling get_default_config(). super().__init__() # NOTE: Tkinter variables are now created inside get_config_ui # to ensure they are fresh each time the UI is built. def get_name(self) -> str: """Returns the user-friendly name of the component.""" return "Test Component" def get_description(self) -> str: """Returns a brief description of what the component does.""" return "A dummy component for demonstrating and testing the plugin system." def get_default_config(self) -> ConfigType: """Defines the default configuration parameters for this component.""" return { "test_parameter_1": "default_string_value", "test_parameter_2": True } def get_icon_path(self) -> Optional[str]: """Returns the path to this component's icon.""" # The icon is expected to be in the same directory as this adapter file. icon_path = Path(__file__).parent / "test_component.png" if icon_path.exists(): return str(icon_path) log.warning(f"Icon not found at: {icon_path}") return None def get_config_ui(self, parent: tk.Frame) -> Optional[tk.Frame]: """Creates the configuration UI for this test component.""" log.debug(f"Creating config UI for '{self.get_name()}'") frame = ttk.Frame(parent, padding="10") frame.columnconfigure(1, weight=1) # --- Initialize Tkinter variables locally within this method --- # This ensures they are new for each UI creation and avoids conflicts # with previously destroyed widgets. param1_var = tk.StringVar() param2_var = tk.BooleanVar() # --- UI Widgets --- label1 = ttk.Label(frame, text="Test Parameter 1 (String):") label1.grid(row=0, column=0, padx=5, pady=5, sticky="w") entry1 = ttk.Entry(frame, textvariable=param1_var) entry1.grid(row=0, column=1, padx=5, pady=5, sticky="ew") check2 = ttk.Checkbutton(frame, text="Enable Test Feature", variable=param2_var) check2.grid(row=1, column=0, columnspan=2, padx=5, pady=5, sticky="w") # --- Nested helper functions to manage UI state --- # These functions have access to the local tkinter variables. def update_ui_from_config(): """Helper to sync the UI with the current self.config state.""" param1_var.set(self.config.get("test_parameter_1", "")) param2_var.set(self.config.get("test_parameter_2", False)) def update_config_from_ui(*args): """Helper to sync self.config with the current UI state.""" self.config["test_parameter_1"] = param1_var.get() self.config["test_parameter_2"] = param2_var.get() log.info(f"TestComponent config updated from UI: {self.config}") # --- Load current config into UI variables --- update_ui_from_config() # --- Bind UI changes back to the config dictionary --- param1_var.trace_add("write", update_config_from_ui) param2_var.trace_add("write", update_config_from_ui) return frame def run(self, input_data: Optional[any] = None) -> any: """Executes the dummy logic of this component.""" log.info(f"--- Running '{self.get_name()}' ---") if input_data: log.info(f"Received input data: {input_data}") log.info(f"Using configuration: {self.config}") if self.config.get("test_parameter_2"): log.info("Test Feature is ENABLED.") else: log.info("Test Feature is DISABLED.") output_message = f"Output from {self.get_name()} with param1='{self.config.get('test_parameter_1')}'" log.info("--- Finished running ---") return output_message