Skip to content
ispyisail edited this page Oct 7, 2026 · 16 revisions

MCP server

Let an AI assistant (Claude, or any other MCP client) read and verify QElectroTech projects directly, instead of reasoning from a screenshot: what a project contains, what an edit actually changed, and what a whole corpus of projects contains.

It lives at misc/qet-mcp/qet_mcp.py in the source tree β€” a small stdio Model Context Protocol server, Python 3.9+ and the standard library only, no MCP SDK dependency. Added in PR #969; its scripting-API foundation landed alongside in PR #970. Both merged 2026-09-21.


Why this exists

Verifying a change by screenshot is unreliable, and this tool exists because that unreliability produced two wrong conclusions in one review session:

  • A drag of a multi-element selection looked like it had left the symbols behind and detached their labels. Diffing the saved file showed all four elements had moved by an identical (0, -80) and no label had moved at all. A bug report was one step away from being filed.
  • An "Apply" button looked like it did nothing. It was disabled, because a required field was empty.

Both times the pixels misled and the model told the truth. So the tools here read the model β€” the project XML and, where one exists, the project database β€” instead of the rendered scene.

Most tools parse the .qet/.elmt file directly: fast, no display needed, immune to a stray dialog. Two (qet_export, qet_edit) launch QElectroTech itself, in an isolated sandbox, because exporting and editing through the real application is the only way to get the real behaviour β€” see JavaScript Scripting for the engine qet_edit and qet_query drive underneath.


Installation

# from the QElectroTech source tree
python3 misc/qet-mcp/qet_mcp.py --list   # list the tools and exit
python3 misc/qet-mcp/qet_mcp.py          # speak MCP on stdin/stdout
python3 misc/qet-mcp/qet_mcp.py --call qet_project_info '{"path": "drawing.qet"}'   # run one tool and exit

Register it with an MCP client β€” for Claude Code or Claude Desktop, a mcpServers block:

{
  "mcpServers": {
    "qet": {
      "command": "python3",
      "args": ["/path/to/qelectrotech/misc/qet-mcp/qet_mcp.py"],
      "env": {
        "QET_MCP_WORKSPACE": "/home/you/drawings",
        "QET_ENABLE_SCRIPTING": "1"
      }
    }
  }
}

Nothing to build, nothing to pip install β€” the two environment variables above are the only setup that matters, and both are covered below.


Using it without Claude Code

Added in PR #1128.

A chat assistant in a web browser β€” claude.ai, or any other web chat β€” cannot start a program on your computer, so it cannot run this server. There are two ways round that.

In the Claude desktop app

The Claude desktop app for Windows and macOS runs local MCP servers, and it uses the same account as the website. All the tools work, including qet_edit and qet_export, because QElectroTech runs on your own machine.

  1. Install Python 3.9 or later. Nothing else is needed.

  2. In the desktop app, open Settings β†’ Developer β†’ Edit Config. This opens claude_desktop_config.json.

  3. Add the server, with your own paths:

    {
      "mcpServers": {
        "qet": {
          "command": "python",
          "args": ["C:\\path\\to\\qelectrotech\\misc\\qet-mcp\\qet_mcp.py"],
          "env": {
            "QET_MCP_WORKSPACE": "C:\\Users\\you\\Documents\\drawings",
            "QET_ENABLE_SCRIPTING": "1"
          }
        }
      }
    }

    On macOS use python3 and ordinary / paths. In JSON every \ in a Windows path is written \\.

  4. Quit the app completely and start it again. The tools appear under the chat box's tools menu.

  5. If the server did not come installed with QElectroTech, add "QET_BINARY": "C:\\Program Files\\QElectroTech\\bin\\qelectrotech.exe" (your own path) to the env block, so the tools that start QElectroTech can find it β€” see The program the server runs.

Only files under QET_MCP_WORKSPACE can be read or written (see Workspace confinement). Leave out QET_ENABLE_SCRIPTING if you do not want the assistant to edit projects; see Scripting gate for what that switches off.

The server's tests run on Linux. It uses nothing platform-specific, but these steps have not yet been tested on Windows or macOS.

With only a browser

If your web chat can run Python (on claude.ai, code execution), upload qet_mcp.py together with your project and ask the assistant to run single tools with --call:

python3 qet_mcp.py --call qet_elements '{"path": "drawing.qet"}'
echo '{"path": "drawing.qet"}' | python3 qet_mcp.py --call qet_conductors -

