prima versione della compact

This commit is contained in:
VALLONGOL 2026-01-16 15:42:22 +01:00
parent 14b69f1e3d
commit b17de5f03c
5 changed files with 175 additions and 39 deletions

2
.vscode/launch.json vendored
View File

@ -26,7 +26,7 @@
"request": "launch",
"module": "VideoReceiverSFP",
"console": "integratedTerminal",
"args": ["--host", "127.0.0.1", "--port", "55556"] //,"--verbose"]//, "--duration", "30"]//, "--type", "MFD"]
"args": ["--host", "127.0.0.1", "--port", "55556", "--compact"] //,"--verbose"]//, "--duration", "30"]//, "--type", "MFD"]
}
]
}

View File

@ -189,6 +189,50 @@ class SfpConnectorModule:
return True
return False
def _load_next_frame(self):
"""Load next frame from `self._sim_dir` for simulation mode.
Returns a PIL.Image (RGB) or None if no frame available.
"""
try:
if not self._sim_dir:
return None
# Cache file list
if not hasattr(self, '_sim_files') or self._sim_files is None:
p = pathlib.Path(self._sim_dir)
if not p.exists() or not p.is_dir():
return None
files = []
for ext in ('*.png', '*.jpg', '*.jpeg', '*.bmp', '*.tif', '*.tiff'):
files.extend(sorted(p.glob(ext)))
self._sim_files = [str(x) for x in files]
if not self._sim_files:
return None
if not self._sim_files:
return None
idx = int(self._frame_idx) % len(self._sim_files)
path = self._sim_files[idx]
try:
if Image is not None:
img = Image.open(path).convert('RGB')
else:
# Fallback: return raw bytes
with open(path, 'rb') as fh:
return fh.read()
except Exception:
logging.getLogger().exception('VideoReceiverSFP: failed to open simulated frame %s', path)
# Skip this file
self._frame_idx = (self._frame_idx + 1) % max(1, len(self._sim_files))
return None
self._frame_idx = (self._frame_idx + 1) % max(1, len(self._sim_files))
return img
except Exception:
logging.getLogger().exception('VideoReceiverSFP: _load_next_frame encountered an error')
return None
def _ensure_mfd_lut(self):
if self._mfd_lut is not None:
return

View File

