import tkinter as tk from tkinter import ttk from typing import Callable, Optional, Dict, Any class ConnectionPanel(ttk.LabelFrame): """Reusable connection panel used by MainView. This encapsulates the small UI that shows connection type/summary and exposes simple hooks so the outer view can attach handlers for Connect and Settings. """ def __init__(self, parent, initial_config: Optional[Dict[str, Any]] = None): super().__init__(parent, text="Connection") self._on_connect: Optional[Callable[[], None]] = None self._on_open_settings: Optional[Callable[[], None]] = None self.conn_type_var = tk.StringVar( value=(initial_config or {}).get("target", {}).get("type", "-") ) self.conn_info_var = tk.StringVar( value=self._format_summary((initial_config or {}).get("target", {})) ) ttk.Label(self, text="Type:").pack(side=tk.LEFT, padx=(6, 2)) ttk.Label(self, textvariable=self.conn_type_var, width=10).pack(side=tk.LEFT) ttk.Label(self, textvariable=self.conn_info_var).pack(side=tk.LEFT, padx=(8, 4)) # Buttons self.connect_button = ttk.Button( self, text="Connect", command=self._handle_connect ) self.connect_button.pack(side=tk.RIGHT, padx=(4, 6)) self.conn_settings_button = ttk.Button( self, text="Settings...", command=self._handle_open_settings ) self.conn_settings_button.pack(side=tk.RIGHT, padx=(4, 0)) def _format_summary(self, cfg: Dict[str, Any]) -> str: try: t = cfg.get("type") if not t: return "-" if t == "sfp": sfp = cfg.get("sfp", {}) ip = sfp.get("ip") or sfp.get("host") or "?" remote = sfp.get("port") or sfp.get("remote_port") if isinstance(remote, (list, tuple)): remote_str = ",".join(str(int(p)) for p in remote) else: try: remote_str = str(int(remote)) if remote is not None else "?" except Exception: remote_str = str(remote) local = sfp.get("local_port") if local is not None: try: local_str = str(int(local)) except Exception: local_str = str(local) return f"{ip} (remote:{remote_str} local:{local_str})" return f"{ip} (remote:{remote_str})" if t == "serial": s = cfg.get("serial", {}) port = s.get("port") or s.get("device") or "?" baud = s.get("baudrate") or s.get("baud") or "?" return f"{port} @{baud}" if t == "tftp": tftp = cfg.get("tftp", {}) host = tftp.get("host") or tftp.get("server") or "?" return f"{host}" return "-" except Exception: return "-" def set_connect_handler(self, cb: Callable[[], None]): self._on_connect = cb def set_open_settings_handler(self, cb: Callable[[], None]): self._on_open_settings = cb def _handle_connect(self): if self._on_connect: try: self._on_connect() except Exception: # Handler should manage its own errors raise def _handle_open_settings(self): if self._on_open_settings: try: self._on_open_settings() except Exception: raise def update_summary(self, conn_config: Dict[str, Any]): try: self.conn_type_var.set( (conn_config or {}).get("target", {}).get("type", "-") ) self.conn_info_var.set( self._format_summary((conn_config or {}).get("target", {})) ) except Exception: pass def update_toggle_state(self, is_connected: bool): try: self.connect_button.config(text="Disconnect" if is_connected else "Connect") except Exception: pass