diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6440cc257..60e606b81 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,6 +109,22 @@ if(QT_VERSION_MAJOR GREATER_EQUAL 6)
endif()
endif()
+# JavaScript scripting (bugtracker #162: `--run script.js`, and later a
+# "Run Script..." menu action) needs QJSEngine, in the Qml module. As with
+# QtPdf above, this is not fatal when missing: some minimal Qt6 packagings
+# may not ship it, and scripting is optional functionality nothing else in
+# the application depends on. When it's missing the feature is silently
+# disabled - see the QET_HAS_SCRIPTING guard in qetscripting.*.
+set(QET_HAS_SCRIPTING FALSE)
+find_package(Qt${QT_VERSION_MAJOR} QUIET COMPONENTS Qml)
+if(TARGET Qt${QT_VERSION_MAJOR}::Qml)
+ set(QET_HAS_SCRIPTING TRUE)
+ list(APPEND QET_PRIVATE_LIBRARIES Qt::Qml)
+ add_compile_definitions(QET_HAS_SCRIPTING)
+else()
+ message(STATUS "Qt Qml module not available: JavaScript scripting (--run) disabled")
+endif()
+
find_package(SQLite3 REQUIRED)
# CMake < 4.3 only creates the SQLite::SQLite3 target (no SQLite3::SQLite3
diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake
index a8654b903..189011bf5 100644
--- a/cmake/qet_compilation_vars.cmake
+++ b/cmake/qet_compilation_vars.cmake
@@ -849,6 +849,20 @@ if(QT_VERSION_MAJOR GREATER_EQUAL 6)
)
endif()
+# JavaScript scripting (bugtracker #162). Unconditionally in the source
+# list, like the QtPdf files above: this file is included before the
+# QET_HAS_SCRIPTING probe runs in the top-level CMakeLists.txt, so the
+# variable isn't set yet here. Same pattern as QtPdf: always compiled, the
+# actual Qt::Qml dependent code is behind #ifdef QET_HAS_SCRIPTING inside
+# qetscripting.cpp/qetscriptapi.cpp themselves, compiling to a harmless
+# stub when the module wasn't found.
+list(APPEND QET_SRC_FILES
+ ${QET_DIR}/sources/scripting/qetscriptapi.cpp
+ ${QET_DIR}/sources/scripting/qetscriptapi.h
+ ${QET_DIR}/sources/scripting/qetscripting.cpp
+ ${QET_DIR}/sources/scripting/qetscripting.h
+)
+
set(TS_FILES
${QET_DIR}/lang/qet_ar.ts
${QET_DIR}/lang/qet_ca.ts
diff --git a/sources/main.cpp b/sources/main.cpp
index a6590ec93..b8dabc258 100644
--- a/sources/main.cpp
+++ b/sources/main.cpp
@@ -16,6 +16,9 @@
along with QElectroTech. If not, see .
*/
#include "cli_export.h"
+#ifdef QET_HAS_SCRIPTING
+#include "scripting/qetscripting.h"
+#endif
#include "logging/eventloopwatchdog.h"
#include "logging/qetlogger.h"
#include "machine_info.h"
@@ -135,6 +138,17 @@ QGuiApplication::setHighDpiScaleFactorRoundingPolicy(QetSettings::hdpiScaleFacto
QET::QetMessageBox::setNonInteractive(true);
return CLIExport::run(export_app.arguments());
}
+#ifdef QET_HAS_SCRIPTING
+ // Headless scripting: --run (bugtracker
+ // #162). Same reasoning as the export branch above for running
+ // before SingleApplication and answering message boxes headlessly.
+ if (QetScripting::isRunRequest(raw_args)) {
+ QApplication script_app(argc, argv);
+ QETProject::setBackupEnabled(false);
+ QET::QetMessageBox::setNonInteractive(true);
+ return QetScripting::run(script_app.arguments());
+ }
+#endif
}
// Resolve the logger's state (log directory, session filename, open
diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp
index 7c21742c4..d7f668e62 100644
--- a/sources/qetdiagrameditor.cpp
+++ b/sources/qetdiagrameditor.cpp
@@ -16,6 +16,9 @@
along with QElectroTech. If not, see .
*/
#include "qetdiagrameditor.h"
+#ifdef QET_HAS_SCRIPTING
+#include "scripting/qetscripting.h"
+#endif
#include
#include "ElementsCollection/elementscollectionwidget.h"
#include "QWidgetAnimation/qwidgetanimation.h"
@@ -549,6 +552,15 @@ void QETDiagramEditor::setUpActions()
m_terminal_numbering = new QAction(QET::Icons::TerminalStrip, tr("Numérotation automatique des bornes"), this);
connect(m_terminal_numbering, &QAction::triggered, this, &QETDiagramEditor::slot_terminalNumbering);
+#ifdef QET_HAS_SCRIPTING
+ // Run a JavaScript macro against the current project (bugtracker #162).
+ m_run_script = new QAction(tr("Exécuter un script..."), this);
+ m_run_script->setStatusTip(
+ tr("Exécute un script JavaScript sur le projet courant (voir qet.*"
+ " dans le script pour l'API disponible)"));
+ connect(m_run_script, &QAction::triggered, this, &QETDiagramEditor::slot_runScript);
+#endif
+
#ifdef QET_EXPORT_PROJECT_DB
m_export_project_db = new QAction(QET::Icons::DocumentSpreadsheet, tr("Exporter la base de donnée interne du projet"), this);
connect(m_export_project_db, &QAction::triggered, [this]() {
@@ -1002,6 +1014,9 @@ void QETDiagramEditor::setUpMenu()
menu_project -> addAction(m_project_export_wiring_list);
menu_project -> addAction(m_project_wiring_list_view);
menu_project -> addAction(m_terminal_numbering);
+#ifdef QET_HAS_SCRIPTING
+ menu_project -> addAction(m_run_script);
+#endif
#ifdef QET_EXPORT_PROJECT_DB
menu_project -> addSeparator();
menu_project -> addAction(m_export_project_db);
@@ -1856,6 +1871,9 @@ void QETDiagramEditor::slot_updateActions()
m_project_export_wiring_list -> setEnabled(opened_project);
m_project_wiring_list_view -> setEnabled(opened_project);
m_terminal_numbering -> setEnabled(editable_project);
+#ifdef QET_HAS_SCRIPTING
+ m_run_script -> setEnabled(opened_project);
+#endif
#ifdef QET_EXPORT_PROJECT_DB
m_export_project_db -> setEnabled(editable_project);
#endif
@@ -2970,3 +2988,29 @@ void QETDiagramEditor::slot_terminalNumbering() {
}
}
}
+
+#ifdef QET_HAS_SCRIPTING
+/**
+ @brief QETDiagramEditor::slot_runScript
+ Run a JavaScript macro against the current project (bugtracker #162).
+ See QetScriptApi for what a script can do -- read-mostly: folio/element/
+ conductor counts and the same export operations the --export-* CLI
+ flags provide. Export/save calls inside the script act on this
+ project's file on disk, so unsaved edits in the open editor are not
+ visible to the script; save first if that matters.
+*/
+void QETDiagramEditor::slot_runScript() {
+ QETProject *project = currentProject();
+ if (!project) return;
+
+ const QString script_path = QFileDialog::getOpenFileName(
+ this,
+ tr("Exécuter un script"),
+ QString(),
+ tr("Scripts JavaScript (*.js);;Tous les fichiers (*)")
+ );
+ if (script_path.isEmpty()) return;
+
+ QetScripting::runOnProject(script_path, project);
+}
+#endif
diff --git a/sources/qetdiagrameditor.h b/sources/qetdiagrameditor.h
index ed07a2395..4a8b3f1fe 100644
--- a/sources/qetdiagrameditor.h
+++ b/sources/qetdiagrameditor.h
@@ -136,6 +136,9 @@ class QETDiagramEditor : public QETMainWindow
void editProjectProperties(ProjectView *);
void editProjectProperties(QETProject *);
void slot_terminalNumbering();
+#ifdef QET_HAS_SCRIPTING
+ void slot_runScript();
+#endif
void editDiagramProperties(DiagramView *);
void editDiagramProperties(Diagram *);
void addDiagramToProject(QETProject *);
@@ -211,6 +214,9 @@ class QETDiagramEditor : public QETMainWindow
*m_project_export_wiring_list, ///< Action to export the wiring list
*m_project_wiring_list_view, ///< Action to show the wiring list read from the project database
*m_terminal_numbering, ///< Action to launch terminal numbering
+#ifdef QET_HAS_SCRIPTING
+ *m_run_script, ///< Action to run a JavaScript macro against the current project
+#endif
*m_export_project_db, ///Export to file the internal database of the current project
*m_tile_window, ///< Show MDI subwindows as tile
*m_cascade_window, ///< Show MDI subwindows as cascade
diff --git a/sources/scripting/qetscriptapi.cpp b/sources/scripting/qetscriptapi.cpp
new file mode 100644
index 000000000..af6dedaa4
--- /dev/null
+++ b/sources/scripting/qetscriptapi.cpp
@@ -0,0 +1,170 @@
+/*
+ Copyright 2006-2026 The QElectroTech Team
+ This file is part of QElectroTech.
+
+ QElectroTech is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ QElectroTech is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QElectroTech. If not, see .
+*/
+#include "qetscriptapi.h"
+
+#include "../cli_export.h"
+#include "../diagram.h"
+#include "../diagramcontent.h"
+#include "../qetproject.h"
+
+#include
+
+QetScriptApi::QetScriptApi(QETProject *project, QObject *parent) :
+ QObject(parent),
+ m_project(project)
+{
+}
+
+QString QetScriptApi::projectTitle() const
+{
+ return m_project ? m_project->title() : QString();
+}
+
+QString QetScriptApi::filePath() const
+{
+ return m_project ? m_project->filePath() : QString();
+}
+
+int QetScriptApi::folioCount() const
+{
+ return m_project ? m_project->diagrams().count() : 0;
+}
+
+QString QetScriptApi::folioTitle(int index) const
+{
+ if (!m_project) return QString();
+ const QList diagrams = m_project->diagrams();
+ if (index < 0 || index >= diagrams.count()) return QString();
+ return diagrams.at(index)->title();
+}
+
+int QetScriptApi::elementCount(int folioIndex) const
+{
+ if (!m_project) return 0;
+ const QList diagrams = m_project->diagrams();
+ if (folioIndex < 0 || folioIndex >= diagrams.count()) return 0;
+ DiagramContent content(diagrams.at(folioIndex), false);
+ return content.m_elements.count();
+}
+
+int QetScriptApi::conductorCount(int folioIndex) const
+{
+ if (!m_project) return 0;
+ const QList diagrams = m_project->diagrams();
+ if (folioIndex < 0 || folioIndex >= diagrams.count()) return 0;
+ DiagramContent content(diagrams.at(folioIndex), false);
+ return content.conductors(DiagramContent::AnyConductor).count();
+}
+
+/**
+ @brief QetScriptApi::runFlag
+ Build a CLIExport::run() argument list from this project's own file
+ path plus @p args, and run it. Reopens the project from disk -- see the
+ class comment for why that trade-off was made.
+ @param flag one of the --export-* / --resave / --set-titleblock flags
+ @param args the flag's own positional arguments (output path, etc.)
+ @return true if CLIExport::run() returned 0 (success)
+*/
+bool QetScriptApi::runFlag(const QString &flag, const QStringList &args)
+{
+ if (!m_project) {
+ log(QStringLiteral("qet.%1: no project").arg(flag));
+ return false;
+ }
+ const QString path = m_project->filePath();
+ if (path.isEmpty()) {
+ log(QStringLiteral("qet.%1: project has no file path -- save it first").arg(flag));
+ return false;
+ }
+ QStringList full_args;
+ full_args << flag << path << args;
+ return CLIExport::run(full_args) == 0;
+}
+
+bool QetScriptApi::exportPdf(const QString &output, bool showTerminals)
+{
+ QStringList args{output};
+ if (showTerminals) args << QStringLiteral("--show-terminals");
+ return runFlag(QStringLiteral("--export-pdf"), args);
+}
+
+bool QetScriptApi::exportPng(const QString &outDir, bool showTerminals)
+{
+ QStringList args{outDir};
+ if (showTerminals) args << QStringLiteral("--show-terminals");
+ return runFlag(QStringLiteral("--export-png"), args);
+}
+
+bool QetScriptApi::exportSvg(const QString &outDir, bool showTerminals)
+{
+ QStringList args{outDir};
+ if (showTerminals) args << QStringLiteral("--show-terminals");
+ return runFlag(QStringLiteral("--export-svg"), args);
+}
+
+bool QetScriptApi::exportCables(const QString &output)
+{
+ return runFlag(QStringLiteral("--export-cables"), {output});
+}
+
+bool QetScriptApi::exportWires(const QString &output)
+{
+ return runFlag(QStringLiteral("--export-wires"), {output});
+}
+
+bool QetScriptApi::exportBom(const QString &output)
+{
+ return runFlag(QStringLiteral("--export-bom"), {output});
+}
+
+bool QetScriptApi::exportWiring(const QString &output)
+{
+ return runFlag(QStringLiteral("--export-wiring"), {output});
+}
+
+bool QetScriptApi::exportNets(const QString &output)
+{
+ return runFlag(QStringLiteral("--export-nets"), {output});
+}
+
+bool QetScriptApi::exportLinks(const QString &output)
+{
+ return runFlag(QStringLiteral("--export-links"), {output});
+}
+
+bool QetScriptApi::exportInfo(const QString &output)
+{
+ return runFlag(QStringLiteral("--info"), output.isEmpty() ? QStringList{} : QStringList{output});
+}
+
+bool QetScriptApi::setTitleBlock(const QString &output, const QStringList &assignments)
+{
+ QStringList args{output};
+ args << assignments;
+ return runFlag(QStringLiteral("--set-titleblock"), args);
+}
+
+bool QetScriptApi::save(const QString &output)
+{
+ return runFlag(QStringLiteral("--resave"), {output});
+}
+
+void QetScriptApi::log(const QString &message)
+{
+ QTextStream(stderr) << message << "\n";
+}
diff --git a/sources/scripting/qetscriptapi.h b/sources/scripting/qetscriptapi.h
new file mode 100644
index 000000000..e454e1d3b
--- /dev/null
+++ b/sources/scripting/qetscriptapi.h
@@ -0,0 +1,89 @@
+/*
+ Copyright 2006-2026 The QElectroTech Team
+ This file is part of QElectroTech.
+
+ QElectroTech is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ QElectroTech is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QElectroTech. If not, see .
+*/
+#ifndef QET_SCRIPT_API_H
+#define QET_SCRIPT_API_H
+
+#include
+#include
+#include
+
+class QETProject;
+
+/**
+ @brief The QetScriptApi class
+ The object a script sees as `qet` (bugtracker #162): a small,
+ deliberately read-mostly surface over an open project, for scripted
+ batch/CI work and human-written macros.
+
+ @b Scope, on purpose: this reads the model (folio count, titles, element
+ and conductor counts -- the same data the `--info` CLI export already
+ reports) and can trigger the same export/save operations the `--export-*`
+ CLI flags already do. It does not create or edit diagram geometry, does
+ not touch the undo stack, and does not drive the GUI. Those are all
+ explicitly out of scope for this first version; see the discussion on
+ bugtracker #162.
+
+ Export/save methods are thin wrappers around CLIExport::run() -- the
+ exact same, already-tested code path the `--export-*` flags use -- built
+ from the project's own file path, not the live in-memory instance. That
+ keeps this file free of any dependency on cli_export.cpp's internals,
+ at the cost of re-opening the project from disk for each call: fine for
+ the batch/CI use case this targets, and for the headless `--run` entry
+ point it's exactly what a second `--export-*` invocation would have
+ done anyway. A macro acting on unsaved GUI edits should call save()
+ first.
+*/
+class QetScriptApi : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit QetScriptApi(QETProject *project, QObject *parent = nullptr);
+
+ // -- read the model --
+ Q_INVOKABLE QString projectTitle() const;
+ Q_INVOKABLE QString filePath() const;
+ Q_INVOKABLE int folioCount() const;
+ Q_INVOKABLE QString folioTitle(int index) const;
+ Q_INVOKABLE int elementCount(int folioIndex) const;
+ Q_INVOKABLE int conductorCount(int folioIndex) const;
+
+ // -- export / save: thin wrappers around the --export-* CLI paths --
+ Q_INVOKABLE bool exportPdf(const QString &output, bool showTerminals = false);
+ Q_INVOKABLE bool exportPng(const QString &outDir, bool showTerminals = false);
+ Q_INVOKABLE bool exportSvg(const QString &outDir, bool showTerminals = false);
+ Q_INVOKABLE bool exportCables(const QString &output);
+ Q_INVOKABLE bool exportWires(const QString &output);
+ Q_INVOKABLE bool exportBom(const QString &output);
+ Q_INVOKABLE bool exportWiring(const QString &output);
+ Q_INVOKABLE bool exportNets(const QString &output);
+ Q_INVOKABLE bool exportLinks(const QString &output);
+ Q_INVOKABLE bool exportInfo(const QString &output);
+ Q_INVOKABLE bool setTitleBlock(const QString &output, const QStringList &assignments);
+ Q_INVOKABLE bool save(const QString &output);
+
+ // -- logging: a script has no console of its own --
+ Q_INVOKABLE void log(const QString &message);
+
+ private:
+ bool runFlag(const QString &flag, const QStringList &args);
+
+ QETProject *m_project;
+};
+
+#endif // QET_SCRIPT_API_H
diff --git a/sources/scripting/qetscripting.cpp b/sources/scripting/qetscripting.cpp
new file mode 100644
index 000000000..7a6addbf9
--- /dev/null
+++ b/sources/scripting/qetscripting.cpp
@@ -0,0 +1,126 @@
+/*
+ Copyright 2006-2026 The QElectroTech Team
+ This file is part of QElectroTech.
+
+ QElectroTech is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ QElectroTech is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QElectroTech. If not, see .
+*/
+#include "qetscripting.h"
+
+#include "qetscriptapi.h"
+#include "../qetproject.h"
+
+#include
+#include
+#include
+
+#ifdef QET_HAS_SCRIPTING
+#include
+#endif
+
+namespace {
+ QTextStream out(stdout);
+ QTextStream err(stderr);
+}
+
+namespace QetScripting {
+
+bool isRunRequest(const QStringList &args)
+{
+ return args.contains(QStringLiteral("--run"));
+}
+
+#ifdef QET_HAS_SCRIPTING
+
+int run(const QStringList &args)
+{
+ const int idx = args.indexOf(QStringLiteral("--run"));
+ const QString script_path = args.value(idx + 1);
+ const QString project_path = args.value(idx + 2);
+ if (script_path.isEmpty() || project_path.isEmpty()) {
+ err << "Usage: qelectrotech --run \n";
+ return 2;
+ }
+ if (!QFileInfo::exists(script_path)) {
+ err << "Script not found: " << script_path << "\n";
+ return 2;
+ }
+ if (!QFileInfo::exists(project_path)) {
+ err << "Project not found: " << project_path << "\n";
+ return 2;
+ }
+
+ QETProject project(project_path);
+ if (project.state() != QETProject::Ok) {
+ err << "Failed to open project: " << project_path
+ << " (state " << project.state() << ")\n";
+ return 1;
+ }
+
+ return runOnProject(script_path, &project) ? 0 : 1;
+}
+
+bool runOnProject(const QString &scriptPath, QETProject *project)
+{
+ QFile file(scriptPath);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ err << "Cannot open script: " << scriptPath << "\n";
+ return false;
+ }
+ const QString source = QString::fromUtf8(file.readAll());
+ file.close();
+
+ QJSEngine engine;
+ auto *api = new QetScriptApi(project, &engine);
+ QJSValue qet_value = engine.newQObject(api);
+ // newQObject() takes ownership by default (QJSEngine::JavaScriptOwnership),
+ // which would delete api as soon as the engine's GC decides to -- api's
+ // real owner is the engine itself via the parent-child relationship set
+ // above, so keep the engine, not the GC, in charge of its lifetime.
+ engine.setObjectOwnership(api, QJSEngine::CppOwnership);
+ engine.globalObject().setProperty(QStringLiteral("qet"), qet_value);
+
+ QJSValue result = engine.evaluate(source, scriptPath);
+ if (result.isError()) {
+ err << "Script error: " << scriptPath << ":"
+ << result.property(QStringLiteral("lineNumber")).toInt() << ": "
+ << result.toString() << "\n";
+ return false;
+ }
+ return true;
+}
+
+#else // !QET_HAS_SCRIPTING
+
+// Qt::Qml was not found at configure time (see the QET_HAS_SCRIPTING probe
+// in the top-level CMakeLists.txt). Compile to a clear, non-silent failure
+// rather than omitting these symbols -- the same "always compiled, the
+// disabled path says why" shape as the QtPdf feature guard.
+
+int run(const QStringList &)
+{
+ err << "This build of QElectroTech was compiled without the Qt Qml "
+ "module, so JavaScript scripting (--run) is not available.\n";
+ return 1;
+}
+
+bool runOnProject(const QString &, QETProject *)
+{
+ err << "This build of QElectroTech was compiled without the Qt Qml "
+ "module, so JavaScript scripting is not available.\n";
+ return false;
+}
+
+#endif // QET_HAS_SCRIPTING
+
+}
diff --git a/sources/scripting/qetscripting.h b/sources/scripting/qetscripting.h
new file mode 100644
index 000000000..ac7e1e51a
--- /dev/null
+++ b/sources/scripting/qetscripting.h
@@ -0,0 +1,61 @@
+/*
+ Copyright 2006-2026 The QElectroTech Team
+ This file is part of QElectroTech.
+
+ QElectroTech is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ QElectroTech is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QElectroTech. If not, see .
+*/
+#ifndef QET_SCRIPTING_H
+#define QET_SCRIPTING_H
+
+#include
+
+class QETProject;
+
+/**
+ @brief JavaScript scripting entry points (bugtracker #162).
+
+ A script sees a single global, `qet` (see QetScriptApi), exposing a
+ deliberately small, mostly read-only surface: folio/element/conductor
+ counts and the same export operations the `--export-*` CLI flags
+ provide. See QetScriptApi's class comment for what is and is not in
+ scope for this first version.
+*/
+namespace QetScripting {
+
+ /**
+ @brief True if @p args is a `--run `
+ invocation.
+ */
+ bool isRunRequest(const QStringList &args);
+
+ /**
+ @brief Run the script named in @p args against the project also
+ named there, headless.
+ Usage: qelectrotech --run
+ @return process exit code: 0 on success, 1 if the project failed to
+ open or the script threw, 2 on a usage error.
+ */
+ int run(const QStringList &args);
+
+ /**
+ @brief Run @p scriptPath against an already-open @p project (the
+ "Run Script..." GUI macro path). Errors go to stderr; there is no
+ modal reporting in this first version.
+ @return true if the script ran without throwing.
+ */
+ bool runOnProject(const QString &scriptPath, QETProject *project);
+
+}
+
+#endif // QET_SCRIPTING_H