@ -23,6 +23,7 @@ def run_orchestrator():
parser.add_argument("--no-gui", action="store_true", help="Run in headless mode (no GUI)")
parser.add_argument("--normalize", choices=["none", "cp", "autocontrast"], default="none", help="Normalize raw frames before display: 'cp' uses controlpanel.normalize_image if available, 'autocontrast' uses PIL fallback")
parser.add_argument("--verbose", action="store_true", help="Enable verbose DEBUG logging")
parser.add_argument("--compact", action="store_true", help="Start viewers in compact mode (hide controls, accessible via right-click)")
parser.add_argument(
'--image-type', '--type', dest='image_type', type=str, choices=['MFD', 'SAR', 'ALL'], default='ALL',
help='Filter for a specific image type (MFD or SAR). Default is ALL.'
@ -150,8 +151,8 @@ def run_orchestrator():
logging.getLogger().exception('Failed to set SAR parameter on module')
# Instantiate embedded viewers inside the Labelframes
viewer = SfpViewerWithParams(on_mfd_param_changed=on_mfd_param_changed, master=left_box)
sar_viewer = SfpSarViewer(on_sar_param_changed=on_sar_param_changed, master=right_box)
viewer = SfpViewerWithParams(on_mfd_param_changed=on_mfd_param_changed, master=left_box, compact=args.compact)
sar_viewer = SfpSarViewer(on_sar_param_changed=on_sar_param_changed, master=right_box, compact=args.compact)
logging.info("VideoReceiverSFP: embedded MFD and SAR viewers created")
except ImportError:

View File

@ -19,7 +19,7 @@ except Exception:
class SfpSarViewer:
def __init__(self, window_title: str = "SFP SAR Viewer", on_sar_param_changed: Optional[Callable] = None, master: Optional[tk.Widget] = None):
def __init__(self, window_title: str = "SFP SAR Viewer", on_sar_param_changed: Optional[Callable] = None, master: Optional[tk.Widget] = None, compact: bool = False):
# Support embedding inside an existing master (Labelframe/frame). If no master provided,
# create a standalone Tk root as before.
if master is None:
@ -32,6 +32,8 @@ class SfpSarViewer:
self._owns_root = False
self._on_sar_param_changed = on_sar_param_changed
# Compact mode: hide controls by default and expose via right-click
self._compact = bool(compact)
# Build UI: image on left, controls on right
main = ttk.Frame(self._root)
@ -46,20 +48,22 @@ class SfpSarViewer:
self._img_canvas.pack(fill="both", expand=True)
self._canvas_image_id = None
# Right controls
# Right controls (hidden in compact mode)
self._brightness_var = tk.IntVar(value=0)
self._contrast_var = tk.IntVar(value=0)
self._save_png_var = tk.BooleanVar(value=False)
if not self._compact:
ctrl_frame = ttk.Labelframe(main, text="SAR Controls", padding=8)
ctrl_frame.grid(row=0, column=1, sticky="nsew", padx=(8,0))
# Brightness slider (-100..100)
ttk.Label(ctrl_frame, text="Brightness").grid(row=0, column=0, sticky="w")
self._brightness_var = tk.IntVar(value=0)
b_slider = ttk.Scale(ctrl_frame, from_=-100, to=100, orient="horizontal",
variable=self._brightness_var, command=self._on_brightness_changed)
b_slider.grid(row=1, column=0, sticky="ew", pady=(0,6))
# Contrast slider (-100..100)
ttk.Label(ctrl_frame, text="Contrast").grid(row=2, column=0, sticky="w")
self._contrast_var = tk.IntVar(value=0)
c_slider = ttk.Scale(ctrl_frame, from_=-100, to=100, orient="horizontal",
variable=self._contrast_var, command=self._on_contrast_changed)
c_slider.grid(row=3, column=0, sticky="ew", pady=(0,6))
@ -77,11 +81,23 @@ class SfpSarViewer:
save_img_btn.grid(row=6, column=0, sticky="ew", pady=(4,8))
# Save PNG toggle
self._save_png_var = tk.BooleanVar(value=False)
save_cb = ttk.Checkbutton(ctrl_frame, text="Save .png", variable=self._save_png_var, command=self._on_save_png_toggled)
save_cb.grid(row=7, column=0, sticky="w", pady=(2,2))
else:
# Compact mode: provide context menu on canvas to open controls and metadata
try:
self._context_menu = tk.Menu(self._root, tearoff=0)
self._context_menu.add_command(label="Open Controls", command=self._open_controls_window)
self._context_menu.add_command(label="Show Metadata", command=self._on_show_metadata)
self._img_canvas.bind("<Button-3>", self._on_canvas_right_click)
except Exception:
pass
if not self._compact:
try:
ctrl_frame.columnconfigure(0, weight=1)
except Exception:
pass
main.rowconfigure(0, weight=1)
main.rowconfigure(1, weight=0)
main.columnconfigure(0, weight=3)
@ -177,6 +193,49 @@ class SfpSarViewer:
if self._on_sar_param_changed:
self._on_sar_param_changed("save_png", None, val)
def _on_canvas_right_click(self, event):
try:
if hasattr(self, '_context_menu') and self._context_menu:
self._context_menu.tk_popup(event.x_root, event.y_root)
except Exception:
pass
def _open_controls_window(self):
"""Open a Toplevel window with SAR controls (used in compact mode)."""
try:
win = tk.Toplevel(self._root)
win.title('SAR Controls')
frm = ttk.Frame(win, padding=8)
frm.pack(fill='both', expand=True)
ttk.Label(frm, text="Brightness").grid(row=0, column=0, sticky="w")
b_slider = ttk.Scale(frm, from_=-100, to=100, orient="horizontal",
variable=self._brightness_var, command=self._on_brightness_changed)
b_slider.grid(row=1, column=0, sticky="ew", pady=(0,6))
ttk.Label(frm, text="Contrast").grid(row=2, column=0, sticky="w")
c_slider = ttk.Scale(frm, from_=-100, to=100, orient="horizontal",
variable=self._contrast_var, command=self._on_contrast_changed)
c_slider.grid(row=3, column=0, sticky="ew", pady=(0,6))
auto_btn = ttk.Button(frm, text="AutoContrast", command=self._on_autocontrast)
auto_btn.grid(row=4, column=0, sticky="ew", pady=(4,8))
meta_btn = ttk.Button(frm, text="Show Metadata", command=self._on_show_metadata)
meta_btn.grid(row=5, column=0, sticky="ew", pady=(4,8))
save_img_btn = ttk.Button(frm, text="Save Image", command=self._on_save_current_image)
save_img_btn.grid(row=6, column=0, sticky="ew", pady=(4,8))
save_cb = ttk.Checkbutton(frm, text="Save .png", variable=self._save_png_var, command=self._on_save_png_toggled)
save_cb.grid(row=7, column=0, sticky="w", pady=(2,2))
frm.columnconfigure(0, weight=1)
win.transient(self._root)
win.lift()
except Exception:
logging.exception("Failed to open SAR controls window")
def _on_show_metadata(self):
try:
if self._meta_window is None or not getattr(self._meta_window, 'winfo_exists', lambda: False)():

View File

@ -30,7 +30,8 @@ class SfpViewerWithParams:
def __init__(self,
window_title: str = "SFP Viewer with MFD Params",
on_mfd_param_changed: Optional[Callable] = None,
master: Optional[tk.Widget] = None):
master: Optional[tk.Widget] = None,
compact: bool = False):
"""Initialize viewer with MFD parameter controls.
Args:
@ -49,6 +50,8 @@ class SfpViewerWithParams:
# Callback for parameter changes
self._on_mfd_param_changed = on_mfd_param_changed
# Compact mode: hide controls by default and expose via right-click
self._compact = bool(compact)
# MFD state manager (local instance)
if MfdState:
@ -112,10 +115,18 @@ class SfpViewerWithParams:
self._img_label = tk.Label(self._img_canvas, bg="black")
self._img_canvas.create_window(242, 242, window=self._img_label)
# Right: MFD Parameters
if self._mfd_state:
# Right: MFD Parameters (hidden in compact mode)
if not self._compact and self._mfd_state:
params_frame = self._build_mfd_params_frame(main_frame)
params_frame.grid(row=0, column=1, sticky="nsew")
else:
# Create a context menu on the image canvas to open controls when compact
try:
self._context_menu = tk.Menu(self._root, tearoff=0)
self._context_menu.add_command(label="Open Controls", command=self._open_controls_window)
self._img_canvas.bind("<Button-3>", self._on_canvas_right_click)
except Exception:
pass
# Bottom: Status bar
@ -319,6 +330,27 @@ class SfpViewerWithParams:
if self._on_mfd_param_changed:
self._on_mfd_param_changed("save_bin", None, val)
def _on_canvas_right_click(self, event):
try:
# Show context menu
if hasattr(self, '_context_menu') and self._context_menu:
self._context_menu.tk_popup(event.x_root, event.y_root)
except Exception:
pass
def _open_controls_window(self):
"""Open a Toplevel window containing the MFD parameter controls."""
try:
win = tk.Toplevel(self._root)
win.title("MFD Controls")
# Build controls inside this window using same builder
frame = self._build_mfd_params_frame(win)
frame.pack(fill='both', expand=True, padx=6, pady=6)
win.transient(self._root)
win.lift()
except Exception:
logging.exception("Failed to open MFD controls window")
def show_frame(self, frame: Any) -> None:
"""Queue a frame for display (thread-safe)."""
try: