Automating QElectroTech
How to drive QET from other programs, and what the XML files look like.
Correction, September 2026. Earlier versions of this page stated that QElectroTech supports Python scripting and a plugin system, and told readers to install plugins into
~/.local/share/QElectroTech/plugins/and equivalents. None of that exists. There is no embedded interpreter, no plugin interface, and no plugin directory — QET never reads those paths. The page below describes what is actually there, which is a good deal, just not that.
What QET actually offers:
| A headless command line | 13 verbs that open a project and export, inspect or rewrite it without a GUI. |
| JavaScript scripting | --run script.js project.qet, or "Run Script..." in the GUI — read the model, export, edit geometry with real undo. See JavaScript Scripting. |
| XML files | .qet and .elmt are plain XML you can read and write with any tool. |
| An external companion program | qet_tb_generator, launched from a menu entry. |
| C++ source | For anyone building QET itself, or a fork. |
What it still does not offer: a plugin API, a loadable-module directory, or an IPC/automation interface into an already-running instance. The JavaScript engine above is explicit, one-shot script execution, not a plugin system — nothing runs unless you tell it to.
1. The command line
This is the part most automation should use. A recognised export flag is detected before the GUI starts, so the process runs headless, does the work and exits — it does not open a window and does not hand off to an already-running instance.
qelectrotech --export-pdf myproject.qet out.pdf
Arguments are positional, not --flag=value. The shape is always
qelectrotech <flag> <project.qet> <output>, with two exceptions noted below.
The verbs
| Flag | Output | Notes |
|---|---|---|
--export-pdf |
one PDF | all folios, one page each |
--export-png |
a directory | one NN_Title.png per folio |
--export-svg |
a directory | one NN_Title.svg per folio |
--export-bom |
CSV | bill of materials, from the project database — same source as the GUI export |
--export-wiring |
CSV | from-to wiring list, one row per conductor — same as menu Project → Wiring list (database) / Export the wiring plan |
--export-cables |
CSV | the same logical list, built from the document XML instead |
--export-wires |
CSV | conductor numbers — same as menu Project → Export the list of conductor names |
--export-nets |
CSV | electrical nets — terminals grouped into potentials |
--export-links |
CSV | cross-references, flagging masters and slaves with no link |
--info |
JSON | structural dump: per-folio element and conductor counts, unconnected terminals. Writes to stdout if no output path is given |
--resave |
.qet |
loads and writes the XML back out |
--set-titleblock |
.qet |
stamps title-block fields, then saves |
--check-elements |
report | validates .elmt files — takes a file or directory, not a project |
Two extra switches:
--show-terminals— paint terminal markers and names into PDF/PNG/SVG output. Off by default, which matches the GUI export dialog. Useful for visually debugging an unconnected pin.--set-titleblocktakes trailingkey=valueassignments after the output path:date=todayor an ISOYYYY-MM-DDdate, standard title-block keys, or any other key, which is stored as a custom field. Bad assignments fail before anything is written.
Exit codes
0 success · 1 the work failed (project would not open, nothing to export,
file not writable) · 2 you called it wrong (missing argument, bad assignment).
That makes it safe to use in a CI pipeline directly.
Worth knowing
--export-cablesand--export-wiringare meant to agree. One is built from the document XML, the other from the project database. Running both and diffing them is a direct check that the database still describes the project — something otherwise only observable through the GUI.- Always pass a timeout in scripts. A project saved by an older QET raises a version warning on load; CLI mode answers message boxes instead of showing them, but an unexpected modal is the classic way to hang a headless run forever.
- Crash-recovery backups are disabled in CLI mode on purpose — the background backup write races process exit.
QT_QPA_PLATFORM=offscreenis enough for these verbs. No Xvfb is required.- This is also QET's real answer to "print wire labels." There is no
built-in wire-marker/ferrule label printer in QET (see the User Manual's
Printing Diagrams section for what printing does cover). The documented forum workflow is to export this CSV
and feed it into a label printer's own software — Brady, WAGO
Smart-Printer and Phoenix Contact's tools all accept CSV directly. A
known limitation, reported by a panel builder on the forum: the export
is one row per conductor with no quantity consolidation, so N identical
labels come out as N duplicate rows rather than one row with a quantity
column — expect to de-duplicate in a spreadsheet before printing a large
batch. Cembre-style printers that need a
.FNRfile or support-code column aren't produced by this export at all; that mapping has to be built by hand from the CSV.
Example: a revision pipeline
set -e
qelectrotech --set-titleblock in.qet stamped.qet indexrev=C date=today
qelectrotech --export-pdf stamped.qet "release/rev-C.pdf"
qelectrotech --export-bom stamped.qet "release/rev-C-bom.csv"
See also the CLI Reference.
2. Reading and writing the files directly
.qet and .elmt are XML. Anything that can parse XML can process them —
Python's xml.etree, lxml, xmlstarlet, XSLT, whatever you like. This is
what people mean when they say they "script QET": the script is an ordinary
program of your own that reads and writes the files, run alongside QET rather
than inside it.
A real .qet skeleton
<project title="ArduinoLCD" version="0.80">
<properties>
<property show="1" name="saveddate">17/04/2021</property>
</properties>
<newdiagrams>
<border rows="8" cols="17" rowsize="80" colsize="60" .../>
<inset folio="%id/%total" author="" title="" .../>
<conductors type="multi" .../>
<report label="%f-%l%c"/>
<xrefs>
<xref type="coil" master_label="%f-%l%c" slave_label="(%f-%l%c)" .../>
</xrefs>
</newdiagrams>
<diagram title="LCD 4 DATA" order="1" folio="%id/%total" cols="11" rows="7" ...>
<elements>
<element x="390" y="570" z="10" orientation="0"
type="embed://import/oznaczenia/tekst_08.elmt"
uuid="{52d4b9e8-05c3-49a2-8455-a42ff651200a}"
prefix="" freezeLabel="false">
...
</element>
</elements>
<conductors> ... </conductors>
</diagram>
<collection> ... </collection>
</project>
Points that trip people up:
- There is no
<diagrams>wrapper.<diagram>elements are direct children of<project>, one per folio, ordered by theirorderattribute. <element>has noid. It is identified byuuid, and itstypeattribute is a location, usuallyembed://…for an element copied into the project's own collection.<newdiagrams>holds the defaults for new folios, not the folios themselves.- Project-wide element definitions live under
<collection>, so a project is usually self-contained.
Full reference: Project XML.
A real .elmt skeleton
<definition version="0.90" type="element" link_type="master"
width="200" height="70" hotspot_x="100" hotspot_y="35">
<uuid uuid="{2cbd2b72-1d04-f03d-758f-ce3a14dbb3c5}"/>
<names>
<name lang="en">UPS</name>
<name lang="fr">UPS</name>
</names>
<kindInformations>
<kindInformation name="type">coil</kindInformation>
</kindInformations>
<informations>Author: RDS for QElectroTech</informations>
<description>
<rect x="0" y="0" width="460" height="590" style="..."/>
<terminal uuid="{73279019-…}" name="" x="-90" y="10" orientation="w" type="Generic"/>
</description>
</definition>
Points that trip people up:
width,height,hotspot_x,hotspot_yare required attributes of<definition>, and width/height must be multiples of 10 — QET rounds them up otherwise.- The uuid is an attribute (
<uuid uuid="{…}"/>), not element text. <name>entries are per-language and need alangattribute.link_typeis what makes an element a master, slave or terminal — see Linking elements.
Full reference: Elements XML.
Validate what you generate
Nothing checks a hand-written file until QET opens it, but the CLI will check element files for you:
qelectrotech --check-elements my_elements/
It reports each file as OK, WARN (loads but suspicious — for instance zero
terminals) or FAIL (unparseable, wrong root tag, missing bounding box), and
exits non-zero on any failure. Put it in CI if you generate .elmt files.
Rules of thumb
- Close the project in QET before rewriting its file. QET holds its own in-memory model and will overwrite you on save.
--resaveis a cheap normaliser: load and write back, then diff, to see what QET silently rewrites before you build a tool on an assumption about the markup.- Element and conductor identity is by UUID. Copying an element node without giving it a fresh uuid produces two elements that QET considers the same one.
3. qet_tb_generator — what "plugin" means in QET
There is one menu entry, Launch the terminal block creation plugin, and one
program behind it. qet_tb_generator is a separate third-party program,
distributed on PyPI, that reads and writes .qet files from the outside to
build terminal strips.
python -m pip install --upgrade qet_tb_generator
QET launches it by searching a fixed list of locations — QETApp::dataDir() + "/binary/", the current directory, ~/.qet/, and then whatever PATH
resolves — and starting it as an ordinary child process. That is the entire
integration: no shared memory, no API, no callbacks. If the executable is not
on one of those paths, the menu entry cannot find it.
Community support lives on the forum: Scripts · Code/Programming
Writing "a plugin" therefore means writing a standalone program that manipulates the files, exactly as §2 describes. There is no interface to implement and no directory to install into.
4. The C++ source
For changing QET itself, or building a fork:
- Doxygen API documentation: https://de-backer.github.io/qelectrotech-source-mirror/html/pages.html
- Source: https://github.com/qelectrotech/qelectrotech-source-mirror
- Building from source · Contributing
Orientation points, all in sources/:
| Class | File | Role |
|---|---|---|
QETProject |
qetproject.cpp |
a project: folios, collection, load and save |
Diagram |
diagram.cpp |
one folio (a QGraphicsScene) |
Element |
qetgraphicsitem/element.cpp |
a placed symbol |
Conductor |
qetgraphicsitem/conductor.cpp |
a wire |
Terminal |
qetgraphicsitem/terminal.cpp |
a connection point |
projectDataBase |
dataBase/projectdatabase.cpp |
the query cache — see The project database |
Note the vocabulary gap: the UI says wire, page and symbol; the code says conductor, diagram and element. Grepping for the UI word is the usual way to conclude wrongly that something is not implemented.
5. Things that do not exist
Stated plainly, because this page previously claimed otherwise:
| Claim | Reality |
|---|---|
| Embedded Python scripting | Still no Python interpreter. Every Python reference in the source is the qet_tb_generator launcher. |
| A plugin system / plugin interface | Still no QPluginLoader, no plugin ABI, nothing that loads external code, and no directory a project or a third party can drop code into. |
| An automation API into a running instance | Still none. SingleApplication forwards file arguments to a running instance; that is all. |
One thing that changed: QET now has an embedded JavaScript engine
(QJSEngine, not Python) for explicit, one-shot script runs — --run script.js project.qet, or "Run Script..." in the GUI. Not a plugin system: nothing
runs automatically, no project file can carry or trigger a script, and it
only reads/exports/edits through a narrow, named API, not arbitrary code
against QET's internals. See JavaScript Scripting for what
it actually does.
Getting Started
🌐 Languages — English · Français · Deutsch
Guides
Conductors — wire properties, what feeds which export, and cables
Wire & cable catalogue — cable types, IEC 60757 core colours, assigning a core to a conductor
Printing and exporting — paper, PDF, images, and what each path does differently
Linking elements — master, slave, terminal
PLC modules — I/O tables and linking a wire to a specific point
Using the element editor — drawing tools, saving, checks
Preferences reference — what each settings page does
Keyboard-only control — mouseless QET, and the one real gap
Mouse modifiers — what Shift, Ctrl and Alt change while you drag
Managing collections — folders, writability, building your own shortlist
Templates — reusable multi-element blocks, and why clicking one does nothing
Search & Replace — bulk property changes
Building a nomenclature query — the BOM/summary table builder
Linking wires across pages — folio reports
Variables & formulas — %f, %{label}, sequences
Auto-numbering — schemes, sequences, freezing
Terminal strips — strips, levels, bridges
Title block templates — the .titleblock format
Importing EPLAN parts (.edz) — EPLAN Data Portal
DXF import & export — two unrelated features, one format
The project database — the in-memory SQLite cache
Development
Automating QET — CLI, XML formats, external tools
CLI Reference — command line usage
JavaScript Scripting — --run, geometry editing, undo
Vision — proposal, under discussion
