Commit Graph

10062 Commits

Author SHA1 Message Date
ispyisail b899d46453 Merge master (#1264, #1265) into feature/qet-mcp-layout-ops
README and tests: both PRs added text at the same places; kept both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
2026-10-03 15:56:00 +13:00
ispyisail 78111063fb Merge pull request #1265 from ispyisail/feature/qet-mcp-layout-check
Add qet_layout_check: score a drawing and fix wires that jog
2026-10-03 15:52:16 +13:00
ispyisail 41ba7d267f Merge pull request #1264 from ispyisail/feature/script-terminal-position
Add terminalPosition() and conductorPath() to the scripting API
2026-10-03 15:51:43 +13:00
ispyisail d952fb9a0d qet-mcp: place_element takes an angle, for symbols turned along a rung
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
2026-10-03 13:41:25 +13:00
ispyisail 372e541a00 qet-mcp: ops that line symbols up so wires come out straight
A wire is straight only when its two terminals are exactly in line, and
qet_edit placed a symbol only by its origin, so an assistant had to guess
where the terminals would land -- and usually missed by a few pixels.

- place_element adds a symbol with one of its terminals in line with
  another symbol's, gap pixels away on the side that terminal faces, and
  notes when the new symbol's terminal faces the wrong way to meet it.
- align_terminal moves a placed symbol across so two terminals line up.
- align_elements lines up symbols' boxes (left/center/right/top/middle/
  bottom), as Edit > Align does.
- distribute_elements spaces symbols' origins evenly, or a pitch apart,
  keeping symbols on the grid on it.

place_element and align_terminal need qet.terminalPosition(); on a build
without it the edit stops before running, naming the missing call, and
every other op still works there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
2026-10-03 13:37:00 +13:00
ispyisail cc23cf820f Scripting: add terminalPosition() and conductorPath()
A script laying out a drawing could read where a symbol is
(elementGeometry) but not where its terminals are, so it could not place
one symbol with a terminal exactly in line with another's -- the one
thing that makes the wire between them straight. And it could read a
wire's drawn path only through conductorSegments(), which names the wire
by a terminal and so refuses any terminal carrying two wires: 282 of the
3120 wires in the shipped examples.

