sistemati anche i tooltip dei messaggi B
This commit is contained in:
parent
ac843839bb
commit
7d15cf143a
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"window_geometry": "1600x1099+52+52",
|
"window_geometry": "1600x1099+298+121",
|
||||||
"main_sash_position": [
|
"main_sash_position": [
|
||||||
1,
|
1,
|
||||||
793
|
793
|
||||||
|
|||||||
@ -481,6 +481,11 @@ class MonitorApp(tk.Frame):
|
|||||||
row_fr.pack(fill=tk.X, padx=2, pady=1)
|
row_fr.pack(fill=tk.X, padx=2, pady=1)
|
||||||
lbl = tk.Label(row_fr, text=name, width=28, anchor=tk.W)
|
lbl = tk.Label(row_fr, text=name, width=28, anchor=tk.W)
|
||||||
lbl.pack(side=tk.LEFT)
|
lbl.pack(side=tk.LEFT)
|
||||||
|
# attach tooltip to the label itself (description will be added later for widgets)
|
||||||
|
try:
|
||||||
|
from .monitor_helpers import UNSET_TEXT
|
||||||
|
except Exception:
|
||||||
|
UNSET_TEXT = '<unset>'
|
||||||
# determine per-field editability: make mission date/time readonly
|
# determine per-field editability: make mission date/time readonly
|
||||||
root_field = full.split('.')[0]
|
root_field = full.split('.')[0]
|
||||||
readonly_roots = ('date_of_mission', 'time_of_mission')
|
readonly_roots = ('date_of_mission', 'time_of_mission')
|
||||||
@ -524,6 +529,21 @@ class MonitorApp(tk.Frame):
|
|||||||
if editable:
|
if editable:
|
||||||
cb.bind('<<ComboboxSelected>>', lambda e, path=full: self._on_field_changed(path))
|
cb.bind('<<ComboboxSelected>>', lambda e, path=full: self._on_field_changed(path))
|
||||||
cb.pack(side=tk.RIGHT, fill=tk.X, expand=True)
|
cb.pack(side=tk.RIGHT, fill=tk.X, expand=True)
|
||||||
|
# attach tooltip showing message-field and unit when available
|
||||||
|
try:
|
||||||
|
msg_label = getattr(self, 'current_form_label', '')
|
||||||
|
unit = None
|
||||||
|
try:
|
||||||
|
unit = self._find_unit_for_field(msg_label, name)
|
||||||
|
except Exception:
|
||||||
|
unit = None
|
||||||
|
tt = f"{msg_label} - {name}"
|
||||||
|
if unit:
|
||||||
|
tt = f"{tt} ({unit})"
|
||||||
|
from pymsc.gui.components.base_widgets import ToolTip
|
||||||
|
ToolTip(cb, tt)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
widget = ('combobox', cb, enum_items)
|
widget = ('combobox', cb, enum_items)
|
||||||
else:
|
else:
|
||||||
# numeric or text entry
|
# numeric or text entry
|
||||||
@ -550,6 +570,21 @@ class MonitorApp(tk.Frame):
|
|||||||
ent.bind('<Return>', lambda e, path=full: self._on_entry_finished(path))
|
ent.bind('<Return>', lambda e, path=full: self._on_entry_finished(path))
|
||||||
ent.bind('<FocusOut>', lambda e, path=full: self._on_entry_finished(path))
|
ent.bind('<FocusOut>', lambda e, path=full: self._on_entry_finished(path))
|
||||||
ent.pack(side=tk.RIGHT, fill=tk.X, expand=True)
|
ent.pack(side=tk.RIGHT, fill=tk.X, expand=True)
|
||||||
|
# attach tooltip showing message-field and unit when available
|
||||||
|
try:
|
||||||
|
msg_label = getattr(self, 'current_form_label', '')
|
||||||
|
unit = None
|
||||||
|
try:
|
||||||
|
unit = self._find_unit_for_field(msg_label, name)
|
||||||
|
except Exception:
|
||||||
|
unit = None
|
||||||
|
tt = f"{msg_label} - {name}"
|
||||||
|
if unit:
|
||||||
|
tt = f"{tt} ({unit})"
|
||||||
|
from pymsc.gui.components.base_widgets import ToolTip
|
||||||
|
ToolTip(ent, tt)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
widget = ('entry', ent)
|
widget = ('entry', ent)
|
||||||
|
|
||||||
# register created widget for later updates; values will be filled
|
# register created widget for later updates; values will be filled
|
||||||
@ -559,6 +594,69 @@ class MonitorApp(tk.Frame):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _find_unit_for_field(self, message_label: str, field_name: str):
|
||||||
|
"""Find unit string for given message label and field by inspecting command registry."""
|
||||||
|
try:
|
||||||
|
import pymsc.gui.command_registry as reg
|
||||||
|
from pymsc.utils import converters as conv
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
lists = [
|
||||||
|
getattr(reg, 'CHECKBOXES', []),
|
||||||
|
getattr(reg, 'COMBOBOXES', []),
|
||||||
|
getattr(reg, 'SPINBOXES', []),
|
||||||
|
getattr(reg, 'LIST_CONTROLS', []),
|
||||||
|
getattr(reg, 'LABELS', [])
|
||||||
|
]
|
||||||
|
|
||||||
|
for lst in lists:
|
||||||
|
for item in lst:
|
||||||
|
# check up to 3 message/field pairs
|
||||||
|
for i in (1, 2, 3):
|
||||||
|
mkey = f"message{i}" if f"message{i}" in item else ('message' if 'message' in item and i == 1 else None)
|
||||||
|
fkey = f"field{i}" if f"field{i}" in item else ('field' if 'field' in item and i == 1 else None)
|
||||||
|
if not mkey or not fkey:
|
||||||
|
continue
|
||||||
|
msgobj = item.get(mkey)
|
||||||
|
fieldreg = item.get(fkey)
|
||||||
|
if not msgobj or not fieldreg:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
mid = getattr(msgobj, 'message_id', None)
|
||||||
|
except Exception:
|
||||||
|
mid = None
|
||||||
|
if mid != message_label:
|
||||||
|
continue
|
||||||
|
# compare field names by suffix
|
||||||
|
try:
|
||||||
|
if isinstance(fieldreg, str) and (fieldreg.lower().endswith(field_name.lower()) or fieldreg.lower().endswith('_' + field_name.lower())):
|
||||||
|
# found matching registry item - determine unit based on lsb/scale
|
||||||
|
# look for lsb/scale keys
|
||||||
|
lsb = item.get(f"lsb{i}", item.get('lsb'))
|
||||||
|
scale = item.get(f"scale{i}", item.get('scale', 1))
|
||||||
|
# map known scales to units
|
||||||
|
if scale == getattr(conv, 'RAD_TO_DEG', None):
|
||||||
|
return 'deg'
|
||||||
|
if scale == getattr(conv, 'MS_TO_KNOTS', None):
|
||||||
|
return 'kts'
|
||||||
|
if scale == getattr(conv, 'METERS_TO_FEET', None):
|
||||||
|
return 'ft'
|
||||||
|
# check lsb-based heuristics
|
||||||
|
if lsb == getattr(conv, 'SEMICIRCLE_RAD_LSB', None) or lsb == getattr(conv, 'SEMICIRCLE_LSB', None):
|
||||||
|
return 'deg' if scale == getattr(conv, 'RAD_TO_DEG', None) else 'rad'
|
||||||
|
if lsb == getattr(conv, 'GEOPOS_RAD_LSB', None):
|
||||||
|
return 'deg'
|
||||||
|
if lsb == getattr(conv, 'CRS_SLAVE_RNG_METERS_LSB', None) or 'range' in field_name.lower() or 'lat' in field_name.lower() or 'lon' in field_name.lower():
|
||||||
|
# assume distance - if scale converts to feet, show ft otherwise meters
|
||||||
|
if scale == getattr(conv, 'METERS_TO_FEET', None):
|
||||||
|
return 'ft'
|
||||||
|
return 'm'
|
||||||
|
return None
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return None
|
||||||
|
|
||||||
def _on_entry_focus_in(self, field_path):
|
def _on_entry_focus_in(self, field_path):
|
||||||
"""Mark entry as being edited to prevent refresh from overwriting user input."""
|
"""Mark entry as being edited to prevent refresh from overwriting user input."""
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -432,7 +432,7 @@ LIST_CONTROLS = [
|
|||||||
"message2": msg_a4, "field2": "a4_radio_altimeter_invalid", "type2": "checkbox", "label2": "inv", "tooltip2": "inv"
|
"message2": msg_a4, "field2": "a4_radio_altimeter_invalid", "type2": "checkbox", "label2": "inv", "tooltip2": "inv"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "attitude", "label": "Invalid", "description": "Set attitude invalid",
|
"command": "attitude", "label": "Attitude invalid", "description": "Set attitude invalid",
|
||||||
"message1": msg_a4, "field1": "ATTITUDE_INVALID", "type1": "checkbox", "tooltip1": "A4 ATT INV"
|
"message1": msg_a4, "field1": "ATTITUDE_INVALID", "type1": "checkbox", "tooltip1": "A4 ATT INV"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -11,6 +11,44 @@ from pymsc.utils.converters import (
|
|||||||
FEET_TO_METERS, MS_TO_KNOTS, KNOTS_TO_MS
|
FEET_TO_METERS, MS_TO_KNOTS, KNOTS_TO_MS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _infer_unit_from_info(info: Dict, idx: int):
|
||||||
|
"""Infer a short unit string from a command registry `info` dict.
|
||||||
|
|
||||||
|
Looks for `lsb{idx}` / `scale{idx}` or fallback to `lsb`/`scale`.
|
||||||
|
Returns unit like 'deg', 'rad', 'kts', 'm', 'ft' or None.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
suffix = '' if idx == -1 or idx == 1 else str(idx)
|
||||||
|
lsb = info.get(f'lsb{suffix}', info.get('lsb'))
|
||||||
|
scale = info.get(f'scale{suffix}', info.get('scale'))
|
||||||
|
# Angle in degrees if scale==RAD_TO_DEG
|
||||||
|
if scale == RAD_TO_DEG:
|
||||||
|
return 'deg'
|
||||||
|
# Speed in knots
|
||||||
|
if scale == MS_TO_KNOTS:
|
||||||
|
return 'kts'
|
||||||
|
# Feet/meter mapping
|
||||||
|
if scale == METERS_TO_FEET:
|
||||||
|
return 'ft'
|
||||||
|
# heuristics based on lsb
|
||||||
|
from pymsc.utils import converters as conv
|
||||||
|
if lsb in (getattr(conv, 'SEMICIRCLE_RAD_LSB', None), getattr(conv, 'SEMICIRCLE_LSB', None)):
|
||||||
|
# if a scale converts to degrees, label deg, else rad
|
||||||
|
if scale == RAD_TO_DEG:
|
||||||
|
return 'deg'
|
||||||
|
return 'rad'
|
||||||
|
if lsb == getattr(conv, 'GEOPOS_RAD_LSB', None):
|
||||||
|
return 'deg'
|
||||||
|
if lsb in (getattr(conv, 'CRS_SLAVE_RNG_METERS_LSB', None), getattr(conv, 'VELOCITY_METERS_LSB', None)):
|
||||||
|
# distance or velocity in meters -> show 'm' or 'ft' depending on scale
|
||||||
|
if scale == METERS_TO_FEET:
|
||||||
|
return 'ft'
|
||||||
|
return 'm'
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
# Registry for finding widgets by their label (used for automation/scripts)
|
# Registry for finding widgets by their label (used for automation/scripts)
|
||||||
WIDGET_MAP: Dict[str, Any] = {}
|
WIDGET_MAP: Dict[str, Any] = {}
|
||||||
|
|
||||||
@ -61,6 +99,20 @@ class CommandFrameCheckBox(BaseCommandFrame):
|
|||||||
width=self.column_widths[1]
|
width=self.column_widths[1]
|
||||||
)
|
)
|
||||||
self.command_check.grid(row=0, column=1)
|
self.command_check.grid(row=0, column=1)
|
||||||
|
# attach tooltip on the interactive control: "<message_id> - <field>"
|
||||||
|
try:
|
||||||
|
msg_obj = self.info.get('message')
|
||||||
|
if msg_obj and hasattr(msg_obj, 'message_id'):
|
||||||
|
tt = f"{msg_obj.message_id} - {self.info.get('field','')}"
|
||||||
|
# append short unit to tooltip for editable control
|
||||||
|
unit = _infer_unit_from_info(self.info, 1)
|
||||||
|
if unit:
|
||||||
|
tt = f"{tt} ({unit})"
|
||||||
|
else:
|
||||||
|
tt = self.info.get('tooltip', self.info.get('description',''))
|
||||||
|
ToolTip(self.command_check, tt)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
initial_val = self.info['message'].get_value_for_field(self.info['field'])
|
initial_val = self.info['message'].get_value_for_field(self.info['field'])
|
||||||
self.command_var.set(bool(initial_val))
|
self.command_var.set(bool(initial_val))
|
||||||
|
|
||||||
@ -71,6 +123,20 @@ class CommandFrameCheckBox(BaseCommandFrame):
|
|||||||
width=self.column_widths[2]
|
width=self.column_widths[2]
|
||||||
)
|
)
|
||||||
self.tellback_check.grid(row=0, column=2)
|
self.tellback_check.grid(row=0, column=2)
|
||||||
|
# attach tooltip to tellback showing its source message-field (B-message)
|
||||||
|
try:
|
||||||
|
msg_tb = self.info.get('message_tb')
|
||||||
|
field_tb = self.info.get('field_tb')
|
||||||
|
if msg_tb and hasattr(msg_tb, 'message_id') and field_tb:
|
||||||
|
tt_tb = f"{msg_tb.message_id} - {field_tb}"
|
||||||
|
unit_tb = _infer_unit_from_info(self.info, -1)
|
||||||
|
if unit_tb:
|
||||||
|
tt_tb = f"{tt_tb} ({unit_tb})"
|
||||||
|
else:
|
||||||
|
tt_tb = self.info.get('tooltip_tb', self.info.get('description',''))
|
||||||
|
ToolTip(self.tellback_check, tt_tb)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def on_toggle(self):
|
def on_toggle(self):
|
||||||
# Skip if message is None (disabled field)
|
# Skip if message is None (disabled field)
|
||||||
@ -137,6 +203,16 @@ class CommandFrameComboBox(BaseCommandFrame):
|
|||||||
)
|
)
|
||||||
self.combo.grid(row=0, column=1)
|
self.combo.grid(row=0, column=1)
|
||||||
self.combo.bind("<<ComboboxSelected>>", self.on_select)
|
self.combo.bind("<<ComboboxSelected>>", self.on_select)
|
||||||
|
# attach tooltip to combobox showing message-field when possible
|
||||||
|
try:
|
||||||
|
msg_obj = self.info.get('message')
|
||||||
|
if msg_obj and hasattr(msg_obj, 'message_id'):
|
||||||
|
tt = f"{msg_obj.message_id} - {self.info.get('field','')}"
|
||||||
|
else:
|
||||||
|
tt = self.info.get('tooltip', self.info.get('description',''))
|
||||||
|
ToolTip(self.combo, tt)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
if self.info.get('message_tb'):
|
if self.info.get('message_tb'):
|
||||||
self.tb_var = tk.StringVar(value="---")
|
self.tb_var = tk.StringVar(value="---")
|
||||||
@ -145,6 +221,20 @@ class CommandFrameComboBox(BaseCommandFrame):
|
|||||||
width=self.column_widths[2], relief="sunken"
|
width=self.column_widths[2], relief="sunken"
|
||||||
)
|
)
|
||||||
self.tb_label.grid(row=0, column=2)
|
self.tb_label.grid(row=0, column=2)
|
||||||
|
# tooltip for tellback cell: show B-message.field mapping
|
||||||
|
try:
|
||||||
|
msg_tb = self.info.get('message_tb')
|
||||||
|
field_tb = self.info.get('field_tb')
|
||||||
|
if msg_tb and hasattr(msg_tb, 'message_id') and field_tb:
|
||||||
|
tt_tb = f"{msg_tb.message_id} - {field_tb}"
|
||||||
|
unit_tb = _infer_unit_from_info(self.info, -1)
|
||||||
|
if unit_tb:
|
||||||
|
tt_tb = f"{tt_tb} ({unit_tb})"
|
||||||
|
else:
|
||||||
|
tt_tb = self.info.get('tooltip_tb', self.info.get('description',''))
|
||||||
|
ToolTip(self.tb_label, tt_tb)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def on_select(self, event=None):
|
def on_select(self, event=None):
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
@ -171,7 +261,12 @@ class CommandFrameComboBox(BaseCommandFrame):
|
|||||||
val_tb_text = self.info["values_tb"][idx_tb]
|
val_tb_text = self.info["values_tb"][idx_tb]
|
||||||
except (IndexError, KeyError, TypeError):
|
except (IndexError, KeyError, TypeError):
|
||||||
val_tb_text = "ERR"
|
val_tb_text = "ERR"
|
||||||
self.tb_var.set(val_tb_text)
|
# append unit if available (combobox tellbacks are typically enums, but keep flexible)
|
||||||
|
unit = _infer_unit_from_info(self.info, -1)
|
||||||
|
if unit:
|
||||||
|
self.tb_var.set(f"{val_tb_text} ({unit})")
|
||||||
|
else:
|
||||||
|
self.tb_var.set(val_tb_text)
|
||||||
elapsed = (time.time() - self.start_time) * 1000
|
elapsed = (time.time() - self.start_time) * 1000
|
||||||
if idx_tb == self.combo.current():
|
if idx_tb == self.combo.current():
|
||||||
self.label.config(bg="green")
|
self.label.config(bg="green")
|
||||||
@ -242,6 +337,17 @@ class CommandFrameSpinBox(BaseCommandFrame):
|
|||||||
# Also use variable trace to detect arrow changes
|
# Also use variable trace to detect arrow changes
|
||||||
self.cmd_var.trace_add("write", self._on_var_change)
|
self.cmd_var.trace_add("write", self._on_var_change)
|
||||||
|
|
||||||
|
# attach tooltip to spinbox showing message-field when possible
|
||||||
|
try:
|
||||||
|
msg_obj = self.info.get('message')
|
||||||
|
if msg_obj and hasattr(msg_obj, 'message_id'):
|
||||||
|
tt = f"{msg_obj.message_id} - {self.info.get('field','')}"
|
||||||
|
else:
|
||||||
|
tt = self.info.get('tooltip', self.info.get('description',''))
|
||||||
|
ToolTip(self.spin, tt)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
if self.info.get('message_tb'):
|
if self.info.get('message_tb'):
|
||||||
self.tb_var = tk.StringVar(value="0")
|
self.tb_var = tk.StringVar(value="0")
|
||||||
self.tb_label = tk.Label(
|
self.tb_label = tk.Label(
|
||||||
@ -249,6 +355,20 @@ class CommandFrameSpinBox(BaseCommandFrame):
|
|||||||
width=self.column_widths[2], relief="sunken"
|
width=self.column_widths[2], relief="sunken"
|
||||||
)
|
)
|
||||||
self.tb_label.grid(row=0, column=2)
|
self.tb_label.grid(row=0, column=2)
|
||||||
|
# tooltip for spinbox tellback cell
|
||||||
|
try:
|
||||||
|
msg_tb = self.info.get('message_tb')
|
||||||
|
field_tb = self.info.get('field_tb')
|
||||||
|
if msg_tb and hasattr(msg_tb, 'message_id') and field_tb:
|
||||||
|
tt_tb = f"{msg_tb.message_id} - {field_tb}"
|
||||||
|
unit_tb = _infer_unit_from_info(self.info, -1)
|
||||||
|
if unit_tb:
|
||||||
|
tt_tb = f"{tt_tb} ({unit_tb})"
|
||||||
|
else:
|
||||||
|
tt_tb = self.info.get('tooltip_tb', self.info.get('description',''))
|
||||||
|
ToolTip(self.tb_label, tt_tb)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def _validate(self, p):
|
def _validate(self, p):
|
||||||
if p == "" or p == "-":
|
if p == "" or p == "-":
|
||||||
@ -335,7 +455,16 @@ class CommandFrameSpinBox(BaseCommandFrame):
|
|||||||
raw_tb = msg_tb.get_value_for_field(self.info['field_tb'])
|
raw_tb = msg_tb.get_value_for_field(self.info['field_tb'])
|
||||||
readable_tb = get_correct_value(self.info, -1, raw_tb)
|
readable_tb = get_correct_value(self.info, -1, raw_tb)
|
||||||
fmt = "%.0f" if self.info.get("number_format") == "integer" else "%.4f"
|
fmt = "%.0f" if self.info.get("number_format") == "integer" else "%.4f"
|
||||||
self.tb_var.set(fmt % readable_tb)
|
# append unit indication
|
||||||
|
unit = _infer_unit_from_info(self.info, -1)
|
||||||
|
try:
|
||||||
|
txt = fmt % readable_tb
|
||||||
|
except Exception:
|
||||||
|
txt = str(readable_tb)
|
||||||
|
if unit:
|
||||||
|
self.tb_var.set(f"{txt} ({unit})")
|
||||||
|
else:
|
||||||
|
self.tb_var.set(txt)
|
||||||
elapsed = (time.time() - self.start_time) * 1000
|
elapsed = (time.time() - self.start_time) * 1000
|
||||||
target = float(self.cmd_var.get())
|
target = float(self.cmd_var.get())
|
||||||
if abs(readable_tb - target) < 0.0001:
|
if abs(readable_tb - target) < 0.0001:
|
||||||
@ -412,7 +541,16 @@ class CommandFrameLabels(tk.Frame):
|
|||||||
raw = msg.get_value_for_field(field)
|
raw = msg.get_value_for_field(field)
|
||||||
val = get_correct_value(self.info, i, raw)
|
val = get_correct_value(self.info, i, raw)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
self.vars[field].set("%.4f" % val)
|
fmt = "%.0f" if self.info.get(f"number_format{i}") == "integer" else "%.4f"
|
||||||
|
unit = _infer_unit_from_info(self.info, i)
|
||||||
|
try:
|
||||||
|
txt = fmt % val
|
||||||
|
except Exception:
|
||||||
|
txt = str(val)
|
||||||
|
if unit:
|
||||||
|
self.vars[field].set(f"{txt} ({unit})")
|
||||||
|
else:
|
||||||
|
self.vars[field].set(txt)
|
||||||
else:
|
else:
|
||||||
self.vars[field].set("N/A")
|
self.vars[field].set("N/A")
|
||||||
|
|
||||||
@ -456,6 +594,15 @@ class CommandFrameControls(tk.Frame):
|
|||||||
command=lambda f=field, m=msg: self._on_check(f, m)
|
command=lambda f=field, m=msg: self._on_check(f, m)
|
||||||
)
|
)
|
||||||
cb.grid(row=0, column=i)
|
cb.grid(row=0, column=i)
|
||||||
|
# attach tooltip to checkbox
|
||||||
|
try:
|
||||||
|
if msg and hasattr(msg, 'message_id'):
|
||||||
|
tt = f"{msg.message_id} - {field}"
|
||||||
|
else:
|
||||||
|
tt = self.info.get(f"tooltip{i}", self.info.get('description',''))
|
||||||
|
ToolTip(cb, tt)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
WIDGET_MAP[field] = {"widget": cb, "var": self.vars[field]}
|
WIDGET_MAP[field] = {"widget": cb, "var": self.vars[field]}
|
||||||
|
|
||||||
elif ctrl_type == "combobox":
|
elif ctrl_type == "combobox":
|
||||||
@ -467,6 +614,18 @@ class CommandFrameControls(tk.Frame):
|
|||||||
)
|
)
|
||||||
cmb.grid(row=0, column=i)
|
cmb.grid(row=0, column=i)
|
||||||
cmb.bind("<<ComboboxSelected>>", lambda e, f=field, m=msg, idx=i: self._on_combo(f, m, idx))
|
cmb.bind("<<ComboboxSelected>>", lambda e, f=field, m=msg, idx=i: self._on_combo(f, m, idx))
|
||||||
|
# attach tooltip to combobox
|
||||||
|
try:
|
||||||
|
if msg and hasattr(msg, 'message_id'):
|
||||||
|
tt = f"{msg.message_id} - {field}"
|
||||||
|
unit = _infer_unit_from_info(self.info, i)
|
||||||
|
if unit:
|
||||||
|
tt = f"{tt} ({unit})"
|
||||||
|
else:
|
||||||
|
tt = self.info.get(f"tooltip{i}", self.info.get('description',''))
|
||||||
|
ToolTip(cmb, tt)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
WIDGET_MAP[field] = {"widget": cmb, "var": self.vars[field]}
|
WIDGET_MAP[field] = {"widget": cmb, "var": self.vars[field]}
|
||||||
|
|
||||||
elif ctrl_type == "spinbox":
|
elif ctrl_type == "spinbox":
|
||||||
@ -492,6 +651,18 @@ class CommandFrameControls(tk.Frame):
|
|||||||
# Detect arrow button clicks
|
# Detect arrow button clicks
|
||||||
sp.bind("<ButtonRelease-1>", lambda e, f=field, m=msg, idx=i: self._on_spin_arrow(f, m, idx))
|
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]}
|
WIDGET_MAP[field] = {"widget": sp, "var": self.vars[field]}
|
||||||
|
# attach tooltip to spinbox (include unit if available)
|
||||||
|
try:
|
||||||
|
if msg and hasattr(msg, 'message_id'):
|
||||||
|
tt = f"{msg.message_id} - {field}"
|
||||||
|
else:
|
||||||
|
tt = self.info.get(f"tooltip{i}", self.info.get('description',''))
|
||||||
|
unit = _infer_unit_from_info(self.info, i)
|
||||||
|
if unit:
|
||||||
|
tt = f"{tt} ({unit})"
|
||||||
|
ToolTip(sp, tt)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
elif ctrl_type == "label":
|
elif ctrl_type == "label":
|
||||||
self.vars[field] = tk.StringVar(value="0")
|
self.vars[field] = tk.StringVar(value="0")
|
||||||
@ -586,7 +757,12 @@ class CommandFrameControls(tk.Frame):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
self.vars[field].set("ERR")
|
self.vars[field].set("ERR")
|
||||||
elif ctrl_type == "spinbox":
|
elif ctrl_type == "spinbox":
|
||||||
self.vars[field].set(readable)
|
# Do not place unit inside editable spinbox value; just format number
|
||||||
|
fmt = '%.0f' if self.info.get(f"number_format{i}") == "integer" else '%.4f'
|
||||||
|
try:
|
||||||
|
self.vars[field].set(fmt % readable)
|
||||||
|
except Exception:
|
||||||
|
self.vars[field].set(readable)
|
||||||
elif ctrl_type == "label":
|
elif ctrl_type == "label":
|
||||||
self.vars[field].set("%.4f" % readable)
|
self.vars[field].set("%.4f" % readable)
|
||||||
self.is_programmatic_change = False
|
self.is_programmatic_change = False
|
||||||
@ -26,7 +26,7 @@ class MissionPage(ScrollableFrame):
|
|||||||
self.f2 = ttk.LabelFrame(self.scrollable_content, text="Attitude (SNU)")
|
self.f2 = ttk.LabelFrame(self.scrollable_content, text="Attitude (SNU)")
|
||||||
self.f2.grid(row=1, column=0, padx=10, pady=5, sticky="nsew")
|
self.f2.grid(row=1, column=0, padx=10, pady=5, sticky="nsew")
|
||||||
atts = [
|
atts = [
|
||||||
"attitude", "true_heading", "magnetic_heading",
|
"attitude", "nav_data_invalid", "true_heading", "magnetic_heading",
|
||||||
"platform_azimuth", "yaw_snu", "pitch_snu", "roll_snu"
|
"platform_azimuth", "yaw_snu", "pitch_snu", "roll_snu"
|
||||||
]
|
]
|
||||||
for a in atts:
|
for a in atts:
|
||||||
@ -50,7 +50,7 @@ class MissionPage(ScrollableFrame):
|
|||||||
# 5. Velocities
|
# 5. Velocities
|
||||||
self.f5 = ttk.LabelFrame(self.scrollable_content, text="Velocities")
|
self.f5 = ttk.LabelFrame(self.scrollable_content, text="Velocities")
|
||||||
self.f5.grid(row=1, column=1, padx=10, pady=5, sticky="nsew")
|
self.f5.grid(row=1, column=1, padx=10, pady=5, sticky="nsew")
|
||||||
vels = ["tas", "cas", "nav_data_invalid", "vx/vy/vz", "accx/accy/accz", "nx/ny/nz", "wind"]
|
vels = ["tas", "cas", "vx/vy/vz", "accx/accy/accz", "nx/ny/nz", "wind"]
|
||||||
for v in vels:
|
for v in vels:
|
||||||
create_widget_by_id(self.f5, v).pack(fill=tk.X, padx=5, pady=2)
|
create_widget_by_id(self.f5, v).pack(fill=tk.X, padx=5, pady=2)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user