152 lines
5.5 KiB
Python
152 lines
5.5 KiB
Python
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:
|
|
"""Return a compact human-readable summary for the connection config.
|
|
|
|
Args:
|
|
cfg (Dict[str, Any]): The connection configuration for a single
|
|
communicator (e.g., sfp/serial/tftp sections).
|
|
|
|
Returns:
|
|
str: A short summary suitable for display in the UI.
|
|
"""
|
|
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]):
|
|
"""Register a callback to be invoked when the Connect button is pressed.
|
|
|
|
The callback receives no arguments.
|
|
"""
|
|
self._on_connect = cb
|
|
|
|
def set_open_settings_handler(self, cb: Callable[[], None]):
|
|
"""Register a callback to open connection settings UI.
|
|
|
|
The callback receives no arguments and is expected to display the
|
|
settings window or dialog when invoked.
|
|
"""
|
|
self._on_open_settings = cb
|
|
|
|
def _handle_connect(self):
|
|
"""Invoke the registered connect handler (if any).
|
|
|
|
This method delegates to the user-provided handler and ensures that
|
|
handler exceptions propagate so the caller can manage them.
|
|
"""
|
|
if self._on_connect:
|
|
try:
|
|
self._on_connect()
|
|
except Exception:
|
|
# Handler should manage its own errors
|
|
raise
|
|
|
|
def _handle_open_settings(self):
|
|
"""Invoke the registered 'open settings' handler (if any)."""
|
|
if self._on_open_settings:
|
|
try:
|
|
self._on_open_settings()
|
|
except Exception:
|
|
raise
|
|
|
|
def update_summary(self, conn_config: Dict[str, Any]):
|
|
"""Update the displayed connection summary according to configuration.
|
|
|
|
Args:
|
|
conn_config (Dict[str, Any]): Full configuration dictionary where
|
|
the 'target' key holds the per-communicator section.
|
|
"""
|
|
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):
|
|
"""Change the Connect button label to reflect connection state.
|
|
|
|
Args:
|
|
is_connected (bool): If True, button shows 'Disconnect'. Otherwise
|
|
it shows 'Connect'.
|
|
"""
|
|
try:
|
|
self.connect_button.config(text="Disconnect" if is_connected else "Connect")
|
|
except Exception:
|
|
pass
|