592 lines
24 KiB
Python
592 lines
24 KiB
Python
# -*- coding: utf-8 -*-
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
import time
|
|
import logging
|
|
from typing import Any, Dict, Optional
|
|
|
|
from .base_widgets import BaseCommandFrame, ToolTip
|
|
from pymsc.utils.converters import (
|
|
RAD_TO_DEG, DEG_TO_RAD, NM_TO_METERS, METERS_TO_FEET,
|
|
FEET_TO_METERS, MS_TO_KNOTS, KNOTS_TO_MS
|
|
)
|
|
|
|
# Registry for finding widgets by their label (used for automation/scripts)
|
|
WIDGET_MAP: Dict[str, Any] = {}
|
|
|
|
def get_correct_value(info: Dict, idx: int, value: float) -> float:
|
|
"""
|
|
Applies LSB and Scale factors to convert a raw value to a readable value.
|
|
Returns None if value is None (field not found).
|
|
"""
|
|
if value is None:
|
|
return None
|
|
suffix = str(idx) if idx != -1 else ""
|
|
lsb = info.get(f"lsb{suffix}", 1.0)
|
|
scale = info.get(f"scale{suffix}", 1.0)
|
|
result = value * (scale * lsb)
|
|
return result
|
|
|
|
def set_correct_value(info: Dict, idx: int, value: float) -> int:
|
|
"""
|
|
Applies LSB and Scale factors to convert a readable value to a raw integer.
|
|
"""
|
|
suffix = str(idx) if idx != -1 else ""
|
|
lsb = info.get(f"lsb{suffix}", 1.0)
|
|
scale = info.get(f"scale{suffix}", 1.0)
|
|
raw_val = value / (scale * lsb)
|
|
return int(raw_val)
|
|
|
|
class CommandFrameCheckBox(BaseCommandFrame):
|
|
"""
|
|
Widget containing a Label, a Command Checkbox, and a Tellback Checkbox.
|
|
"""
|
|
def __init__(self, parent: tk.Widget, command_info: Dict, **kwargs):
|
|
super().__init__(parent, command_info, **kwargs)
|
|
WIDGET_MAP[self.info['label']] = self
|
|
self._create_ui()
|
|
|
|
def _create_ui(self):
|
|
self.label = tk.Label(
|
|
self, text=self.info['label'], width=self.column_widths[0],
|
|
anchor="w", font=("Helvetica", 8)
|
|
)
|
|
self.label.grid(row=0, column=0, sticky="w", padx=5)
|
|
self.tooltip = ToolTip(self.label, self.info['description'])
|
|
|
|
if self.info.get('message'):
|
|
self.command_var = tk.BooleanVar()
|
|
self.command_check = tk.Checkbutton(
|
|
self, variable=self.command_var, command=self.on_toggle,
|
|
width=self.column_widths[1]
|
|
)
|
|
self.command_check.grid(row=0, column=1)
|
|
initial_val = self.info['message'].get_value_for_field(self.info['field'])
|
|
self.command_var.set(bool(initial_val))
|
|
|
|
if self.info.get('message_tb'):
|
|
self.tellback_var = tk.BooleanVar()
|
|
self.tellback_check = tk.Checkbutton(
|
|
self, variable=self.tellback_var, state='disabled',
|
|
width=self.column_widths[2]
|
|
)
|
|
self.tellback_check.grid(row=0, column=2)
|
|
|
|
def on_toggle(self):
|
|
# Skip if message is None (disabled field)
|
|
if not self.info.get('message'):
|
|
return
|
|
self.start_time = time.time()
|
|
val = 1 if self.command_var.get() else 0
|
|
self.info['message'].set_value_for_field(self.info['field'], val)
|
|
|
|
# Log the change
|
|
logger = logging.getLogger('ARTOS.MCS')
|
|
state = "ON" if val == 1 else "OFF"
|
|
logger.info(f"User changed '{self.info['label']}' → {state}")
|
|
|
|
if self.script_manager.is_recording:
|
|
action_val = "on" if val == 1 else "off"
|
|
self.script_manager.write_command(self.info['label'], "toggle", action_val)
|
|
if self.info.get('message_tb'):
|
|
self.monitor_tellback()
|
|
|
|
def monitor_tellback(self):
|
|
msg_tb = self.info['message_tb']
|
|
field_tb = self.info['field_tb']
|
|
current_tb = bool(msg_tb.get_value_for_field(field_tb))
|
|
self.tellback_var.set(current_tb)
|
|
elapsed = (time.time() - self.start_time) * 1000
|
|
if current_tb == self.command_var.get():
|
|
self.label.config(bg="green" if current_tb else "yellow")
|
|
return
|
|
if elapsed > self.threshold_ms:
|
|
self.label.config(bg="red")
|
|
return
|
|
self.after(self.update_interval_ms, self.monitor_tellback)
|
|
|
|
def check_updated_value(self):
|
|
if self.info.get('message'):
|
|
current_val = bool(self.info['message'].get_value_for_field(self.info['field']))
|
|
self.command_var.set(current_val)
|
|
if self.info.get('message_tb'):
|
|
current_tb = bool(self.info['message_tb'].get_value_for_field(self.info['field_tb']))
|
|
self.tellback_var.set(current_tb)
|
|
|
|
class CommandFrameComboBox(BaseCommandFrame):
|
|
"""
|
|
Widget containing a Label, a Command ComboBox, and a Tellback Label.
|
|
"""
|
|
def __init__(self, parent: tk.Widget, command_info: Dict, **kwargs):
|
|
super().__init__(parent, command_info, **kwargs)
|
|
WIDGET_MAP[self.info['label']] = self
|
|
self._create_ui()
|
|
|
|
def _create_ui(self):
|
|
self.label = tk.Label(
|
|
self, text=self.info['label'], width=self.column_widths[0],
|
|
anchor="w", font=("Helvetica", 8)
|
|
)
|
|
self.label.grid(row=0, column=0, sticky="w", padx=5)
|
|
self.tooltip = ToolTip(self.label, self.info['description'])
|
|
|
|
self.cmd_var = tk.StringVar()
|
|
self.combo = ttk.Combobox(
|
|
self, textvariable=self.cmd_var, values=self.info["values"],
|
|
width=self.column_widths[1], state="readonly"
|
|
)
|
|
self.combo.grid(row=0, column=1)
|
|
self.combo.bind("<<ComboboxSelected>>", self.on_select)
|
|
|
|
if self.info.get('message_tb'):
|
|
self.tb_var = tk.StringVar(value="---")
|
|
self.tb_label = tk.Label(
|
|
self, textvariable=self.tb_var, bg="white",
|
|
width=self.column_widths[2], relief="sunken"
|
|
)
|
|
self.tb_label.grid(row=0, column=2)
|
|
|
|
def on_select(self, event=None):
|
|
self.start_time = time.time()
|
|
idx = self.combo.current()
|
|
self.info['message'].set_value_for_field(self.info['field'], idx)
|
|
|
|
# Log the change
|
|
logger = logging.getLogger('ARTOS.MCS')
|
|
logger.info(f"User changed '{self.info['label']}' → {self.combo.get()}")
|
|
|
|
if self.script_manager.is_recording:
|
|
self.script_manager.write_command(self.info['label'], "set_value", self.combo.get())
|
|
if self.info.get('message_tb'):
|
|
self.monitor_tellback()
|
|
|
|
def monitor_tellback(self):
|
|
msg_tb = self.info['message_tb']
|
|
idx_tb = msg_tb.get_value_for_field(self.info['field_tb'])
|
|
# Handle None or invalid index
|
|
if idx_tb is None:
|
|
self.tb_var.set("---")
|
|
return
|
|
try:
|
|
val_tb_text = self.info["values_tb"][idx_tb]
|
|
except (IndexError, KeyError, TypeError):
|
|
val_tb_text = "ERR"
|
|
self.tb_var.set(val_tb_text)
|
|
elapsed = (time.time() - self.start_time) * 1000
|
|
if idx_tb == self.combo.current():
|
|
self.label.config(bg="green")
|
|
return
|
|
if elapsed > self.threshold_ms:
|
|
self.label.config(bg="red")
|
|
return
|
|
self.after(self.update_interval_ms, self.monitor_tellback)
|
|
|
|
def check_updated_value(self):
|
|
if self.info.get('message'):
|
|
current_idx = self.info['message'].get_value_for_field(self.info['field'])
|
|
if current_idx is None:
|
|
self.cmd_var.set("---")
|
|
else:
|
|
try:
|
|
self.cmd_var.set(self.info['values'][current_idx])
|
|
except (IndexError, TypeError):
|
|
self.cmd_var.set("ERR")
|
|
if self.info.get('message_tb'):
|
|
current_tb_idx = self.info['message_tb'].get_value_for_field(self.info['field_tb'])
|
|
if current_tb_idx is None:
|
|
self.tb_var.set("---")
|
|
else:
|
|
try:
|
|
self.tb_var.set(self.info['values_tb'][current_tb_idx])
|
|
except (IndexError, TypeError):
|
|
self.tb_var.set("ERR")
|
|
|
|
class CommandFrameSpinBox(BaseCommandFrame):
|
|
"""
|
|
Widget containing a Label, a Command SpinBox, and a Tellback Label.
|
|
Supports scaling (LSB/Scale) and numeric validation.
|
|
"""
|
|
def __init__(self, parent: tk.Widget, command_info: Dict, **kwargs):
|
|
super().__init__(parent, command_info, **kwargs)
|
|
WIDGET_MAP[self.info['label']] = self
|
|
self.is_programmatic_change = False
|
|
self.user_editing = False # Track if user is actively editing
|
|
self._create_ui()
|
|
|
|
def _create_ui(self):
|
|
self.label = tk.Label(
|
|
self, text=self.info['label'], width=self.column_widths[0],
|
|
anchor="w", font=("Helvetica", 8)
|
|
)
|
|
self.label.grid(row=0, column=0, sticky="w", padx=5)
|
|
self.tooltip = ToolTip(self.label, self.info['description'])
|
|
|
|
is_int = self.info.get("number_format", "float") == "integer"
|
|
self.cmd_var = tk.IntVar() if is_int else tk.DoubleVar()
|
|
self.cmd_var.set(self.info.get('default_value', 0))
|
|
|
|
v_cmd = (self.register(self._validate), '%P')
|
|
self.spin = tk.Spinbox(
|
|
self, textvariable=self.cmd_var, from_=self.info["min_value"],
|
|
to=self.info["max_value"], increment=self.info.get("increment", 1),
|
|
width=self.column_widths[1], validate="key", validatecommand=v_cmd
|
|
)
|
|
self.spin.grid(row=0, column=1)
|
|
self.spin.bind("<Return>", self.on_change)
|
|
self.spin.bind("<FocusOut>", self.on_change)
|
|
# Track when user starts editing
|
|
self.spin.bind("<FocusIn>", self._on_focus_in)
|
|
self.spin.bind("<KeyPress>", self._on_key_press)
|
|
# Detect arrow button clicks - send value immediately after arrow click
|
|
self.spin.bind("<ButtonRelease-1>", self._on_arrow_click)
|
|
# Also use variable trace to detect arrow changes
|
|
self.cmd_var.trace_add("write", self._on_var_change)
|
|
|
|
if self.info.get('message_tb'):
|
|
self.tb_var = tk.StringVar(value="0")
|
|
self.tb_label = tk.Label(
|
|
self, textvariable=self.tb_var, bg="white",
|
|
width=self.column_widths[2], relief="sunken"
|
|
)
|
|
self.tb_label.grid(row=0, column=2)
|
|
|
|
def _validate(self, p):
|
|
if p == "" or p == "-":
|
|
return True
|
|
try:
|
|
float(p)
|
|
return True
|
|
except ValueError:
|
|
return False
|
|
|
|
def _on_focus_in(self, event=None):
|
|
"""Called when spinbox receives focus - user is about to edit"""
|
|
self.user_editing = True
|
|
|
|
def _on_key_press(self, event=None):
|
|
"""Called when user types - mark as editing"""
|
|
self.user_editing = True
|
|
|
|
def _on_arrow_click(self, event=None):
|
|
"""Called when user clicks spinbox arrows - send value immediately"""
|
|
# Small delay to ensure the value has been updated by the spinbox
|
|
self.after(50, self._send_arrow_value)
|
|
|
|
def _on_var_change(self, *args):
|
|
"""Called when spinbox value changes (from arrows or typing)"""
|
|
# If we're not programmatically changing and not already sending
|
|
if not self.is_programmatic_change and not hasattr(self, '_sending_arrow_value'):
|
|
# User changed value, mark as editing
|
|
self.user_editing = True
|
|
|
|
def _send_arrow_value(self):
|
|
"""Send the value after arrow click"""
|
|
if self.is_programmatic_change:
|
|
return
|
|
self._sending_arrow_value = True
|
|
try:
|
|
val = float(self.cmd_var.get())
|
|
raw_val = set_correct_value(self.info, -1, val)
|
|
self.info['message'].set_value_for_field(self.info['field'], raw_val)
|
|
|
|
# Log the change
|
|
logger = logging.getLogger('ARTOS.MCS')
|
|
unit = self.info.get('unit', '')
|
|
unit_str = f" {unit}" if unit else ""
|
|
logger.info(f"User changed '{self.info['label']}' → {val}{unit_str}")
|
|
|
|
# Clear editing flag after sending
|
|
self.user_editing = False
|
|
except (tk.TclError, ValueError):
|
|
pass
|
|
finally:
|
|
delattr(self, '_sending_arrow_value')
|
|
|
|
def on_change(self, event=None):
|
|
if self.is_programmatic_change:
|
|
return
|
|
# Skip if message is None (disabled field)
|
|
if not self.info.get('message'):
|
|
return
|
|
self.start_time = time.time()
|
|
try:
|
|
val = float(self.cmd_var.get())
|
|
except tk.TclError:
|
|
self.user_editing = False # Clear flag even on error
|
|
return
|
|
raw_val = set_correct_value(self.info, -1, val)
|
|
self.info['message'].set_value_for_field(self.info['field'], raw_val)
|
|
|
|
# Log the change
|
|
logger = logging.getLogger('ARTOS.MCS')
|
|
unit = self.info.get('unit', '')
|
|
unit_str = f" {unit}" if unit else ""
|
|
logger.info(f"User changed '{self.info['label']}' → {val}{unit_str}")
|
|
|
|
if self.script_manager.is_recording:
|
|
self.script_manager.write_command(self.info['label'], "set_value", val)
|
|
# Clear editing flag after successful change
|
|
self.user_editing = False
|
|
if self.info.get('message_tb'):
|
|
self.monitor_tellback()
|
|
|
|
def monitor_tellback(self):
|
|
msg_tb = self.info['message_tb']
|
|
raw_tb = msg_tb.get_value_for_field(self.info['field_tb'])
|
|
readable_tb = get_correct_value(self.info, -1, raw_tb)
|
|
fmt = "%.0f" if self.info.get("number_format") == "integer" else "%.4f"
|
|
self.tb_var.set(fmt % readable_tb)
|
|
elapsed = (time.time() - self.start_time) * 1000
|
|
target = float(self.cmd_var.get())
|
|
if abs(readable_tb - target) < 0.0001:
|
|
self.label.config(bg="green")
|
|
return
|
|
if elapsed > self.threshold_ms:
|
|
self.label.config(bg="red")
|
|
return
|
|
self.after(self.update_interval_ms, self.monitor_tellback)
|
|
|
|
def check_updated_value(self):
|
|
# Skip if user is actively editing this field
|
|
if self.user_editing:
|
|
return
|
|
|
|
# Skip if message is None (disabled field)
|
|
if not self.info.get('message'):
|
|
return
|
|
|
|
self.is_programmatic_change = True
|
|
raw_val = self.info['message'].get_value_for_field(self.info['field'])
|
|
readable = get_correct_value(self.info, -1, raw_val)
|
|
if readable is not None:
|
|
self.cmd_var.set(readable)
|
|
else:
|
|
self.cmd_var.set(0) # Default value for None
|
|
self.is_programmatic_change = False
|
|
if self.info.get('message_tb'):
|
|
raw_tb = self.info['message_tb'].get_value_for_field(self.info['field_tb'])
|
|
readable_tb = get_correct_value(self.info, -1, raw_tb)
|
|
if readable_tb is not None:
|
|
fmt = "%.0f" if self.info.get("number_format") == "integer" else "%.4f"
|
|
self.tb_var.set(fmt % readable_tb)
|
|
else:
|
|
self.tb_var.set("---")
|
|
|
|
class CommandFrameLabels(tk.Frame):
|
|
"""
|
|
Widget for displaying up to 3 read-only values from the bus.
|
|
"""
|
|
def __init__(self, parent: tk.Widget, command_info: Dict, **kwargs):
|
|
super().__init__(parent)
|
|
self.info = command_info
|
|
self.vars = {}
|
|
self.column_widths = [20, 14, 14, 14]
|
|
WIDGET_MAP[self.info['label']] = self
|
|
self._create_ui()
|
|
|
|
def _create_ui(self):
|
|
self.label = tk.Label(
|
|
self, text=self.info['label'], width=self.column_widths[0],
|
|
anchor="w", font=("Helvetica", 8)
|
|
)
|
|
self.label.grid(row=0, column=0, sticky="w", padx=5)
|
|
ToolTip(self.label, self.info['description'])
|
|
|
|
for i in range(1, 4):
|
|
msg = self.info.get(f"message{i}")
|
|
if msg:
|
|
field = self.info[f"field{i}"]
|
|
self.vars[field] = tk.StringVar(value="0")
|
|
lbl = tk.Label(
|
|
self, textvariable=self.vars[field], bg="#f0f0f0",
|
|
width=self.column_widths[i], relief="groove", font=("Helvetica", 8)
|
|
)
|
|
lbl.grid(row=0, column=i, padx=2)
|
|
ToolTip(lbl, self.info.get(f"tooltip{i}", ""))
|
|
|
|
def check_updated_value(self):
|
|
for i in range(1, 4):
|
|
msg = self.info.get(f"message{i}")
|
|
if msg:
|
|
field = self.info[f"field{i}"]
|
|
raw = msg.get_value_for_field(field)
|
|
val = get_correct_value(self.info, i, raw)
|
|
if val is not None:
|
|
self.vars[field].set("%.4f" % val)
|
|
else:
|
|
self.vars[field].set("N/A")
|
|
|
|
class CommandFrameControls(tk.Frame):
|
|
"""
|
|
Highly flexible widget that can combine up to 3 sub-controls.
|
|
"""
|
|
def __init__(self, parent: tk.Widget, command_info: Dict, **kwargs):
|
|
super().__init__(parent)
|
|
self.info = command_info
|
|
self.column_widths = [20, 14, 14, 14]
|
|
self.vars = {}
|
|
self.is_programmatic_change = False
|
|
self.editing_fields = {} # Track which fields are being edited
|
|
WIDGET_MAP[self.info['label']] = self
|
|
self._create_ui()
|
|
|
|
def _create_ui(self):
|
|
self.title_label = tk.Label(
|
|
self, text=self.info['label'], width=self.column_widths[0],
|
|
anchor="w", font=("Helvetica", 8)
|
|
)
|
|
self.title_label.grid(row=0, column=0, sticky="w", padx=5)
|
|
ToolTip(self.title_label, self.info['description'])
|
|
|
|
for i in range(1, 4):
|
|
ctrl_type = self.info.get(f"type{i}")
|
|
if not ctrl_type:
|
|
continue
|
|
self._add_sub_control(i, ctrl_type)
|
|
|
|
def _add_sub_control(self, i: int, ctrl_type: str):
|
|
field = self.info[f"field{i}"]
|
|
msg = self.info.get(f"message{i}")
|
|
|
|
if ctrl_type == "checkbox":
|
|
self.vars[field] = tk.BooleanVar()
|
|
lbl_text = self.info.get(f"label{i}", "")
|
|
cb = tk.Checkbutton(
|
|
self, text=lbl_text, variable=self.vars[field], width=self.column_widths[i],
|
|
command=lambda f=field, m=msg: self._on_check(f, m)
|
|
)
|
|
cb.grid(row=0, column=i)
|
|
WIDGET_MAP[field] = {"widget": cb, "var": self.vars[field]}
|
|
|
|
elif ctrl_type == "combobox":
|
|
self.vars[field] = tk.StringVar()
|
|
vals = self.info.get(f"values{i}", [])
|
|
cmb = ttk.Combobox(
|
|
self, textvariable=self.vars[field], values=vals,
|
|
width=self.column_widths[i], state="readonly"
|
|
)
|
|
cmb.grid(row=0, column=i)
|
|
cmb.bind("<<ComboboxSelected>>", lambda e, f=field, m=msg, idx=i: self._on_combo(f, m, idx))
|
|
WIDGET_MAP[field] = {"widget": cmb, "var": self.vars[field]}
|
|
|
|
elif ctrl_type == "spinbox":
|
|
number_format = self.info.get(f"number_format{i}", "float")
|
|
is_int = number_format == "integer"
|
|
self.vars[field] = tk.IntVar() if is_int else tk.DoubleVar()
|
|
self.editing_fields[field] = False # Initialize editing state
|
|
fmt = '%.0f' if is_int else '%.4f'
|
|
min_v = self.info.get(f"min_value{i}", -999999)
|
|
max_v = self.info.get(f"max_value{i}", 999999)
|
|
inc_v = self.info.get(f"increment{i}", 1)
|
|
|
|
sp = tk.Spinbox(
|
|
self, textvariable=self.vars[field], width=self.column_widths[i],
|
|
from_=min_v, to=max_v, increment=inc_v, format=fmt
|
|
)
|
|
sp.grid(row=0, column=i)
|
|
sp.bind("<Return>", lambda e, f=field, m=msg, idx=i: self._on_spin(f, m, idx))
|
|
sp.bind("<FocusOut>", lambda e, f=field, m=msg, idx=i: self._on_spin(f, m, idx))
|
|
# Track editing state
|
|
sp.bind("<FocusIn>", lambda e, f=field: self._set_editing(f, True))
|
|
sp.bind("<KeyPress>", lambda e, f=field: self._set_editing(f, True))
|
|
# Detect arrow button clicks
|
|
sp.bind("<ButtonRelease-1>", lambda e, f=field, m=msg, idx=i: self._on_spin_arrow(f, m, idx))
|
|
WIDGET_MAP[field] = {"widget": sp, "var": self.vars[field]}
|
|
|
|
elif ctrl_type == "label":
|
|
self.vars[field] = tk.StringVar(value="0")
|
|
lbl = tk.Label(
|
|
self, textvariable=self.vars[field], relief="groove",
|
|
width=self.column_widths[i], anchor="w", font=("Helvetica", 8)
|
|
)
|
|
lbl.grid(row=0, column=i)
|
|
|
|
elif ctrl_type == "space":
|
|
lbl = tk.Label(self, text="", width=self.column_widths[i])
|
|
lbl.grid(row=0, column=i)
|
|
|
|
def _set_editing(self, field: str, is_editing: bool):
|
|
"""Track editing state for a specific field"""
|
|
self.editing_fields[field] = is_editing
|
|
|
|
def _on_check(self, field: str, msg: Any):
|
|
val = 1 if self.vars[field].get() else 0
|
|
msg.set_value_for_field(field, val)
|
|
from pymsc.gui.script_manager import get_script_manager
|
|
sm = get_script_manager()
|
|
if sm.is_recording:
|
|
sm.write_command(field, "toggle", "on" if val else "off")
|
|
|
|
def _on_combo(self, field: str, msg: Any, idx: int):
|
|
cmb = WIDGET_MAP[field]["widget"]
|
|
val_idx = cmb.current()
|
|
msg.set_value_for_field(field, val_idx)
|
|
from pymsc.gui.script_manager import get_script_manager
|
|
sm = get_script_manager()
|
|
if sm.is_recording:
|
|
sm.write_command(field, "set_value", cmb.get())
|
|
|
|
def _on_spin(self, field: str, msg: Any, idx: int):
|
|
if self.is_programmatic_change:
|
|
return
|
|
val = float(self.vars[field].get())
|
|
raw = set_correct_value(self.info, idx, val)
|
|
msg.set_value_for_field(field, raw)
|
|
# Clear editing flag after change
|
|
self.editing_fields[field] = False
|
|
from pymsc.gui.script_manager import get_script_manager
|
|
sm = get_script_manager()
|
|
if sm.is_recording:
|
|
sm.write_command(field, "set_value", val)
|
|
|
|
def _on_spin_arrow(self, field: str, msg: Any, idx: int):
|
|
"""Handle spinbox arrow button clicks - send value immediately with small delay"""
|
|
# Small delay to ensure the value has been updated
|
|
self.after(50, lambda: self._send_spin_arrow_value(field, msg, idx))
|
|
|
|
def _send_spin_arrow_value(self, field: str, msg: Any, idx: int):
|
|
"""Send spinbox value after arrow click"""
|
|
if self.is_programmatic_change:
|
|
return
|
|
try:
|
|
val = float(self.vars[field].get())
|
|
raw = set_correct_value(self.info, idx, val)
|
|
msg.set_value_for_field(field, raw)
|
|
# Clear editing flag
|
|
self.editing_fields[field] = False
|
|
except (ValueError, tk.TclError):
|
|
pass
|
|
|
|
def check_updated_value(self):
|
|
"""
|
|
Syncs sub-controls with their corresponding 1553 message fields.
|
|
Renamed from check_actual_value for consistency with MainWindow refresh loop.
|
|
"""
|
|
self.is_programmatic_change = True
|
|
for i in range(1, 4):
|
|
ctrl_type = self.info.get(f"type{i}")
|
|
if not ctrl_type or ctrl_type == "space":
|
|
continue
|
|
field = self.info.get(f"field{i}")
|
|
msg = self.info.get(f"message{i}")
|
|
if not msg or not field:
|
|
continue
|
|
|
|
# Skip spinbox refresh if user is actively editing
|
|
if ctrl_type == "spinbox" and self.editing_fields.get(field, False):
|
|
continue
|
|
|
|
raw = msg.get_value_for_field(field)
|
|
readable = get_correct_value(self.info, i, raw)
|
|
if ctrl_type == "checkbox":
|
|
self.vars[field].set(bool(raw))
|
|
elif ctrl_type == "combobox":
|
|
try:
|
|
self.vars[field].set(self.info[f"values{i}"][int(raw)])
|
|
except IndexError:
|
|
self.vars[field].set("ERR")
|
|
elif ctrl_type == "spinbox":
|
|
self.vars[field].set(readable)
|
|
elif ctrl_type == "label":
|
|
self.vars[field].set("%.4f" % readable)
|
|
self.is_programmatic_change = False |