qet-mcp: set a folio's conductor defaults (set_conductor_default)

The "one text per potential" switch (onetextperfolio) lives in a folio's
conductor defaults, which no scripting call or qet_edit op could reach, so
callers patched the saved .qet afterwards (#1178).

qet.setConductorDefault(folio, property, value) sets onetextperfolio or any
setConductorProperty name on a folio's defaults, or with folio -1 on the
project's defaults that new folios copy. A change to onetextperfolio
re-shows or hides the conductor texts at once, as the Folio properties
dialog does. Not on the undo stack, like both dialogs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-02 07:16:44 +13:00
parent b6495b51a6
commit 7df9052a47
5 changed files with 141 additions and 0 deletions
+7
View File
@@ -487,6 +487,13 @@ Python, plus the hang guard on `addConductor` and the database refresh in
at names none of them and is refused, so address a potential from one of
its leaves. Property names are the file's own, so `qet_conductors` reads
back exactly what was set.
- **`set_conductor_default` sets a folio's conductor defaults**, the
Conductors tab of Folio properties: `onetextperfolio` (`"true"` shows one
wire number per potential on the folio), or any `set_conductor` property,
which conductors drawn later on that folio start from. `"folio": -1` sets
the project's defaults instead, which each folio added afterwards copies;
it does not change existing folios. Like the dialogs, it is not on the
undo stack.
- **`link_elements` takes a folio for each end**, because a master and its
slave are normally on different folios. Whether a pair may be linked is
decided by QElectroTech's own `isLinkable()`, so a script cannot make a
+19
View File
@@ -1425,6 +1425,10 @@ OPS = {
"set_project_title": ("setProjectTitle", [("title", "str")]),
"set_folio_border": ("setFolioBorder", [("folio", "folio"), ("property", "str"),
("value", "str")]),
# A folio's conductor defaults, or with "folio": -1 the project's ones
# that each new folio copies.
"set_conductor_default": ("setConductorDefault", [("folio", "folio"), ("property", "str"),
("value", "str")]),
"embed_title_block_template": ("embedTitleBlockTemplate", [("name", "str")]),
"duplicate_elements": ("duplicateElements", [("folio", "folio"), ("elements", "elmts"),
("to_folio", "folio"), ("x", "num"), ("y", "num")]),
@@ -1499,6 +1503,10 @@ CONDUCTOR_PROPERTIES = ["num", "formula", "function", "bus", "cable",
"color2", "bicolor", "style", "dash-size",
"condsize", "numsize", "displaytext"]
# A folio's conductor defaults take the same names, plus the folio-wide
# "one text per potential" switch, which no single conductor carries.
CONDUCTOR_DEFAULT_PROPERTIES = ["onetextperfolio"] + CONDUCTOR_PROPERTIES
# Methods this tool needs that only exist in a build carrying the drawing
# verbs. Probed in the script rather than assumed, because the failure mode
# otherwise is a TypeError on line N of a generated file the caller never
@@ -1752,6 +1760,11 @@ def _build_script(operations: list, output: str) -> str:
if op.get("kind") == "element_info" and not op.get("field"):
raise ValueError(f"operation {i}: element_info needs a non-empty "
f"\"field\" (information key)")
if (name == "set_conductor_default"
and op.get("property") not in CONDUCTOR_DEFAULT_PROPERTIES):
raise ValueError(f"operation {i}: unknown conductor default "
f"{op.get('property')!r}; expected one of "
f"{', '.join(CONDUCTOR_DEFAULT_PROPERTIES)}")
if name == "set_folio_border" and op.get("property") not in FOLIO_BORDER_PROPERTIES:
raise ValueError(f"operation {i}: unknown folio border property "
f"{op.get('property')!r}; expected one of "
@@ -2840,6 +2853,12 @@ TOOLS = [
"set_folio_border sets one "
"of the folio frame's " + ", ".join(FOLIO_BORDER_PROPERTIES) +
" (counts 1-99, sizes 1-1000, display-* true/false). "
"set_conductor_default sets one of a folio's conductor defaults "
"(Folio properties > Conductors): onetextperfolio (true/false, one "
"wire number per potential on the folio) or any set_conductor "
"property, which new conductors on that folio start from; \"folio\": "
"-1 sets the project's defaults that each folio added later copies. "
"Not on the undo stack. "
"embed_title_block_template copies a template into the project from "
"the common/company/custom collection that has it (only reachable if "
"the binary's compiled-in template path resolves to something real -- "
+26
View File
@@ -401,6 +401,8 @@ class EditValidation(unittest.TestCase):
"regex": False, "case_sensitive": True}],
"set_project_title": [{"op": "set_project_title", "title": "T"}],
"set_folio_border": [f, {"op": "set_folio_border", "folio": "$f", "property": "columns", "value": "10"}],
"set_conductor_default": [f, {"op": "set_conductor_default", "folio": "$f",
"property": "onetextperfolio", "value": "true"}],
"embed_title_block_template": [{"op": "embed_title_block_template", "name": "default"}],
"duplicate_elements": el + [{"op": "duplicate_elements", "id": "d", "folio": "$f",
"elements": ["$e"], "to_folio": "$f", "x": 50, "y": 50}],
@@ -525,6 +527,8 @@ class EditValidation(unittest.TestCase):
("unknown kind", {"op": "search_and_replace", "kind": "folio", "field": "x",
"pattern": "a", "replacement": "b", "regex": False,
"case_sensitive": False}),
("unknown conductor default", {"op": "set_conductor_default", "folio": 0,
"property": "label", "value": "x"}),
("unknown conductor field", {"op": "search_and_replace", "kind": "conductor",
"field": "label", "pattern": "a", "replacement": "b",
"regex": False, "case_sensitive": False}),
@@ -3685,6 +3689,28 @@ class Integration(unittest.TestCase):
with self.subTest(prop=prop, value=good):
self.ok(self.sb.edit(base, [{"op": "set_folio_border", "folio": 0, "property": prop, "value": good}]))
def test_conductor_defaults(self):
"""The folio's "one text per potential" switch (#1178), and the
project's defaults that a folio added later copies, as saved."""
base = self.sb.new(folios=1)
r = self.ok(self.sb.edit(base, [
{"op": "set_conductor_default", "folio": 0, "property": "onetextperfolio", "value": "true"},
{"op": "set_conductor_default", "folio": 0, "property": "conductor_section", "value": "1.5"},
{"op": "set_conductor_default", "folio": -1, "property": "onetextperfolio", "value": "true"},
{"op": "add_folio"}]))
root = ET.parse(r["output"]).getroot()
folios = [d.find("defaultconductor") for d in root.iter("diagram")]
self.assertEqual([d.get("onetextperfolio") for d in folios], ["1", "1"],
"set on folio 0; copied from the project's defaults by the new folio")
self.assertEqual(folios[0].get("conductor_section"), "1.5")
self.assertEqual(root.find("newdiagrams/conductors").get("onetextperfolio"), "1")
for prop, bad in (("onetextperfolio", "yes"), ("numsize", "0"), ("color", "notacolour")):
with self.subTest(prop=prop, value=bad):
self.assertFalse(self.sb.edit(base, [{"op": "set_conductor_default", "folio": 0,
"property": prop, "value": bad}])["ok"])
self.assertFalse(self.sb.edit(base, [{"op": "set_conductor_default", "folio": 5,
"property": "onetextperfolio", "value": "true"}])["ok"])
# ---- duplicating ----
def test_duplicate_returns_copies_in_the_order_asked(self):