terminalPosition(folio, element, terminal) returns where a wire docks on
the terminal, in folio coordinates, and which way it leaves (n/e/s/w,
the element's rotation included). conductorPath(folio, uuid) returns any
wire's drawn path as points, by its uuid.

tst_scriptlayoutreads checks the two against each other on every wire of
a fixture: each path starts and ends where its terminals' positions say,
and leaves each the way it faces; where conductorSegments() can name a
wire, both give the same points; and a quarter turn turns the facing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
2026-10-03 13:26:34 +13:00
ispyisail 5f2d31bff7 qet-mcp: add qet_layout_check, a score and fixes for how a drawing reads
A wire is straight only when its two terminals are exactly in line, and a
symbol is placed by its origin with its terminals at an offset from it, so
symbols an assistant places "under each other" by eye land a few pixels
apart and the wire jogs. Nothing told it so.

qet_layout_check reads every symbol and every wire's drawn path through
the scripting API (the default path is not saved in the file) and reports
a 0-100 score with: wires that jog where moving one symbol would make them
straight, extra bends, wires through symbols, overlaps, symbols off the
grid and crossings. "fixes" is one move_element per symbol, planned
together so they can be applied in one qet_edit call: a straight wire pins
its symbols, a move never lands a symbol on another or across a wire, and
symbols lined up with each other go onto the grid together. Styles: iec
(columns), nfpa (rungs) or auto. Read-only.

Over the 23 shipped examples, applying the fixes never lowers a score and
a second check proposes no further moves on 21 of them; a sloppy
assistant-style drawing goes from 40 to 100 in one round, in both styles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
2026-10-03 13:17:09 +13:00
ispyisail 18568dfd53 Merge pull request #1263 from ispyisail/fix/1238-empty-xrefpos
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m59s
Fix #1238: cross-reference drawn over the contact's label
2026-10-03 11:01:58 +13:00
ispyisail 9749985089 Fix #1238: cross-reference drawn over the label in older projects
XRefProperties read the stored cross-reference position with
QMetaEnum::keyToValue() and cast the result straight to
Qt::AlignmentFlag. An empty value gives -1. Earlier versions saved
xrefpos="" into projects and the settings file (18 of the 29 shipped
examples that carry cross-reference settings have it), and -1 matches
no branch of DynamicElementTextItem::setXref_item(), so the
cross-reference stayed at (0,0): the top-left corner of the label,
on top of it.

fromXml() fell back to AlignBottom only when the attribute was
missing, and fromSettings() only when the key was missing (#296), so
an empty value kept producing the bad position and was saved back
empty on every save.

Both now go through one helper that returns AlignBottom for an empty,
unknown, or not-offered value. The next save writes "AlignBottom", so
affected projects heal once resaved.

tst_xrefpos covers fromXml(), fromSettings() and the rewrite on save;
it fails 5 of 14 cases without this change. Exporting
examples/2612_ats_singlephase.qet to SVG before and after shows every
slave cross-reference moving from over its label to below it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELKpbGxqJd7EFiTUypBtVe
2026-10-03 10:18:06 +13:00
Laurent Trinques e1f14e4b2f Merge pull request #1258 from ispyisail/fix/route-close-symbols
Fix wire routing looping between close symbols (#1178)
2026-10-02 23:15:33 +02:00
Laurent Trinques d8add2b8a0 Merge pull request #1257 from ispyisail/feature/pdf-reproducible
Add repeatable PDF export when SOURCE_DATE_EPOCH is set
2026-10-02 23:15:05 +02:00
Laurent Trinques ff64fdcb00 Merge pull request #1256 from ispyisail/fix/route-review-followups
Fix the follow-ups from the review of wire routing (#1245)
2026-10-02 23:14:51 +02:00
Laurent Trinques e42d09da45 Merge pull request #1255 from ispyisail/fix/pdf-link-order
Fix PDF cross-reference links coming out in a different order each run
2026-10-02 23:14:31 +02:00
Laurent Trinques 12ca26420b Merge pull request #1262 from ispyisail/fix/cli-common-elements-dir
Fix --common-elements-dir being ignored by --run and the export
2026-10-02 23:06:15 +02:00
Laurent Trinques 04b7659180 Merge pull request #1261 from ispyisail/fix/english-untranslated
Fix 175 strings showing in French in the English interface
2026-10-02 23:04:14 +02:00
Laurent Trinques 7d61acebd9 Merge pull request #1260 from ispyisail/fix/live-mode-polish
Fix live mode's English text, settings path and screenshot (#1178)
2026-10-02 23:02:54 +02:00
Laurent Trinques bb005f16f4 Merge pull request #1259 from neitri/master
Russian translate new dialog text
2026-10-02 23:01:07 +02:00
Laurent Trinques 69a71d775f Merge pull request #1254 from ispyisail/fix/settings-dir-macos
Fix #1246: QET_SETTINGS_DIR reads the wrong folder on macOS
2026-10-02 22:58:14 +02:00
Laurent Trinques 069e7d815d Merge pull request #1250 from ispyisail/feature/mcp-folio-presets
Add paper size presets for folios to the scripting API and MCP
2026-10-02 22:57:04 +02:00
ispyisail 0559087617 Move tst_pdfreproducible so it does not clash with #1255
Both added their test right after tst_pdfpagesize in
tests/qttest/CMakeLists.txt, so whichever merged second conflicted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJw3u5AdKQgQVPug8CzuMQ
2026-10-03 09:49:54 +13:00
ispyisail 5ed5eb82ee Honour --common-elements-dir and the other folder options in headless runs
Reported on #1178. Two things kept --common-elements-dir from working for
--run and the command-line export:

- main() runs both before QETApp exists, and only QETApp::parseArguments()
  applied the folder options, so they were ignored. The application of
  those options is now QETApp::applyDirectoryArguments(), static, and the
  two headless branches call it too. They also drop the options from the
  arguments they pass on: the export reads its project and output by
  position, so "--export-pdf --common-elements-dir=X p.qet out.pdf" took
  the option for the project ("Project not found").
- commonElementsDir() read the saved collection path before the
  command-line one, so a user's own setting beat a folder given for this
  run. The command line now comes first, in the GUI too.

Tested with a --run script adding a symbol that exists only in a given
folder: master fails with or without a saved collection path; this
resolves it, before or after --run, and over a saved path to another
folder. Without the option it still fails, and a saved path to the
folder still works.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELKpbGxqJd7EFiTUypBtVe
2026-10-03 09:33:24 +13:00
ispyisail 8361a1efce Build and work on Qt before 6.8
QPdfWriter::setDocumentId() is new in Qt 6.8, QTimeZone::UTC in 6.5 and
QHashSeed in 6.2, so the build failed on Qt 6.4 (Ubuntu 24.04). Qt before
6.8 also writes no /ModDate and no XMP metadata, and setDocumentDate()
then gave up and left the time of the export in the file. Those two are
now rewritten when present, and no document id is set where Qt writes
none.

tst_pdfreproducible also checks that the xref table still matches the
file after the dates are rewritten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJw3u5AdKQgQVPug8CzuMQ
2026-10-03 09:30:11 +13:00
ispyisail ee62f2a4e8 English: translate the 175 entries still unfinished in qet_en.ts
lupdate had added them, but nobody filled them in, so the English
interface showed these strings in French: align, group and ungroup,
arc and fillet, centre on cursor, paste at the original position, wire
hops, the collection prefixes, the materials list, saving and loading
settings, the terminal-name check in the symbol editor, recovery files,
the macro recorder's messages and a few more.

About 30 of them were pre-filled by lupdate from an identical string
elsewhere and left unfinished; some of those guesses were wrong (a
dialog's "Annuler" offered as "Undo"). All are reviewed here. Plural
messages get both English forms. lrelease now reports 3487 finished,
0 unfinished. Other languages are untouched.

Terms follow the existing English: sheet (folio), terminal (borne),
conductor (conducteur), parts list (nomenclature); "liste de matériaux"
is "materials list" so it is not confused with the parts list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELKpbGxqJd7EFiTUypBtVe
2026-10-03 09:19:08 +13:00
ispyisail 5859bc3353 Live mode: English text, the right settings tab, folio-only screenshot
Reported on #1178 after a first try of live mode on macOS:

- The English interface showed the live-mode setting in French. The
  strings were in lang/qet_en.ts but untranslated. This fills in English
  for live mode, the script buttons, the macro recorder and the Connect
  an AI assistant dialog (101 entries); other features' untranslated
  English entries are left for their own change.
- The setting is on Configurer QElectroTech > Général > Projets, but the
  start-up warning, the MCP server's error and its README all said
  "Configurer > Général". All three now name the Projects tab, and the
  README and the server name the English menus and buttons.
- qet_live_screenshot grabbed the whole viewport, so a tall window around
  a landscape folio came back mostly empty. It now crops to the folio's
  frame and title block where they are on screen (the whole viewport if
  the folio is scrolled out of sight), and says so in cropped_to_folio.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:14:57 +13:00
Mikhail d8b687530d Russian translate new dialog text 2026-10-03 00:45:20 +05:00
ispyisail 84cda1f0b0 Routing: no loops between close symbols, and route inside frames
Reported on #1178: with "route": "avoid", two contacts one above the
other, wired bottom terminal to top terminal 30 px apart or closer, got
a five-segment loop (at 20 px: v 80, h -20, v -140, h 20, v 80) that ran
down through the lower contact and back up past the upper one.

The cause was exitPoint(): it walked out of a terminal until clear of
every obstacle, so with another symbol in front it walked through that
symbol, and the search then had to come back. The same walk made a wire
between two symbols inside a frame (a cabinet drawn as one element)
leave through the frame's side, go round, and cross back in.

The router now knows each terminal's own symbol (Request::start_symbol,
end_symbol; applyRoute() fills them):
- two terminals facing each other on one line with nothing between
  them are joined straight, however close;
- the exit walks through the margin around other symbols but never
  through one; a terminal pointing straight into another symbol gets
  "no-route" instead of a route through it;
- an obstacle drawn around either end's own symbol is left out.
Without the symbols (the old Request), routes are as before.

Rerouting every wire of four shipped examples (perceuse, affuteuse_250h,
Polonez, industrial; 1331 wires): master routes 121 of them through
another symbol, this none (10 pass through a second symbol lying
exactly on an end symbol's rectangle, which no route can avoid). 99
wires that master routed through a symbol now get "no-route" and keep
their path. The 1232 wires both route are 6 % shorter in total
(436,144 -> 410,608 units) with 11 % fewer bends (2256 -> 2010).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:43:27 +13:00
ispyisail b0d73080ce Routing: follow up scorpio810's review of #1245
- Rename the `near` lambda in Conductor::setPathPoints() to `isNear`:
  `near` and `far` are empty macros in the Windows headers (minwindef.h).
- Reword the log when setPathPoints() refuses a route. "does not join the
  two terminals" named only one of its reasons; the router always returns
  the two terminals and at least one exit point, so a refusal means the
  route is not a run of horizontal and vertical segments between them.
- README: running along another wire is penalised, not forbidden, so two
  wires can end up drawn on top of each other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:13:31 +13:00
ispyisail 8bbdafe2e4 qet-mcp README: the preset note says frame, not PDF page
The note the op returns reads "...; frame 1223.25 x 791.25 pt"; the README
quoted it as "PDF page".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:51:44 +13:00
ispyisail 51d20abca4 Write the same PDF for the same project when SOURCE_DATE_EPOCH is set
Two exports of one project differed in the creation and modification
dates (the time of the export, in local time) and in the document id
(random). With SOURCE_DATE_EPOCH set (reproducible-builds.org),
--export-pdf now uses the time it names, in UTC, and a document id
derived from the project: version 5 of the project uuid with the
SHA-256 of the project file, so it is the same for the same file and
changes when the file does. Qt has no setter for the dates, so
PdfLinks::setDocumentDate() rewrites them in the document information
and the XMP metadata after the file is written, fixing the XMP /Length
and shifting the xref table. Without the variable nothing changes.

Only --export-pdf reads it; the print window's PDF export does not, as
a person exporting by hand wants the real date.

qet_export (misc/qet-mcp) gains "reproducible" and "source_date_epoch".
It reports whether the build honoured the variable, since an older one
ignores it.

tst_pdfreproducible exports examples/741.qet four times and requires
identical bytes and the epoch's date; it fails with either this change
or the hash seed fix reverted. Projects with cross-reference links also
need their links in a fixed order, which is a separate fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:50:28 +13:00
ispyisail 580cbb1cb1 Merge remote-tracking branch 'upstream/master' into feature/mcp-folio-presets
# Conflicts:
#	misc/qet-mcp/README.md
#	misc/qet-mcp/qet_mcp.py
2026-10-03 07:44:54 +13:00
ispyisail 6e478e0e3f Fix the hash seed main() asks for having no effect
main() set QT_HASH_SEED=0 so that hashes, and so the XML a project is
saved as, come out the same in every run. Qt reads that variable once,
when the first hash is made, and that happens before main(): printed
right after the qputenv(), QHashSeed::globalSeed() was a different
random number in every run. The setting never took effect.

QHashSeed::setDeterministicGlobalSeed() does what the variable was
meant to, for every hash made from then on. The variable is kept for
the processes QElectroTech starts.

One visible effect: the PDF engine writes its fonts in QHash order, so
--export-pdf of the same project gave different files from run to run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:40:32 +13:00
ispyisail 76cad625af Read QET_SETTINGS_DIR/QElectroTech/ on macOS too (#1246)
On macOS, Qt names the settings subfolder after the organization domain
when one is set, so QElectroTech read <folder>/qelectrotech.org/ while
main.cpp, tst_settingsdir and qet_mcp.py all use <folder>/QElectroTech/.
Clear the domain when QET_SETTINGS_DIR is set, on macOS only. Nothing in
QElectroTech reads it; SingleApplication hashes it into its key, so these
runs no longer hand off to an open QElectroTech.

test_qet_mcp.py: find true on PATH (/usr/bin/true on macOS), and compare
the found binary with samefile() on case-insensitive file systems.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:30:07 +13:00
ispyisail 8e6dc1a849 Write a PDF's cross-reference links in the same order in every run
injectCrossRefLinks() drew each link as it found it: the contacts of a
cross-reference come from a QMultiMap keyed by Element pointer, and the
scene's items in stacking order. Both change from run to run, so the same
project exported twice gave two different PDFs whose only difference was
the order of the link annotations.

The links are now collected and drawn sorted by their rectangle on the
page (top, left, bottom, right), with the target as the tie-break. The
links themselves are unchanged. The print window's PDF export goes
through the same function.

tst_pdflinkorder exports examples/industrial.qet five times in separate
processes and requires the same links in the same order. It fails on
the old code (lists differ at index 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:24:46 +13:00
Laurent Trinques 25084a44da Update splash file
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m40s
2026-10-02 15:55:51 +02:00
Laurent Trinques 30cd96e240 Update translations files 2026-10-02 15:49:50 +02:00
Laurent Trinques 8e164df351 Merge pull request #1249 from ispyisail/fix/pdf-landscape-paper-size
Fix landscape folios exporting slightly off the paper size in PDF
2026-10-02 15:06:12 +02:00
Laurent Trinques 2a72e77b3e Merge branch 'master' into fix/pdf-landscape-paper-size 2026-10-02 15:06:00 +02:00
Laurent Trinques 8fc43aa30f Merge pull request #1245 from ispyisail/feature/mcp-route-around
Add wire routing around symbols to the scripting API and MCP
2026-10-02 14:05:13 +02:00
Laurent Trinques e413fde272 Merge pull request #1253 from jp2images/svg-icons-b
Convert the remaining theme icons to SVG and drop unused PNGs (#1063)
2026-10-02 13:56:56 +02:00
Laurent Trinques d05e396fb1 Merge pull request #1248 from ispyisail/feature/wiring-terminal-identity
Add terminal index and uuid to the wiring list export
2026-10-02 13:44:52 +02:00
Laurent Trinques 8a63a2d529 Merge pull request #1247 from ispyisail/fix/stable-potential-text
Fix wire number moving between tied wires from run to run
2026-10-02 13:43:37 +02:00
Laurent Trinques 8baeac16e9 Merge pull request #1244 from ispyisail/fix/windows-longpath-manifest
Add longPathAware to the Windows manifest
2026-10-02 13:22:32 +02:00
Laurent Trinques cdc8e9db13 Merge pull request #1243 from ispyisail/fix/element-long-paths
Fix symbols in deep folders on Windows (paths of 260+ characters)
2026-10-02 13:13:33 +02:00
Laurent Trinques 29527d11cb Merge pull request #1242 from ispyisail/fix/import-category-by-name
Fix placing a symbol twice in a project with an extra symbol folder
2026-10-02 13:06:09 +02:00
Laurent Trinques 8a27ae6cee Merge pull request #1241 from ispyisail/fix/editor-parts-list-drag
Fix dragging parts selected in the symbol editor's Parts list
2026-10-02 12:58:17 +02:00
Laurent Trinques e13023f916 Merge pull request #1240 from ispyisail/fix/project-properties-ok-marks-modified
Fix Project properties > OK marking the project modified with no edit
2026-10-02 12:54:46 +02:00
Laurent Trinques f4869f6c0a Merge pull request #1239 from ispyisail/fix/project-properties-page-num-placeholder
Fix Project properties saving the Page Num placeholder as a numbering
2026-10-02 12:48:25 +02:00
Jeff Patterson 6753aa3ce7 Loaded icons by theme name and removed the PNGs nothing uses 2026-10-02 05:47:40 -05:00
Jeff Patterson 0d473a3889 Imported the Breeze page icons and generated the light and dark icon themes 2026-10-02 05:47:40 -05:00
Jeff Patterson 15439cfc84 Traced the last PNG theme icons and took ispyisail's circle, ellipse and fit-window drawings 2026-10-02 05:47:40 -05:00