Repository navigation
api_reference
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 | 14 verbs that open a project and export, inspect or rewrite it without a GUI. |
| JavaScript scripting |
--run script.js project.qet, or Run a script... in the editor β read the model, export, edit with real undo. See JavaScript Scripting. |
| An AI-assistant server |
misc/qet-mcp/qet_mcp.py, which lets Claude, Copilot and others read, check and edit projects through that scripting engine. See MCP server. |
| 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 or a loadable-module directory. The JavaScript engine above is explicit, one-shot script execution, not a plugin system β nothing runs unless you tell it to. The one way into an already-running QElectroTech is live mode: off by default, confirmed at start, local to your computer, and limited to running scripts and a fixed list of commands, each one undoable.
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.pdfArguments are positional, not --flag=value. The shape is always
qelectrotech <flag> <project.qet> <output>, with two exceptions noted below.
| Flag | Output | Notes |
|---|---|---|
--export-pdf |
one PDF | all sheets, one page each |
--export-png |
a directory | one NN_Title.png per sheet |
--export-svg |
a directory | one NN_Title.svg per sheet |
--export-dxf |
a directory | one DXF per sheet; see DXF import & export |
--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-sheet 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.
| Variable | Effect |
|---|---|
QET_ENABLE_SCRIPTING=1 |
lets --run run a script without the setting being ticked, for CI and batch jobs (see JavaScript Scripting) |
QET_SETTINGS_DIR=<folder> |
keeps QElectroTech's settings in <folder>/QElectroTech/QElectroTech.ini instead of the registry (Windows), the system preferences (macOS) or ~/.config (Linux). Give each job its own folder and it can neither read nor change your own settings, on every system |
QT_QPA_PLATFORM=offscreen |
runs without a display (see below) |
QET_SETTINGS_DIR came from issue #1178
(PRs #1183,
#1254 for macOS).
To point a headless run at a symbol collection, write the folder into that
INI file first:
[elements-collections]
common-collection-path=/path/to/qelectrotech/elements0 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.
-
--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.
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.
.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.
<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 sheet, 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 sheets, not the sheets themselves. - Project-wide element definitions live under
<collection>, so a project is usually self-contained.
Full reference: Project XML.
<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.
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.
It also checks terminal names, with the same rule the element editor applies on save (see Using the element editor Β§5): two terminals of one element sharing a name is a FAIL, and terminals without a name are a WARN (sheet reports, conductor definitions and thumbnails excepted). The element editor's setting to turn the check off does not apply here.
FAIL my_elements/shelly_pro_2pm.elmt (repeated terminal names: N Γ3)
WARN my_elements/90-10-0111.elmt (2 of 2 terminals have no name)
- 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.
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_generatorQET 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.
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: sheets, collection, load and save |
Diagram |
diagram.cpp |
one sheet (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.
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 | Only live mode, for an AI assistant: off by default, a warning at start, a local socket readable only by you, and every action undoable. Otherwise 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
Windows without admin rights β the portable archive, no installer
Guides
Conductors β wire properties, what feeds which export, cables, and hops where wires cross
Wires per terminal β limit the wires on a terminal, chain wiring instead of stars
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
Grid size and element size β why symbols aren't all the same scale, and scaling one without leaving the grid
Preferences reference β what each settings page does
Saving and loading settings β your whole setup in one file, to copy or keep
Keyboard-only control β mouseless QET, and what still needs a mouse
Mouse modifiers β what Shift, Ctrl and Alt change while you drag
3D mouse β SpaceMouse pan, zoom and buttons
Aligning items β snap symbols back to the grid, or line them up
Pictures on a sheet β labels, crop, transparency, what they cost in the file
Arcs and curved wires β the Arc tool, pulling an arc in or out, rounding a corner with a fillet, dashed arcs for lighting layouts
Grouping items β select, move and copy several items as one
Finding your place on a sheet β go to a cell like B13 or 4-B7, keep the headers in sight, show the cell limits, zoom and pan
Showing and hiding kinds of items β hide texts, wire numbers, shapes, pictures, tables or cross-references on every sheet
Drawing faster β place without dragging, the S shortcut bar, command search, gestures
Customising QElectroTech β keys, toolbar size and contents, the gesture ring (partly pending)
Managing collections β folders, writability, building your own shortlist
Templates β reusable multi-element blocks, placed by double-click or drag
Search & Replace β bulk property changes
Building a nomenclature query β the BOM/summary table builder
Linking wires across pages β sheet 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; command-line export and layers
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
MCP server β let an AI assistant read, verify and edit projects
Connecting an AI assistant β setup for Claude, Copilot, Gemini, Codex, Cursor, LM Studio
Script buttons β stored scripts with an icon, by hand or by an assistant
Live mode β an assistant working in the open project while you watch
Macro recorder β record a task by hand, for an assistant to script
Vision β proposal, under discussion