--call <tool> <arguments> runs one tool and exits. The arguments are a JSON object, or - to read it from standard input, which saves quoting JSON for a shell. The result is the tool's JSON, printed to standard output.

Exit code Meaning
0 the tool succeeded
1 the tool reported an error (a missing file, a path outside the workspace) β€” the message is on standard output
2 the call itself was malformed: unknown tool, arguments that are not a JSON object β€” the message is on standard error

The workspace rule applies exactly as it does in the server: with QET_MCP_WORKSPACE unset, only the folder the script was started in.

The sandbox has no QElectroTech in it, so only the tools that read files work there: qet_project_info, qet_elements, qet_conductors, qet_items, qet_diff, qet_scan, qet_element_info, qet_element_search and qet_element_build. Editing, exporting, qet_check and qet_query need the desktop app route above.


Environment variables

Variable Effect
QET_MCP_WORKSPACE Directories tool calls may read and write, :-separated (; on Windows). Unset: the directory the server was started in.
QET_MCP_ALLOW_ANY_PATH=1 Turns the workspace check off entirely β€” equivalent to giving the client local filesystem access with this process's privileges.
QET_ENABLE_SCRIPTING=1 Required by five tools (see below); QElectroTech refuses --run without it, default off since PR #984.

Workspace confinement

Every path in a tool call is chosen by the model. Without a policy, that makes the server a read/write primitive for anything the OS lets the process reach β€” read any project on disk, export somewhere unrelated, overwrite a file, embed an arbitrary local image or PDF. So data paths are confined to QET_MCP_WORKSPACE, checked at the point arguments enter the server. A path outside it is refused with an error naming what was allowed; symlinks are resolved first, so a link planted inside the workspace is judged by where it points.

binary (the qelectrotech executable) and elements_dir (the element collection) have their own rule, below.

The program the server runs

Since PR #1129 (merged 2026-09-29). Before it, binary was taken from each tool call and whatever executable file it named was run, with the call's own paths as arguments. Text inside a project from someone else could therefore steer an assistant into starting another program. Now the server finds QElectroTech itself and a call cannot choose:

Variable Effect
QET_BINARY the QElectroTech the tools launch. Without it: the installation the server sits in (<prefix>/share/qelectrotech/mcp/qet_mcp.py), else qelectrotech on PATH
QET_MCP_BINARIES other executables a call may name as binary, separated like QET_MCP_WORKSPACE β€” for comparing two builds
QET_MCP_ALLOW_ANY_BINARY=1 turns the check off: a call can then run any program
QET_MCP_ELEMENTS element collections a call may name as elements_dir, besides the workspace and the installed one

binary can be left out of every call. When given, it must be the same file (after following symlinks) or one listed in QET_MCP_BINARIES; anything else is refused, even a file inside the workspace. A setup that relied on passing a program that is not on PATH needs QET_BINARY in its env block.

Nothing is overwritten unasked. qet_export, qet_edit, qet_project_new and qet_element_build refuse an output that already exists unless the call passes "overwrite": true β€” the one step this server cannot undo is the one step it will not take on its own.

Scripting gate

Needs QET_ENABLE_SCRIPTING=1 qet_query, qet_continuity, qet_check, qet_layout_check, qet_project_new, qet_edit, and the script, live and recording tools marked so on their own pages
Unaffected Everything else β€” they read the .qet/.elmt directly, or, for qet_export, use a plain CLI flag

The variable goes in the environment the server is started in (the env block above), and the server passes it straight through to QElectroTech β€” it does not set the variable itself. A switch a program turns on for itself is not a switch: whoever configured the server and pointed it at a QElectroTech binary made that choice, and their own interactive QElectroTech keeps whatever its own setting says. Without it, the tools above return "ok": false with a hint naming the variable. Builds from before the setting existed need nothing.


Tools

