From 04716b0731408d8c1e38c7a474bead9555ef818f Mon Sep 17 00:00:00 2001 From: DaTweety <129504390+DaTweety@users.noreply.github.com> Date: Sat, 17 Oct 2026 16:57:21 +0200 Subject: [PATCH] Scripting and qet MCP: draw a cable across conductors qet.addCable(folio, designation, type, coreColors, length, x1, y1, x2, y2) does what the cable tool does after its dialog: a new cable whose trunk is the line between the two points, every conductor the line crosses taking the next core in order along the line, with the colour coreColors lists at that place. One undo step, through AddCableCommand. An empty designation takes the next free name. A conductor another cable already holds is refused rather than taken away, since a script cannot be asked the question the dialog asks. qet.cables() lists the project's cables. The qet MCP server gains the add_cable op for qet_edit. Co-Authored-By: Claude Opus 5.5 --- misc/qet-mcp/qet_mcp.py | 9 ++- misc/qet-mcp/test_qet_mcp.py | 3 + sources/scripting/qetscriptapi.cpp | 106 +++++++++++++++++++++++++++++ sources/scripting/qetscriptapi.h | 7 ++ 4 files changed, 124 insertions(+), 1 deletion(-) diff --git a/misc/qet-mcp/qet_mcp.py b/misc/qet-mcp/qet_mcp.py index 7de720d62..e6d392a1c 100755 --- a/misc/qet-mcp/qet_mcp.py +++ b/misc/qet-mcp/qet_mcp.py @@ -1581,6 +1581,13 @@ OPS = { "remove_folio": ("removeFolio", [("folio", "folio")]), "set_folio": ("setFolioProperty", [("folio", "folio"), ("property", "str"), ("value", "str")]), + # A cable drawn across conductors, as the cable tool draws it: every + # conductor the line crosses gets the next core and its colour. + "add_cable": ("addCable", [("folio", "folio"), ("designation", "str"), + ("type", "str"), ("core_colors", "list"), + ("length", "str"), + ("x1", "num"), ("y1", "num"), + ("x2", "num"), ("y2", "num")]), "add_terminal_strip": ("addTerminalStrip", [("installation", "str"), ("location", "str"), ("name", "str")]), "remove_terminal_strip": ("removeTerminalStrip", [("strip", "folio")]), @@ -5142,7 +5149,7 @@ TOOLS = [ "it to one element AFTER it is placed (add_element does not number " "what it places); slaves and reports are refused, since they take " "their label from their master. " - "Terminal strips: add_terminal_strip returns an index (name " + "Cables: add_cable (folio, designation -- empty for the next free name --, type, core_colors, length, x1, y1, x2, y2) draws a new cable's trunk line across the conductors between the two points, as the cable tool does: each crossed conductor, in order along the line, gets the next core and the colour core_colors lists at that place; a conductor another cable already holds makes the op fail. Returns the cable's uuid. Terminal strips: add_terminal_strip returns an index (name " "it \"$id\"); add_to_strip puts a terminal-type element on " "it, and refuses any other kind. group_terminals/bridge_terminals " "take \"indices\" (at least two) into that strip's real-terminal " diff --git a/misc/qet-mcp/test_qet_mcp.py b/misc/qet-mcp/test_qet_mcp.py index 3ada780c3..434f38ee3 100644 --- a/misc/qet-mcp/test_qet_mcp.py +++ b/misc/qet-mcp/test_qet_mcp.py @@ -437,6 +437,9 @@ class EditValidation(unittest.TestCase): "set_element_text": el + [{"op": "set_element_text", "folio": "$f", "element": "$e", "index": 0, "property": "x", "value": "5"}], "delete_element_text": el + [{"op": "delete_element_text", "folio": "$f", "element": "$e", "index": 0}], + "add_cable": [f, {"op": "add_cable", "id": "k", "folio": "$f", "designation": "-W1", + "type": "NYM-J 3x1,5", "core_colors": ["BN", "BU", "GNYE"], + "length": "2 m", "x1": 0, "y1": 0, "x2": 100, "y2": 0}], "add_terminal_strip": [{"op": "add_terminal_strip", "installation": "a", "location": "b", "name": "c"}], "remove_terminal_strip": [{"op": "remove_terminal_strip", "strip": 0}], "add_to_strip": el + [{"op": "add_to_strip", "strip": 0, "folio": "$f", "element": "$e"}], diff --git a/sources/scripting/qetscriptapi.cpp b/sources/scripting/qetscriptapi.cpp index e40151823..24fcbaaad 100644 --- a/sources/scripting/qetscriptapi.cpp +++ b/sources/scripting/qetscriptapi.cpp @@ -102,6 +102,10 @@ #include #include #include +#include "../cable/addcablecommand.h" +#include "../cable/cable.h" +#include "../cable/cablemanager.h" +#include "../cable/cablepart.h" #include #include #include @@ -2262,6 +2266,108 @@ int QetScriptApi::addShape(int folioIndex, const QString &type, return sortedShapes(folioIndex).indexOf(shape); } +/** + @brief QetScriptApi::addCable + Draw a cable across conductors, as the cable tool does: the line from + (x1, y1) to (x2, y2) on the folio becomes the cable's trunk, every + conductor it crosses gets the next core, in order along the line, and + with it the colour @p coreColors lists at that place. The cable is a + new one, named @p designation (the next free name when empty), of + type @p type, as long as @p length says. One undo step, through the + same AddCableCommand the tool pushes. + A conductor another cable already holds is not taken away from it: + the call is refused instead, since a script cannot be asked, as the + tool's dialog asks, whether to move it. + @return the cable's uuid, or an empty string +*/ +QString QetScriptApi::addCable(int folioIndex, const QString &designation, + const QString &type, const QStringList &coreColors, + const QString &length, + double x1, double y1, double x2, double y2) +{ + if (!m_project) return QString(); + if (m_project->isReadOnly()) { + log(QStringLiteral("qet.addCable: project is read-only")); + return QString(); + } + const QList diagrams = m_project->folios(); + if (folioIndex < 0 || folioIndex >= diagrams.count()) return QString(); + Diagram *diagram = built(diagrams.at(folioIndex)); + + const QLineF line(QPointF(x1, y1), QPointF(x2, y2)); + const QList crossings = CableManager::crossedConductors(diagram, line); + if (crossings.isEmpty()) { + log(QStringLiteral("qet.addCable: the line crosses no conductor")); + return QString(); + } + for (const CableCrossing &crossing : crossings) { + if (crossing.conductor && !crossing.conductor->properties().m_cable_uuid.isNull()) { + log(QStringLiteral("qet.addCable: a crossed conductor already belongs to a cable")); + return QString(); + } + } + + Cable *cable = m_project->newCable(); + cable->setType(type); + cable->setCoreCount(qMax(int(coreColors.size()), int(crossings.size()))); + cable->setCoreColors(coreColors); + cable->setLength(length); + if (designation.trimmed().isEmpty()) { + cable->setDesignation(CableManager::nextDesignation(m_project, diagram)); + } else { + cable->setDesignation(designation.trimmed()); + cable->setDesignationByHand(true); + } + + CablePartData part; + part.uuid = QUuid::createUuid(); + part.diagram = diagram->uuid(); + part.p1 = line.p1(); + part.p2 = line.p2(); + cable->addPart(part); + + int core_number = 0; + for (const CableCrossing &crossing : crossings) + { + Conductor *conductor = crossing.conductor; + if (!conductor) continue; + CableManager::claimConductor(m_project, conductor, cable); + //A cable binds a conductor by its identity (@see + //CableCreateDialog::assignCrossings) + conductor->setUuid(conductor->uuid()); + CableCore core; + core.core = core_number++; + core.part = part.uuid; + core.conductor = conductor->uuid(); + core.position = crossing.position; + cable->setCore(core); + } + + auto *item = new CablePart(part); + item->setCable(cable); + diagram->addItem(item); + m_project->undoStack()->push(new AddCableCommand(cable, item, diagram, true)); + return cable->uuid().toString(); +} + +/** + @brief QetScriptApi::cables + @return one line per cable of the project: designation, type, cores + used of cores, length and uuid +*/ +QStringList QetScriptApi::cables() const +{ + QStringList out; + if (!m_project) return out; + for (Cable *cable : m_project->cables()) { + out << QStringLiteral("%1: %2, %3/%4 cores, %5 %6") + .arg(cable->designation(), cable->type()) + .arg(cable->usedCoreCount()).arg(cable->coreCount()) + .arg(cable->length(), cable->uuid().toString()); + } + return out; +} + namespace { const QStringList &shapePropertyNames() { diff --git a/sources/scripting/qetscriptapi.h b/sources/scripting/qetscriptapi.h index 9d0c9c7b7..13b6a61ca 100644 --- a/sources/scripting/qetscriptapi.h +++ b/sources/scripting/qetscriptapi.h @@ -490,6 +490,13 @@ class QetScriptApi : public QObject Q_INVOKABLE bool setTextRotation(int folioIndex, int textIndex, double angle); Q_INVOKABLE bool deleteText(int folioIndex, int textIndex); + // -- cables -- + Q_INVOKABLE QString addCable(int folioIndex, const QString &designation, + const QString &type, const QStringList &coreColors, + const QString &length, + double x1, double y1, double x2, double y2); + Q_INVOKABLE QStringList cables() const; + Q_INVOKABLE QStringList shapes(int folioIndex) const; Q_INVOKABLE int shapeIndex(int folioIndex, const QString &uuid) const; Q_INVOKABLE int addShape(int folioIndex, const QString &type,