mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-06-13 16:23:14 +02:00
Merge pull request #493 from ispyisail/cli-set-titleblock
CLI: add --set-titleblock, and fix headless backup crash
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
#include "qetgraphicsitem/element.h"
|
#include "qetgraphicsitem/element.h"
|
||||||
#include "qetgraphicsitem/terminal.h"
|
#include "qetgraphicsitem/terminal.h"
|
||||||
#include "qetproject.h"
|
#include "qetproject.h"
|
||||||
|
#include "titleblockproperties.h"
|
||||||
#include "wiringlistexport.h"
|
#include "wiringlistexport.h"
|
||||||
|
|
||||||
// Private Qt PDF engine for drawHyperlink() — see pdf_links / projectprintwindow.
|
// Private Qt PDF engine for drawHyperlink() — see pdf_links / projectprintwindow.
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QDomDocument>
|
#include <QDomDocument>
|
||||||
|
#include <QDate>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
@@ -43,6 +45,7 @@
|
|||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QPageLayout>
|
#include <QPageLayout>
|
||||||
|
#include <QPair>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPdfWriter>
|
#include <QPdfWriter>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
@@ -72,6 +75,7 @@ const QHash<QString, QString> &exportFlags()
|
|||||||
{"--info", "info"},
|
{"--info", "info"},
|
||||||
{"--check-elements", "check"},
|
{"--check-elements", "check"},
|
||||||
{"--resave", "resave"},
|
{"--resave", "resave"},
|
||||||
|
{"--set-titleblock", "settb"},
|
||||||
};
|
};
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
@@ -652,6 +656,92 @@ int resaveProject(QETProject &project, const QString &output)
|
|||||||
return 0;
|
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<QPair<QString, QString>> 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<Diagram *> 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
|
} // anonymous namespace
|
||||||
|
|
||||||
namespace CLIExport {
|
namespace CLIExport {
|
||||||
@@ -727,6 +817,8 @@ int run(const QStringList &args)
|
|||||||
return exportLinks(project, output);
|
return exportLinks(project, output);
|
||||||
if (format == "resave")
|
if (format == "resave")
|
||||||
return resaveProject(project, output);
|
return resaveProject(project, output);
|
||||||
|
if (format == "settb")
|
||||||
|
return setTitleBlock(project, output, rest.mid(2));
|
||||||
return exportImages(project, format, output);
|
return exportImages(project, format, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ namespace CLIExport {
|
|||||||
qelectrotech --info <project.qet> [output.json]
|
qelectrotech --info <project.qet> [output.json]
|
||||||
qelectrotech --check-elements <element.elmt | directory>
|
qelectrotech --check-elements <element.elmt | directory>
|
||||||
qelectrotech --resave <project.qet> <output.qet>
|
qelectrotech --resave <project.qet> <output.qet>
|
||||||
|
qelectrotech --set-titleblock <project.qet> <output.qet> key=value...
|
||||||
|
|
||||||
PDF: one multi-page document (one diagram per page).
|
PDF: one multi-page document (one diagram per page).
|
||||||
PNG/SVG: one file per diagram, named <output_dir>/<NN>_<title>.<ext>.
|
PNG/SVG: one file per diagram, named <output_dir>/<NN>_<title>.<ext>.
|
||||||
@@ -66,6 +67,10 @@ namespace CLIExport {
|
|||||||
per-page element / conductor counts and unconnected terminals.
|
per-page element / conductor counts and unconnected terminals.
|
||||||
check-elements: validate .elmt file(s) against the element schema.
|
check-elements: validate .elmt file(s) against the element schema.
|
||||||
resave: load and rewrite the project XML (round-trip integrity).
|
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);
|
int run(const QStringList &args);
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "machine_info.h"
|
#include "machine_info.h"
|
||||||
#include "qet.h"
|
#include "qet.h"
|
||||||
#include "qetapp.h"
|
#include "qetapp.h"
|
||||||
|
#include "qetproject.h"
|
||||||
#include "singleapplication.h"
|
#include "singleapplication.h"
|
||||||
#include "utils/macosxopenevent.h"
|
#include "utils/macosxopenevent.h"
|
||||||
#include "utils/qetsettings.h"
|
#include "utils/qetsettings.h"
|
||||||
@@ -206,6 +207,10 @@ QGuiApplication::setHighDpiScaleFactorRoundingPolicy(QetSettings::hdpiScaleFacto
|
|||||||
raw_args << QString::fromLocal8Bit(argv[i]);
|
raw_args << QString::fromLocal8Bit(argv[i]);
|
||||||
if (CLIExport::isExportRequest(raw_args)) {
|
if (CLIExport::isExportRequest(raw_args)) {
|
||||||
QApplication export_app(argc, argv);
|
QApplication export_app(argc, argv);
|
||||||
|
// No crash-recovery backups in one-shot CLI mode: the backup write
|
||||||
|
// runs on a background thread referencing the project and races the
|
||||||
|
// process exit (intermittent segfault in QET::writeToFile).
|
||||||
|
QETProject::setBackupEnabled(false);
|
||||||
return CLIExport::run(export_app.arguments());
|
return CLIExport::run(export_app.arguments());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,13 @@
|
|||||||
|
|
||||||
static int BACKUP_INTERVAL = 1200000; //interval in ms of backup = 20min
|
static int BACKUP_INTERVAL = 1200000; //interval in ms of backup = 20min
|
||||||
|
|
||||||
|
bool QETProject::m_backup_enabled = true;
|
||||||
|
|
||||||
|
void QETProject::setBackupEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
m_backup_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief QETProject::QETProject
|
@brief QETProject::QETProject
|
||||||
Create a empty project
|
Create a empty project
|
||||||
@@ -1783,6 +1790,8 @@ void QETProject::addDiagram(Diagram *diagram, int pos)
|
|||||||
*/
|
*/
|
||||||
void QETProject::writeBackup()
|
void QETProject::writeBackup()
|
||||||
{
|
{
|
||||||
|
if (!m_backup_enabled)
|
||||||
|
return;
|
||||||
#ifdef BUILD_WITHOUT_KF5
|
#ifdef BUILD_WITHOUT_KF5
|
||||||
#else
|
#else
|
||||||
# if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
|
# if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
|
||||||
|
|||||||
@@ -105,6 +105,12 @@ class QETProject : public QObject
|
|||||||
QVersionNumber declaredQElectroTechVersion();
|
QVersionNumber declaredQElectroTechVersion();
|
||||||
void setTitle(const QString &);
|
void setTitle(const QString &);
|
||||||
|
|
||||||
|
/// Enable/disable the asynchronous crash-recovery backup for all
|
||||||
|
/// projects. Disabled by the headless CLI: the backup write runs on a
|
||||||
|
/// background thread referencing the project, and a short-lived CLI
|
||||||
|
/// process can destroy the project before the write finishes (crash).
|
||||||
|
static void setBackupEnabled(bool enabled);
|
||||||
|
|
||||||
///DEFAULT PROPERTIES
|
///DEFAULT PROPERTIES
|
||||||
BorderProperties defaultBorderProperties() const;
|
BorderProperties defaultBorderProperties() const;
|
||||||
void setDefaultBorderProperties(const BorderProperties &);
|
void setDefaultBorderProperties(const BorderProperties &);
|
||||||
@@ -241,6 +247,8 @@ class QETProject : public QObject
|
|||||||
|
|
||||||
// attributes
|
// attributes
|
||||||
private:
|
private:
|
||||||
|
/// When false, writeBackup() is a no-op (set by the headless CLI)
|
||||||
|
static bool m_backup_enabled;
|
||||||
/// File path this project is saved to
|
/// File path this project is saved to
|
||||||
QString m_file_path;
|
QString m_file_path;
|
||||||
/// Current state of the project
|
/// Current state of the project
|
||||||
|
|||||||
Reference in New Issue
Block a user