Tool What it answers Launches QET?
qet_project_info Title, format version, sheets with their uuids, element/conductor counts per sheet No
qet_items The drawn items that are not symbols or wires β€” free texts, shapes, pictures, tables, symbols' text fields β€” each with its uuid, sheet and main fields; filter by sheet or kind (text, shape, image, table, element_text) No
qet_elements Placed elements: uuid, type, position, label, information bag; filter by sheet or name No
qet_conductors Conductors and their documentation fields (num, formula, cable, bus, function, colour, section); filter by attribute No
qet_diff What an edit actually changed β€” element moves/adds/removes/relabels, conductor field changes, sheet fields/texts/shapes/images/symbol text fields/terminal strips No
qet_scan Sweep a directory of projects, counting nodes carrying an attribute, with distinct values found No
qet_element_info Introspect a .elmt: translated names, terminals, dynamic-text info fields, part counts No
qet_export Headless export: pdf, png, svg, bom, cables, wires, wiring, nets, links, info Yes
qet_edit Change a project β€” place, move, rotate, label, wire, number, cross-reference, add text/shapes/images, restyle a symbol's text fields, delete, line symbols up, route wires around symbols, size a sheet for paper; returns a qet_diff of the result Yes
qet_query Read-only SQL SELECT/WITH against the project's SQLite database; omit sql to list queryable views/tables Yes*
qet_continuity ERC-style checks against the live Terminal/Conductor graph: unconnected terminals, potential mismatches, sheet-report link mismatches Yes*
qet_project_new Start from nothing: an empty project with a title and sheets, written and read back by QElectroTech itself Yes*
qet_element_search Find a symbol in a collection by name (any language), link type, kind or terminal count; results carry the common:// path and terminal index order qet_edit needs No
qet_check Design-rule checks: duplicate labels, unlabelled masters, unnumbered conductors, empty sheets, masters missing a manufacturer reference, crowded terminals, folio reports with several wires Yes*
qet_layout_check How well a drawing reads, scored 0–100: wires that jog, extra bends, wires through symbols, overlaps, symbols off the grid, crossings, labels over wires, 4-way junctions β€” with the moves that fix the jogs Yes*
qet_element_build Author a new .elmt: draw from lines/rects/ellipses/circles/arcs/polygons/text, with terminals to wire it by; computes and checks the size header No

* Needs QET_ENABLE_SCRIPTING=1.

Three more groups of tools, merged from 2 October 2026, are described on their own pages:

Tools Page PR
qet_script_api, qet_script_test, qet_script_install, qet_script_list, qet_script_read, qet_script_remove, qet_about Script buttons #1224
qet_live_status, qet_live_run_script, qet_live_run_stored, qet_live_command, qet_live_show_folio, qet_live_undo_last, qet_live_screenshot Live mode #1225
qet_live_new_project, qet_live_open_project, qet_live_switch_project, qet_live_save_project, qet_live_close_project, qet_live_print, qet_live_changes, qet_live_layout_check Live mode #1312, #1313
qet_recording_list, qet_recording_read, qet_recording_check, qet_recording_remove Macro recorder #1228

qet_about is the one to call first: it reads what QElectroTech last wrote about itself (version, folders, whether scripting and live mode are on), and house_style, your own drawing rules if you set any, which its description tells the assistant to follow β€” see Your house style.


Worked examples

What did that edit change?

{"name": "qet_diff", "arguments": {"before": "a.qet", "after": "b.qet"}}
"elements": { "moved_count": 4,
              "distinct_move_deltas": [[0.0, -80.0]],
              "relabelled": [], "info_changed": [] }

Four elements moved by one uniform delta; nothing was relabelled. That is the answer a screenshot gave wrongly.

Draw something, and check it landed

{"name": "qet_edit", "arguments": {
  "binary": "/path/to/qelectrotech",
  "project": "in.qet", "output": "out.qet",
  "elements_dir": "/path/to/qelectrotech/elements",
  "operations": [
    {"op": "add_folio", "id": "f"},
    {"op": "set_folio_title", "folio": "$f", "title": "Starter"},
    {"op": "add_element", "id": "k1", "folio": "$f", "path": "common://.../coil.elmt", "x": 100, "y": 100},
    {"op": "add_element", "id": "k2", "folio": "$f", "path": "common://.../coil.elmt", "x": 320, "y": 100},
    {"op": "add_conductor", "folio": "$f", "from": "$k1", "from_terminal": 0, "to": "$k2", "to_terminal": 0},
    {"op": "set_conductor", "folio": "$f", "element": "$k1", "terminal": 0, "property": "num", "value": "W7"},
    {"op": "set_label", "folio": "$f", "element": "$k1", "label": "KM1"}
  ]}}

An op that creates something takes an "id"; later ops name it as "$id". Terminals are addressed by index β€” top to bottom, then left to right, not the order the .elmt lists them; qet_element_info and qet_element_search both report that index order. The result carries a per-operation outcome and a qet_diff, because "addConductor β†’ true" says the call was accepted, not that the file came out right:

