Use manifest defaults when adding components and refresh properties from device
- Populate prop elements with manifest default values when adding components in both offline and live mode - Send config values from manifest in SOX add command for live devices - Refresh button in properties panel now fetches fresh values from the connected device before re-reading the DOM - Properties panel fetches device values on open in live mode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ so later rebinding would only ever be seen by the module that did it.
|
||||
import configparser
|
||||
import os
|
||||
|
||||
VERSION = "0.0.0.108"
|
||||
VERSION = "0.0.0.113"
|
||||
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
|
||||
DEFAULT_GRID_SIZE = 10
|
||||
DEFAULT_ADD_TIMESTAMP = True
|
||||
|
||||
+4
-4
@@ -53,9 +53,10 @@ class DeviceSession:
|
||||
self._load_slot_registry(parts)
|
||||
|
||||
root_comp = self.sox_client.load(0)
|
||||
# Pre-load immediate children so the tree shows the first level
|
||||
if root_comp and root_comp.children:
|
||||
self.load_children(root_comp.id)
|
||||
children = self.load_children(root_comp.id)
|
||||
if children:
|
||||
self.sox_client.update(children, CONFIG | RUNTIME)
|
||||
return root_comp
|
||||
|
||||
def disconnect(self):
|
||||
@@ -283,8 +284,7 @@ class DeviceSession:
|
||||
|
||||
app_elem = ET.SubElement(sax_root, "app")
|
||||
if root_comp is not None:
|
||||
# Build children of root directly under <app>, matching .sax format
|
||||
# where <app> IS the root container (not a <comp>)
|
||||
app_elem.set("id", str(root_comp.id))
|
||||
for child_id in root_comp.children:
|
||||
child_comp = self.sox_client.cache_get(child_id)
|
||||
if child_comp is not None:
|
||||
|
||||
@@ -213,6 +213,12 @@ def _find_slot(ds, comp_id, slot_name):
|
||||
comp = ds.sox_client.cache_get(comp_id)
|
||||
if comp is None:
|
||||
return None
|
||||
type_name = ds.sox_client.type_name_map.get((comp.kit_id, comp.type_id))
|
||||
if type_name and type_name in app_state.final_components_registry:
|
||||
for s in app_state.final_components_registry[type_name]:
|
||||
if s["name"] == slot_name:
|
||||
return {"name": s["name"], "id": s["slot_id"],
|
||||
"type_name": s.get("type", "int")}
|
||||
slots = ds.sox_client.slot_registry.get((comp.kit_id, comp.type_id))
|
||||
if slots is None:
|
||||
return None
|
||||
|
||||
@@ -11,6 +11,7 @@ import app_state
|
||||
import connect_dialog
|
||||
import device_session
|
||||
import link_view
|
||||
import live_hooks
|
||||
import manage_kits
|
||||
import palette
|
||||
import properties
|
||||
@@ -191,7 +192,15 @@ def refresh_all_views():
|
||||
|
||||
def _component_in_document(comp):
|
||||
root = app_state.xml_root_element
|
||||
return root is not None and any(element is comp for element in root.iter())
|
||||
if root is None:
|
||||
return False
|
||||
if any(element is comp for element in root.iter()):
|
||||
return True
|
||||
if app_state.live_mode:
|
||||
comp_id = comp.get("id")
|
||||
if comp_id is not None:
|
||||
return any(e.get("id") == comp_id for e in root.iter())
|
||||
return False
|
||||
|
||||
|
||||
def delete_component(comp):
|
||||
@@ -1329,10 +1338,16 @@ def on_canvas_release(event):
|
||||
if orig_x != raw_x or orig_y != raw_y:
|
||||
meta_before = app_state.drag_data.get("meta_before", meta_str)
|
||||
update_meta_property(comp_element, meta_str, raw_x, raw_y)
|
||||
undo.push(undo.MetaMoveCommand(
|
||||
[(comp_element, meta_before, raw_x, raw_y)],
|
||||
label=f"move {comp_element.get('name', 'component')}"
|
||||
))
|
||||
if live_hooks.is_live():
|
||||
new_meta = comp_element.find("prop[@name='meta']")
|
||||
if new_meta is not None:
|
||||
live_hooks.live_set_prop_value(
|
||||
comp_element, "meta", new_meta.get("val", "0"))
|
||||
if not app_state.live_mode:
|
||||
undo.push(undo.MetaMoveCommand(
|
||||
[(comp_element, meta_before, raw_x, raw_y)],
|
||||
label=f"move {comp_element.get('name', 'component')}"
|
||||
))
|
||||
app_state.mark_dirty()
|
||||
|
||||
app_state.drag_data["tag"] = None
|
||||
|
||||
+46
-6
@@ -518,6 +518,8 @@ class PropertiesPanel:
|
||||
# sibling nothing at all.
|
||||
self._build_header(comp_type)
|
||||
self._build_footer()
|
||||
if app_state.live_mode and app_state.device_session is not None:
|
||||
self._refresh_from_device()
|
||||
self._loading = True
|
||||
self._build_rows_area()
|
||||
self._loading = False
|
||||
@@ -1056,7 +1058,13 @@ class PropertiesPanel:
|
||||
# -- the two buttons --
|
||||
|
||||
def refresh(self):
|
||||
"""Re-reads every value from the DOM, discarding edits that were not saved."""
|
||||
"""Re-reads every value from the DOM, discarding edits that were not saved.
|
||||
|
||||
In live mode, fetches fresh values from the device first.
|
||||
"""
|
||||
if app_state.live_mode and app_state.device_session is not None:
|
||||
self._refresh_from_device()
|
||||
|
||||
self._loading = True
|
||||
for row in self.rows:
|
||||
self._load_row(row)
|
||||
@@ -1066,7 +1074,30 @@ class PropertiesPanel:
|
||||
self._load_meta_row()
|
||||
self._load_path()
|
||||
self._update_tint()
|
||||
self.status_var.set("Refreshed from the document.")
|
||||
if app_state.live_mode:
|
||||
self.status_var.set("Refreshed from device.")
|
||||
else:
|
||||
self.status_var.set("Refreshed from the document.")
|
||||
|
||||
def _refresh_from_device(self):
|
||||
"""Reads fresh config+runtime values from the device into the DOM element."""
|
||||
from sox import CONFIG, RUNTIME
|
||||
ds = app_state.device_session
|
||||
comp_id = self.comp.get("id")
|
||||
if comp_id is None:
|
||||
return
|
||||
try:
|
||||
comp_id = int(comp_id)
|
||||
except ValueError:
|
||||
return
|
||||
sox_comp = ds.sox_client.cache_get(comp_id)
|
||||
if sox_comp is None:
|
||||
return
|
||||
try:
|
||||
ds.sox_client.update([sox_comp], CONFIG | RUNTIME)
|
||||
except Exception:
|
||||
pass
|
||||
ds.update_element_from_cache(comp_id, app_state.xml_root_element)
|
||||
|
||||
def save(self):
|
||||
"""Writes the edited rows back, as one undoable step.
|
||||
@@ -1137,9 +1168,9 @@ class PropertiesPanel:
|
||||
for element, prop_name, _before, after in changes:
|
||||
if prop_name != "meta":
|
||||
set_prop_value(element, prop_name, after)
|
||||
if live_hooks.is_live():
|
||||
if not live_hooks.live_set_prop_value(element, prop_name, after):
|
||||
live_failures.append(prop_name)
|
||||
if live_hooks.is_live():
|
||||
if not live_hooks.live_set_prop_value(element, prop_name, after):
|
||||
live_failures.append(prop_name)
|
||||
|
||||
if not app_state.live_mode:
|
||||
undo.push(undo.SlotValueCommand(list(changes)))
|
||||
@@ -1174,7 +1205,16 @@ class PropertiesPanel:
|
||||
root = app_state.xml_root_element
|
||||
if root is None:
|
||||
return False
|
||||
return any(element is self.comp for element in root.iter())
|
||||
if any(element is self.comp for element in root.iter()):
|
||||
return True
|
||||
if app_state.live_mode:
|
||||
comp_id = self.comp.get("id")
|
||||
if comp_id is not None:
|
||||
for element in root.iter():
|
||||
if element.get("id") == comp_id:
|
||||
self.comp = element
|
||||
return True
|
||||
return False
|
||||
|
||||
def _sync_bar(self, bar, first, last):
|
||||
"""Moves a scrollbar's thumb, and hides the bar entirely when it spans the whole."""
|
||||
|
||||
+45
-1
@@ -8,6 +8,7 @@ import xml.etree.ElementTree as ET
|
||||
import app_state
|
||||
import live_hooks
|
||||
import palette
|
||||
import properties
|
||||
import schema
|
||||
import undo
|
||||
import wiresheet
|
||||
@@ -69,6 +70,7 @@ def refresh_treeview_from_dom(xml_root):
|
||||
|
||||
app_element = xml_root.find("app")
|
||||
if app_element is not None:
|
||||
app_state.tree_element_map[root_node] = app_element
|
||||
parse_xml_to_tree(tree, root_node, app_element)
|
||||
else:
|
||||
parse_xml_to_tree(tree, root_node, xml_root)
|
||||
@@ -754,6 +756,28 @@ def reconcile_schema_kits_before_save():
|
||||
return True
|
||||
|
||||
|
||||
def _build_config_values(full_type):
|
||||
"""Builds SOX config_values list from manifest defaults for an add command."""
|
||||
from sox import type_id_for_slot
|
||||
from live_hooks import _coerce_value
|
||||
|
||||
slots = app_state.final_components_registry.get(full_type, [])
|
||||
config_values = []
|
||||
for slot in slots:
|
||||
if "c" not in slot["flags"]:
|
||||
continue
|
||||
if slot["type"] == "void":
|
||||
continue
|
||||
val = slot["default"]
|
||||
if val is None:
|
||||
val = properties.type_zero(slot["type"])
|
||||
if val is None:
|
||||
continue
|
||||
tid = type_id_for_slot(slot["type"])
|
||||
config_values.append((tid, _coerce_value(tid, val)))
|
||||
return config_values
|
||||
|
||||
|
||||
def add_component(kit_name, type_name, grid_x, grid_y):
|
||||
"""Places a new component of kit::type at a grid position. Returns the element."""
|
||||
parent = current_sheet_container()
|
||||
@@ -784,7 +808,10 @@ def add_component(kit_name, type_name, grid_x, grid_y):
|
||||
comp_name = unique_child_name(parent, type_name)
|
||||
|
||||
if live_hooks.is_live():
|
||||
new_id = live_hooks.live_add_component(parent, kit_name, type_name, comp_name)
|
||||
full_type = f"{kit_name}::{type_name}"
|
||||
config_values = _build_config_values(full_type)
|
||||
new_id = live_hooks.live_add_component(
|
||||
parent, kit_name, type_name, comp_name, config_values or None)
|
||||
if new_id is None:
|
||||
return None
|
||||
comp_id = new_id
|
||||
@@ -796,6 +823,23 @@ def add_component(kit_name, type_name, grid_x, grid_y):
|
||||
element.set("id", str(comp_id))
|
||||
element.set("type", f"{kit_name}::{type_name}")
|
||||
|
||||
full_type = f"{kit_name}::{type_name}"
|
||||
slots = app_state.final_components_registry.get(full_type, [])
|
||||
for slot in slots:
|
||||
flags = slot["flags"]
|
||||
if "a" in flags or "r" in flags:
|
||||
continue
|
||||
if slot["type"] == "void":
|
||||
continue
|
||||
val = slot["default"]
|
||||
if val is None:
|
||||
val = properties.type_zero(slot["type"])
|
||||
if val is None:
|
||||
continue
|
||||
prop = ET.SubElement(element, "prop")
|
||||
prop.set("name", slot["name"])
|
||||
prop.set("val", val)
|
||||
|
||||
index = len(list(parent))
|
||||
parent.append(element)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user