prima versione della compact
This commit is contained in:
parent
14b69f1e3d
commit
b17de5f03c
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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,42 +48,56 @@ class SfpSarViewer:
|
||||
self._img_canvas.pack(fill="both", expand=True)
|
||||
self._canvas_image_id = None
|
||||
|
||||
# Right controls
|
||||
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")
|
||||
# Right controls (hidden in compact mode)
|
||||
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))
|
||||
|
||||
# Autocontrast button
|
||||
auto_btn = ttk.Button(ctrl_frame, text="AutoContrast", command=self._on_autocontrast)
|
||||
auto_btn.grid(row=4, column=0, sticky="ew", pady=(4,8))
|
||||
|
||||
# Show metadata button
|
||||
meta_btn = ttk.Button(ctrl_frame, text="Show Metadata", command=self._on_show_metadata)
|
||||
meta_btn.grid(row=5, column=0, sticky="ew", pady=(4,8))
|
||||
|
||||
# Save current equalized image button
|
||||
save_img_btn = ttk.Button(ctrl_frame, text="Save Image", command=self._on_save_current_image)
|
||||
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))
|
||||
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))
|
||||
|
||||
ctrl_frame.columnconfigure(0, weight=1)
|
||||
# Brightness slider (-100..100)
|
||||
ttk.Label(ctrl_frame, text="Brightness").grid(row=0, column=0, sticky="w")
|
||||
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")
|
||||
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))
|
||||
|
||||
# Autocontrast button
|
||||
auto_btn = ttk.Button(ctrl_frame, text="AutoContrast", command=self._on_autocontrast)
|
||||
auto_btn.grid(row=4, column=0, sticky="ew", pady=(4,8))
|
||||
|
||||
# Show metadata button
|
||||
meta_btn = ttk.Button(ctrl_frame, text="Show Metadata", command=self._on_show_metadata)
|
||||
meta_btn.grid(row=5, column=0, sticky="ew", pady=(4,8))
|
||||
|
||||
# Save current equalized image button
|
||||
save_img_btn = ttk.Button(ctrl_frame, text="Save Image", command=self._on_save_current_image)
|
||||
save_img_btn.grid(row=6, column=0, sticky="ew", pady=(4,8))
|
||||
|
||||
# Save PNG toggle
|
||||
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)():
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user