"diff": {"elements":   {"before": 11, "after": 13, "added": ["{0aa3…}", "{6f63…}"]},
         "conductors": {"before": 47, "after": 48, "added": ["4:{0aa3…}/{2904…}--{6f63…}/{2904…}"],
                        "removed": []}}

Straight wires: place symbols in line, then score the drawing

A wire is straight only when its two terminals are exactly in line; a symbol 3 px off gives a wire with a small jog in it. Four qet_edit operations line symbols up (PR #1266):

Op Arguments Does
place_element folio, path, terminal, next_to, next_to_terminal; optional side (below, above, right, left; default: the way next_to_terminal faces), gap (default 40 px, terminal to terminal), angle (a rotation applied first) adds a symbol with its terminal in line with next_to's terminal, and returns its uuid like add_element. Notes when that terminal faces the wrong way, so the wire would bend
align_terminal folio, element, terminal, to, to_terminal moves a placed symbol across, so its terminal is in line with the other one
align_elements folio, elements, edge (left, center, right, top, middle, bottom); optional to lines up the symbols' boxes, as Γ‰dition > Aligner does; with to, on that symbol
distribute_elements folio, elements, axis (horizontal, vertical); optional pitch spaces the symbols' origins evenly, or pitch apart

place_element and align_terminal need a QElectroTech with qet.terminalPosition() (development builds since 3 October 2026); with an older one the call says which call is missing and changes nothing.

{"name": "qet_edit", "arguments": {
  "project": "in.qet", "output": "out.qet", "elements_dir": "/path/to/qelectrotech/elements",
  "operations": [
    {"op": "add_element", "id": "k1", "folio": 0, "path": "common://.../coil.elmt", "x": 200, "y": 100},
    {"op": "place_element", "id": "h1", "folio": 0, "path": "common://.../lamp.elmt",
     "terminal": 0, "next_to": "$k1", "next_to_terminal": 1},
    {"op": "add_conductor", "folio": 0, "from": "$k1", "from_terminal": 1, "to": "$h1", "to_terminal": 0}
  ]}}

Then qet_layout_check (PR #1265) scores the result from 0 to 100 and lists what spoils it:

{"name": "qet_layout_check", "arguments": {"project": "out.qet"}}
Finding Severity Means
avoidable_bend warning two terminals face each other but are a few pixels out of line, so the wire jogs. fixes holds the move that makes it straight
extra_bends info the wire bends more often than its two ends need, often a segment moved by hand; route_conductor redraws it
wire_through_symbol warning a wire runs through a symbol it is not connected to
overlapping_symbols warning two symbols overlap by more than one grid step
off_grid warning a symbol is off the 10 px grid; fixes moves it on
label_on_wire warning a symbol's label is drawn over a wire: move the label, not the symbol
four_way_junction warning wires leave one point in all four directions, which reads as a crossing when printed: make it two T-junctions
misaligned_branch info two symbols stacked in one side branch are a little out of line, so the branch is not one straight column
crossing info two wires cross; counted so two drafts can be compared, but does not lower the score
Argument
folio one sheet, counted from 1; leave out for all
style iec (current paths are columns, wires mostly vertical), nfpa (ladder rungs are rows, wires mostly horizontal) or auto (default: decided from the drawing)
max_shift the largest move a fix may suggest, default 40 px; a bigger jog is taken as meant
limit how many findings to return, default 50; the summary counts them all

The score is 60 % the share of wires with no finding and 40 % the share of symbols with none. fixes is a list of move_element operations, one per symbol, planned together: pass the whole list to one qet_edit call, then run the check again. It reads only; nothing is saved.

Route a wire around symbols

add_conductor takes "route": "avoid" to draw the new wire around the symbols in its way, instead of QElectroTech's usual two or three straight segments that may cross a symbol. route_conductor does the same to a wire already there, named by element + terminal or by "conductor": "{uuid}", and answers routed or no-route:

{"op": "add_conductor", "folio": 0, "from": "$k1", "from_terminal": 1,
 "to": "$h1", "to_terminal": 0, "route": "avoid"}

Where there is no way round, the wire keeps its path and the op's note says so: not a failure. Route after placing everything; moving a symbol later stretches the path rather than routing it again. What counts as an obstacle is on JavaScript Scripting (PR #1245).

Size the sheets for a paper size

{"op": "set_folio_border", "folio": 0, "property": "preset", "value": "a3-landscape"}

a0–a5, letter, legal, tabloid, ledger, each -portrait or -landscape. It picks the column and row counts and sizes that fill that sheet, allowing for the title block, as one undo step; the op's note says what it chose and the frame's size, and qet_export's PDF of it comes out on that paper (PR #1250). set_folio_border also sets columns, column-width, display-columns, rows, row-height and display-rows one at a time.

Draw a symbol that does not exist yet

{"name": "qet_element_build", "arguments": {
  "output": "/path/to/collection/99_custom/my_resistor.elmt",
  "names": {"en": "Test resistor", "fr": "RΓ©sistance de test"},
  "parts": [
    {"type": "rect", "x": -10, "y": -20, "width": 20, "height": 40},
    {"type": "line", "x1": 0, "y1": -30, "x2": 0, "y2": -20},
    {"type": "line", "x1": 0, "y1": 20,  "x2": 0, "y2": 30},
    {"type": "text", "x": 14, "y": -4, "text": "R"}
  ],
  "terminals": [{"x": 0, "y": -30, "orientation": "n", "name": "1"},
                {"x": 0, "y": 30,  "orientation": "s", "name": "2"}]}}

Then place it with qet_edit like any catalogue element. Unlike a project, a .elmt is not rewritten by QElectroTech on a round trip, so generating one here is safe in a way that generating a .qet would not be β€” there is no toXml() waiting to drop what this writer did not know to emit.

Ask a question the XML cannot answer

{"name": "qet_query", "arguments": {
  "binary": "/path/to/qelectrotech", "project": "industrial.qet",
  "sql": "SELECT label, COUNT(*) AS n FROM element_nomenclature_view WHERE label <> '' GROUP BY label HAVING n > 1 ORDER BY n DESC"}}
"rows": [{"label": "V6", "n": 7}, {"label": "V5", "n": 6}, {"label": "V4", "n": 6}]

Duplicate element labels in a shipped example β€” a design-rule question, answered by the database that already knew it. See The project database for what element_nomenclature_view, project_summary_view and wiring_list_view cover.

How much of a corpus uses a field?

{"name": "qet_scan",
 "arguments": {"directory": "examples", "tag": "conductor", "attribute": "cable"}}
{ "files": 24, "total": 3190, "non_empty": 0, "distinct_values": [] }

Across the shipped examples: 3190 conductors, not one with a cable value.


Notes and limits

  • qet_export isolates its launch. SingleApplication keys its socket on applicationFilePath(), so a second launch of the same binary path forwards its request to an already-running instance and returns that process's answer, with no error. The tool copies the binary to a unique temporary path, gives it a private HOME, and runs it on the offscreen platform. A symlink would not work β€” applicationFilePath() resolves it back to the real path.

  • The CLI matches its flags exactly. --export-bom out.csv is the supported form; --export-bom=out.csv is not recognised as an export at all, so the application starts its interface instead and a headless run hangs. The tool uses the positional form.

  • How qet_diff matches items. Wires, sheets, free texts, shapes, pictures, tables and symbols' text fields are matched by their uuid when every item of that kind in both files has one (PR #1099); each section of the answer says what it was matched by in keyed_by. A file saved by an older QElectroTech may lack some, and then that kind is matched as before: wires by the element uuid and terminal at each end, the other items by position. Matched by position, a moved item reads as one removed and one added. The file's own sheet-scoped wire ids are never used: they are renumbered on every save.

  • Every drawn item can be named by its uuid. qet_items lists them; qet_edit takes a uuid wherever it takes a sheet (folio), a wire (conductor), a terminal, a table (table), a symbol's text field (index, looked up within the op's element) or a free text, shape or picture (index). Unlike a position, a uuid still names the same item after an earlier op adds or removes one (PRs #1101, #1108, #1115, #1126).

  • qet_edit needs a build whose scripting API carries the drawing verbs. Against an older one it reports exactly which methods are missing and changes nothing.

  • elements_dir is not optional for common:// paths. The sandboxed run has its own empty HOME, so QElectroTech falls back to the compiled-in collection path, which on a machine that never ran make install does not exist. The only symptom is add_element reporting that a file plainly present "does not resolve to an element". An absolute .elmt path works without it.

  • set_conductor changes the whole potential, not one segment β€” that is what the application does, since a wire number describes a potential. Name a terminal carrying exactly one conductor; a terminal several conductors meet at names none of them and is refused. set_conductor, move_conductor_segment and delete_conductor also take "conductor": "{uuid}" (as qet_conductors reports it) in place of element + terminal, which works where two conductors meet at a terminal, as long as one of the conductor's two terminals carries only it.

  • A sheet's wire defaults: set_conductor_default.

    Sets one of the defaults in PropriΓ©tΓ©s du folio (Sheet properties). onetextperfolio is the option activer l'option un texte par potentiel: "true" draws one wire number per potential on that sheet instead of one per wire. Any set_conductor property is accepted too, as the starting value for wires drawn later on that sheet.

    {"op": "set_conductor_default", "folio": 0, "property": "onetextperfolio", "value": "true"}

    "folio": -1 sets the project's defaults instead. Sheets added afterwards copy them; existing sheets keep their own, so set each one. Like the dialogs, this has no undo.

  • A terminal can be given by its uuid (PR #1126): terminal, from_terminal and to_terminal also take the terminal's uuid (as qet_element_info lists it) in place of its index, on the op's own element β€” for add_conductor, each end's own. Unlike the index, it tells apart two terminals at the same point. A uuid the element does not have stops the run, with a note naming the argument.

  • link_elements takes a sheet for each end, because a master and its slave are normally on different sheets. Whether a pair may be linked is decided by QElectroTech's own isLinkable(), so a script cannot make a link the GUI would refuse.

  • An element must live inside a collection to be placeable. An absolute .elmt path works, but only if the file sits under a directory QElectroTech knows as a collection β€” write it under the tree passed as elements_dir.

  • qet_element_build checks its size header against a containment constraint, not a formula: the declared box runs from (-hotspot_x, -hotspot_y) to (width - hotspot_x, height - hotspot_y) and the drawing must fit inside it. A drawing that escaped its box is the classic way a hand-written element renders clipped in the collection panel while looking fine in the XML.

  • Two qet_check rules come from the wires-per-terminal discussion (#1158, PR #1271), both info: crowded_terminals lists terminals with more than four wires (two double ferrules, one each side of the screw, is already a lot; cable, busbar and single-line symbols carry more on purpose), and reports_with_several_wires lists folio report arrows with more than one wire β€” a style some drawings use on purpose, not an error. Pass "checks": [...] to run only some rules.

  • qet_element_search treats contact names as the same word in every form: NO, NC, N/O, NF, and "normally open/closed". A one- or two-letter word such as NC matches only as a whole word, so "NC contact" no longer offers coils whose names merely contain those letters (PR #1285).

  • Each launch has its own settings, on every system. The tools that start QElectroTech give it a private home folder and set QET_SETTINGS_DIR, which makes QElectroTech keep its settings in <folder>/QElectroTech/QElectroTech.ini instead of the registry (Windows), the system preferences (macOS) or ~/.config (Linux). That is how elements_dir reaches QElectroTech on Windows and macOS too, and why a tool run never changes your own settings (PRs #1183, #1254). You can use the same variable yourself, for example to run QElectroTech with a separate set of settings.

  • QElectroTech interrupts a script at 30 s of its own accord, separate from the tool's own timeout. A very long operation list hits that first.

  • qet_edit never writes the input. It saves to a separate file and diffs the two, so the original is always the thing the diff is against.


Testing

python3 misc/qet-mcp/test_qet_mcp.py                      # unit + protocol, no QElectroTech needed

QET_BINARY=/path/to/qelectrotech \
QET_ELEMENTS=/path/to/qelectrotech/elements \
QET_EXAMPLES=/path/to/qelectrotech/examples \
QET_ENABLE_SCRIPTING=1 \
    python3 misc/qet-mcp/test_qet_mcp.py                  # everything, integration included

QET_ENABLE_SCRIPTING=1 matters here too: without it the integration tests that drive QElectroTech through a script all fail, and they fail as "the edit did nothing" rather than as "scripting is off" β€” which reads like a regression in the thing under test, not a missing switch.


See also

Getting Started

Home

🌐 Languages β€” English Β· FranΓ§ais Β· Deutsch

Downloads

Windows without admin rights β€” the portable archive, no installer

Quick Start Guide

User Manual

FAQ

Tips & Tricks

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

File formats
Elements XML
Project XML
Development

Building from Source

Contributing Code

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

Development Roadmap

Vision β€” proposal, under discussion

Developer Tools

About

Features

History

Community

License

Contributing to this Wiki

Clone this wiki locally