From 44f0abbb56537e7d3b8efd3675970c4a39092953 Mon Sep 17 00:00:00 2001 From: Shane Ringrose Date: Fri, 12 Jun 2026 05:25:44 +1200 Subject: [PATCH] CLI: add --set-titleblock to stamp title-block fields headlessly The first write-to-project CLI command, aimed at CI / revision workflows: stamp title-block metadata onto every folio (and the project default), then save. Each argument is key=value: qelectrotech --set-titleblock in.qet out.qet revision=B date=today Standard keys map to the documented title-block fields (title, author, date, plant, location, revision, version, filename); date=today uses the current date and an explicit date forces UseDateValue mode; any other key is stored as a custom title-block field. Assignments are parsed up front so a malformed one fails before writing. Addresses the 'saving' side of the CLI-for-scripts request (#162). --- sources/cli_export.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++ sources/cli_export.h | 5 +++ 2 files changed, 97 insertions(+) diff --git a/sources/cli_export.cpp b/sources/cli_export.cpp index ecad24d80..c5215e9bb 100644 --- a/sources/cli_export.cpp +++ b/sources/cli_export.cpp @@ -28,6 +28,7 @@ #include "qetgraphicsitem/element.h" #include "qetgraphicsitem/terminal.h" #include "qetproject.h" +#include "titleblockproperties.h" #include "wiringlistexport.h" // Private Qt PDF engine for drawHyperlink() — see pdf_links / projectprintwindow. @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +75,7 @@ const QHash &exportFlags() {"--info", "info"}, {"--check-elements", "check"}, {"--resave", "resave"}, + {"--set-titleblock", "settb"}, }; return flags; } @@ -652,6 +656,92 @@ int resaveProject(QETProject &project, const QString &output) return 0; } +/// Stamp title-block fields onto every folio (and the project default), then +/// save. Each assignment is "key=value". Standard keys map to the documented +/// title-block fields; "date=today" uses the current date; any other key is +/// stored as a custom title-block field. Aimed at CI/revision workflows +/// (e.g. set revision + date before exporting a new revision). +int setTitleBlock(QETProject &project, const QString &output, + const QStringList &assignments) +{ + if (assignments.isEmpty()) { + err << "No field assignments given (expected key=value).\n"; + return 2; + } + + // Parse "key=value" assignments up front so a bad one fails before writing. + QList> fields; + for (const QString &a : assignments) { + const int eq = a.indexOf('='); + if (eq <= 0) { + err << "Bad assignment '" << a << "' (expected key=value).\n"; + return 2; + } + const QString key = a.left(eq); + const QString val = a.mid(eq + 1); + if (key.compare("date", Qt::CaseInsensitive) == 0 + && val.compare("today", Qt::CaseInsensitive) != 0 + && !QDate::fromString(val, Qt::ISODate).isValid()) { + err << "Bad date '" << val << "' (expected YYYY-MM-DD or 'today').\n"; + return 2; + } + fields << qMakePair(key, val); + } + + auto apply = [&](TitleBlockProperties &p) { + for (const auto &f : fields) { + const QString k = f.first.toLower(); + const QString &v = f.second; + if (k == "title") p.title = v; + else if (k == "author") p.author = v; + else if (k == "filename") p.filename = v; + else if (k == "plant") p.plant = v; + else if (k == "location") p.locmach = v; + else if (k == "revision") p.indexrev = v; + else if (k == "version") p.version = v; + else if (k == "date") { + p.date = (v.compare("today", Qt::CaseInsensitive) == 0) + ? QDate::currentDate() + : QDate::fromString(v, Qt::ISODate); + // An explicit date is only honoured when the folio is in + // "use the date value" mode (not "now"/"null"). + p.useDate = TitleBlockProperties::UseDateValue; + } + else // unknown key -> custom title-block field + p.context.addValue(f.first, v); + } + }; + + // Project default (the template applied to new folios). + TitleBlockProperties def = project.defaultTitleBlockProperties(); + apply(def); + project.setDefaultTitleBlockProperties(def); + + // Every existing folio's own title block. + int folios = 0; + const QList diagrams = project.diagrams(); + for (Diagram *diagram : diagrams) { + TitleBlockProperties p = + diagram->border_and_titleblock.exportTitleBlock(); + apply(p); + diagram->border_and_titleblock.importTitleBlock(p); + ++folios; + } + + const QDomDocument doc = project.toXml(); + QFile file(output); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + err << "Cannot open '" << output << "' for writing.\n"; + return 1; + } + QTextStream fout(&file); + fout << doc.toString(4); + file.close(); + out << "Stamped " << fields.size() << " field(s) on " + << folios << " folio(s) -> " << output << "\n"; + return 0; +} + } // anonymous namespace namespace CLIExport { @@ -727,6 +817,8 @@ int run(const QStringList &args) return exportLinks(project, output); if (format == "resave") return resaveProject(project, output); + if (format == "settb") + return setTitleBlock(project, output, rest.mid(2)); return exportImages(project, format, output); } diff --git a/sources/cli_export.h b/sources/cli_export.h index 5abe194ca..2354fbfc1 100644 --- a/sources/cli_export.h +++ b/sources/cli_export.h @@ -53,6 +53,7 @@ namespace CLIExport { qelectrotech --info [output.json] qelectrotech --check-elements qelectrotech --resave + qelectrotech --set-titleblock key=value... PDF: one multi-page document (one diagram per page). PNG/SVG: one file per diagram, named /_.<ext>. @@ -66,6 +67,10 @@ namespace CLIExport { per-page element / conductor counts and unconnected terminals. check-elements: validate .elmt file(s) against the element schema. resave: load and rewrite the project XML (round-trip integrity). + set-titleblock: stamp title-block fields onto every folio, then save. + Keys: title, author, date (or date=today), plant, location, + revision, version, filename; any other key becomes a custom + field. E.g. --set-titleblock in.qet out.qet revision=B date=today */ int run(const QStringList &args);