The panel shrank to its title bar whenever the selection had nothing it
could edit (a click on an empty spot, several elements, a mix of items):
its content layout used QLayout::SetMaximumSize, and an empty vertical
layout's maximum height is 0. The other right-hand panels took the room,
and when an element was selected again the panels were split again from
their size hints, so the height the user had dragged the panel to was
lost (reported by ankostis in #1439).
With the default size constraint the panel keeps its height and stays
blank while there is nothing to edit. The element, wire, text and shape
editors draw the same: their screenshots in a 730-860 px tall panel are
pixel-identical before and after.
Test: tst_selectionpanelheight (on master the height goes from 560 to
19 px).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Selecting several elements left the Selection properties panel blank and
Edit (Ctrl+E) disabled, so giving many elements the same supplier or
manufacturer meant editing them one by one.
The panel now shows the Information fields for two or more elements, or
for elements selected with other items (left unchanged, and counted in
the panel's header). Ctrl+E opens the same editor as a dialog when only
elements are selected.
- A field shows the value every element shares, or "Multiple values".
Left alone, each element keeps its own value; typed text goes to every
element; the erase box clears the field on every element.
- A restore button per field, and Reset, bring back the values the
elements had.
- One apply is one undo step. The keystrokes typed in the panel merge
into one step but never into an earlier change of the same elements
(a paste of element information, another edit).
- Numbering scheme, label freezing and the number picker are left out:
they are about one element. Custom properties are kept as they are.
Also:
- An element's own texts, selected with it by Select all, count as part
of the element, not as other items.
- ElementInfoWidget::reset(): Reset in the element dialog now brings back
the Information tab's values; it did nothing.
- The panel opens an element's editor on the tab picked last, instead of
the first tab, after the selection went to something else and back.
Test: tst_multielementinfo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The conflict was tests/qttest/CMakeLists.txt, where #1520 added
tst_savedstamps at the place this branch adds tst_joinedwireuuid. Both
are kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The symbol editor has no sheet to fit: its "Fit in view" action zooms on
the drawn parts (elementSceneGeometricRect()), which is what the folio
editor calls "Zoom content". Use that command's zoom-draw icon and name,
and give the action a status tip like the folio editor's.
The object name and the shortcut id stay the same, so saved shortcuts
and toolbar layouts keep working.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The project database's link table should say what each symbol saves:
QETZ-DB-FIRST stage 2a moves link reading and writing to it. Measured over
the corpus and 25 kinds of edit, its rows disagreed with the file for
three reasons; this fixes the one in the database:
- rows followed the order partners were built in, not the order the
symbol saves them (11 symbols, mostly .qetz files where an edit built a
partner's folio, with lazy folios);
- the rows were written by three pieces of code with three rules: the
rebuild and the flush from the built partners then the waiting ones,
the save from its own list.
Element::linksToSave() is now that one list: the order the symbol was
saved with, then partners linked since, a partner on a folio not built
included. toXml() writes it and the database's rows are written from it
(projectDataBase::insertLinkRows()), with their place in a new ord
column of the in-memory table (the .qetz file's own link table is not
touched). A built partner the project no longer has, on a removed folio,
still gets no row, as before.
The other two reasons are not here: symbols sharing a uuid (PR #1522)
and links saved to symbols on a removed folio (F113), which the save will
stop writing once it writes from the database (stage 2a-2).
QET_CHECK_LINK_ROWS=1 says at each save whether the rows are what the
symbols save, without building any folio. tst_resaveunchanged
linkRowsAfterLazyEdit moves a symbol of m_000 as a .qetz with lazy
folios: with the previous order, 2 symbols differ.
No saved byte changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XCdg2Er2vYhB8Vwa5G7fSR
Older versions' copy and paste could leave two symbols with the same
uuid. Wires, links and the project database name a symbol by its uuid,
so a copy was ambiguous: #1417 worked around it for wires, but the
project database keeps one row per uuid, and a link to the uuid could
mean either copy.
When a project opens, every symbol carrying a uuid another symbol also
carries gets a uuid of its own, derived from the shared one and its
folio's uuid, so the same file gives the same result on every open. The
copy that keeps the shared uuid is the first one linked to another
symbol (its partner's link names that uuid), else the first. It is done
once the folio's wires are connected, so #1417's choice of which copy a
wire meets is kept. Folios holding such a copy are built when the
project opens, also with lazy folios.
Only files with shared uuids change, once: 3 of the 341 corpus projects.
tst_sharedsymboluuid: the lamps given one uuid now come back with three;
a linked contact and an unlinked symbol sharing its uuid keep every link
and wire, the linked one keeping the uuid. Both fail without the fix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XCdg2Er2vYhB8Vwa5G7fSR
Deleting a symbol whose terminal held several wires joins the wires
left behind with a new one. That wire got a random uuid, so the same
deletion saved a different file every time: a diff for nothing in a
project kept in git (discussion #1512), and two copies with the same
edit could not be compared.
Its uuid now comes from its two ends (each end's symbol and terminal,
sorted), the recipe a wire saved without a uuid already gets on load
(Diagram::fromXml(), derivedItemUuid()). Undo and redo keep the same
wire, so its uuid too.
tst_joinedwireuuid deletes the coil KA1 of examples/affuteuse_250h.qet
in two runs: both saves are identical and no wire has a random uuid.
Without the fix one wire has a random uuid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XCdg2Er2vYhB8Vwa5G7fSR
The title block variables of a save (saveddate, saveddate-eu,
saveddate-us, savedtime, savedfilename, savedfilepath) were set after
the file was written, and also when a project was opened. A file
therefore held the previous save's date and path: after editing and
saving, the title block showed the date of the save before. And every
save but the first in a session wrote the time of the one before, so a
project saved twice without a change gave two different files, a diff
for nothing in a project kept in git (discussion #1512).
stampSaved() now sets them before writing, when the project has
unsaved changes (the modified flag or the undo stack moved since the
last save, as the window's "[modified]" uses) or is written to another
file. Otherwise they keep what the file holds, and opening a project no
longer sets them.
tst_savedstamps: an unchanged project saved twice in place is
byte-identical; a moved symbol then a save gives today's date and the
file's own path. Both fail without the fix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XCdg2Er2vYhB8Vwa5G7fSR
Windows CI stopped running tests (bdd3b2050) after these two failed:
- tst_reporttextafterinfo wrote "columns numbered from 1" to a .conf
under XDG_CONFIG_HOME, which only Linux reads. On Windows the setting
stayed in the registry, columns counted from 0 and the report read
1-C8. The setting now goes to an INI file QET_SETTINGS_DIR points at
(#1178), read on every system.
- tst_projectlock::readOnlyFolderDoesNotBlock relies on a read-only
folder refusing new files. Windows ignores that flag on folders, so
the lock was taken; the case is skipped there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QetAppTest::run() moved settings, configuration and data into a
temporary folder, but QStandardPaths still pointed at the user's own.
Two things follow on a developer's machine:
- qet-assistant.json, live-mode token included, is rewritten by every
app test as its editor opens.
- A .qetautosave file left in the user's data folder makes QETApp offer
to restore it in a modal dialog at start. Offscreen nobody answers,
so all six app tests hang until ctest times them out.
Test mode is now enabled first thing in run(). tst_assistantinfopaths
(#1513) did this in its own main(); it goes back to QET_APP_TEST_MAIN.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A terminal in a symbol's definition saved without its orientation
attribute crashed QElectroTech whenever the project was opened, in the
GUI and with --resave alike: Qet::orientationFromString() read the
first character of an empty string. Hand-edited or generated symbols,
or a damaged file, can hold such a terminal.
An empty string is now an inconsistency like any other, and the terminal
faces north, as the function's comment already said. Every non-empty
string gives the same orientation as before.
tst_terminalnoorientation runs the real binary's --resave on
fixtures/terminal_no_orientation.qet (unlinked_contact_label.qet with one
terminal's orientation removed): it crashes without the fix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XCdg2Er2vYhB8Vwa5G7fSR
In the realistic styles, some types of terminal get the parts seen on
the real products:
- fuse terminal: black hinged fuse holder on the middle of the terminal,
with its hinge pin, a window on the fuse, a ribbed grip and, for a
fuse terminal with led, a red led
- sectional (disconnect) terminal: orange knife with its hinge and grip
- test terminal: two brass test sockets between the wire entries and
the middle band (in the middle band for a terminal with levels)
The marker tag and the type symbol are drawn on the lever. The lever is
shorter with screw clamps and stays on the middle band for a terminal
with levels.
The realistic styles don't write the xrefs, so each terminal becomes a
clickable zone, like the xref text of the schematic style:
- double-click on a terminal goes to its element on the folio
- the terminal under the mouse is outlined in blue, and a tool tip
shows its label and its xref
- the zones are exported as clickable links in the PDF
A terminal with one level is clickable on its whole body; a terminal
with levels is clickable on the rows of wire entries of each level.
Only the terminals with an xref (element placed on a folio) are
clickable. The zones use the same physical / real indexes as the
schematic style, so the double-click and the PDF export need no
other change.
Add two realistic drawing styles for the terminal strip plan, besides
the existing schematic one:
- Realistic (push-in) and Realistic (screw): the strip seen from above,
at scale (real width of each terminal), like a cabinet mounting
drawing: end stops, group marker carrier with the strip name, one row
of wire entries per level, bridge channels with red bridges, marker
tags with the terminal number, type symbol (fuse, LED, diode,
sectional, ground...), green/yellow PE and terminal color.
The style is chosen for each plan in the "Add terminal strip plan"
dialog and saved with the item (attribute draw_style of
terminal_strip_item). Items saved before keep the schematic style.
Several plans of the same strip can use different styles.
The values used by the realistic styles are in the layout (scale in
px/mm, terminal length in mm, end stops, group marker) and can be set
in the layout editor, whose style combo box only changes the preview.
Separator plates: a new "Separator plate after" check box column in
the terminal strip editor, stored in the terminal element informations
(key terminal_separator). Drawn as a plate at scale in the realistic
styles and as a thick line in the schematic style.
New files: realisticterminalstripdrawer.{h,cpp} (realistic drawing) and
terminalstripdrawerhelpers.h (helpers shared with the schematic drawer).
GeneralConfigurationPage::applyConf() wrote qet-assistant.json before it
applied the collection and title block paths, so the file kept the paths
the dialog had just replaced. The qet MCP server hands the user and
company collections from that file to every headless run it starts: a
company collection chosen in the dialog stayed unknown there, and
company:// paths failed to resolve until QElectroTech was started again.
Write the file last. tst_assistantinfopaths picks a new company and user
collection on the page, applies it and reads the file back; it runs in
QStandardPaths test mode from main() on, since QETApp writes the file to
the standard data folder as its editor opens, whatever --data-dir says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With lazy folios, removing a folio left the tables on folios not built
yet as the file had them. A table split over several folios keeps its
query in its first part; when the folio holding that part was removed,
the next part, built later from the file, named a part that was gone and
was saved with no query. A parts list that listed the removed folio's
symbols also kept its old size. With every folio built neither happens.
Removing a folio that holds a table or a symbol now builds the folios
holding tables first, while the removed folio is still in the project.
A folio with neither builds nothing, as tst_lazyfolios checks.
buildFolios() now holds the project database's update signal back and
sends it once at the end, as pasting does: without that every item built
made every table lay itself out again, and the save after a removal on a
50-folio project took about 270 s instead of 4.5 s. The rows are still
written as each item is built, so no full refill of the database is
needed (one per build made a 136-folio project's edit 20 % slower).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XCdg2Er2vYhB8Vwa5G7fSR
The same 48 edits (industrial.qet and affuteuse_250h.qet, as .qet and
.qetz) are made with lazy folios and with QET_EAGER_FOLIOS=1; the saved
files must be equal but for the saved date and path. Answers the question
raised on #1440 about the hand-kept list of what marks a kept folio stale.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Translate the 2660 untranslated strings of qet_it.ts, including 91
plural forms (singular and plural), so the file no longer has any
unfinished entry.
- Reuse the terms already used in the file (pagina, elemento,
conduttore, terminale, morsetto/morsettiera, cavo, progetto,
cartiglio, collezione, riferimento incrociato, potenziale,
Installazione (=) / Posizione (+)); new cable terms: anima (core),
schermo (shield).
- Style editor colours translated as descriptive names, like the
existing ones.
- Keep placeholders (%1, %n, %{...}), HTML tags, accelerators and
line breaks.
- The "unfinished" drafts were retranslated from the current source
text.
- PlcLinkWidget: the English source "Log in" is translated as
"Collega" (link), which matches the context.
- Checked with lrelease: 4137 translations, none unfinished.
Machine-assisted translation: a review by a native speaker is welcome.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Translate the 1378 untranslated strings of qet_zh.ts, including 82
plural forms, so the file no longer has any unfinished entry.
- Reuse the terms already used in the file (图页, 元件, 导线, 端子排,
线缆, 工程, 标题栏, 库, 交叉引用, 电势); new cable terms: 芯线
(core), 屏蔽 (shield).
- Keep placeholders (%1, %n), HTML tags, accelerators and line breaks.
- The 134 "unfinished" drafts were retranslated from the current
source text.
- Checked with lrelease: 4137 translations, none unfinished.
Machine-assisted translation: a review by a native speaker is welcome.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every marker tag of a strip has the length of its longest terminal
number (at least two digits), so three-digit numbers are no longer
reduced. The font is still reduced if a number doesn't fit the text
zone of the layout.
- The xref is drawn as plain text, without marker tag.
- Every marker tag of the terminal numbers has the same length
(two digits), the font is reduced when the number is longer.
- The body of every terminal reserves the zone of the longest xref
of the strip, so all terminals of a strip are drawn the same way.
- Draw only the terminal number (part after the last ':'), the strip
name is already in the header; the manager and the tree still show
the full label.
- Clip texts to the width of their terminal and shrink the font in
narrow terminals so they no longer overlap.
- New housing: body with narrower wire entry caps and a slope between
them, grey by default (terminals with a color or a ground type keep
theirs); stripes are clipped to this shape.
- White marker tags behind the number and the cross reference, fitted
to the text and its alignment; black ribbed tags for isolating
(sectional) terminals.
- Clamping parts (screw in a window, castle tab, big center circle, tab,
screw) drawn in the free zone between the two tags, whatever the
layout; fewer parts if the zone is small.
- Remove the wire entry mark that overlapped the cross reference text.
- Draw only the terminal number (the part after the last ':') instead of
the full label (e.g. "1" instead of "XAUZ1:1"); the terminal manager
and tree still show the full label.
- Clip each text to the width of its terminal and shrink the font in
narrow terminals so that texts no longer overlap their neighbours.
- Remove the wire entry mark in the lower part of the terminal body,
which overlapped the cross reference text.
Extend the terminal catalog (terminalcatalog.h) with the widths of
more terminal blocks, from the catalogs of Phoenix Contact (UT),
Weidmuller (WDU), WAGO (TOPJOB S 2002/2006), ABB Entrelec (M series)
and Legrand (Viking 3). For Legrand and ABB Entrelec the value is the
pitch given by the catalog.
Each entry now carries its manufacturer, and the element properties
dialog gets a "Manufacturer" combo box before the width one. The list
of widths is filtered by manufacturer and by the selected section; if
the manufacturer has nothing for that section, all its terminals are
listed. The combo box is created by the code, no change in the .ui.
The section combo boxes of the terminal strip editor and of the
element editor now show the nearest AWG / kcmil size next to the
section in mm2, for example "2.5 mm² (AWG 14)".
The correspondence is approximate (the two systems don't have the
same steps) and is stored in TerminalCatalog::awgForSection().
The stored value is unchanged (terminal_section, in mm2).
Terminal block elements can now define, in the element properties
dialog (Terminal block element group):
- the section in mm2 (combo box with the usual sections, editable)
- the width / manufacturer reference (Weidmuller, Phoenix Contact,
WAGO, ABB Entrelec), filtered by the selected section
- the color, using the palette of the terminal strip (editable,
accepts a name, #rrggbb or #rrggbb/#rrggbb)
Values are stored in the element informations (terminal_section,
terminal_width, terminal_color) with the dot as decimal separator,
the keys already read by RealTerminal. An empty value removes the key.
The sections and the manufacturer widths move from
terminalstripmodel.cpp to the new header-only terminalcatalog.h,
shared by the terminal strip editor and the element editor.
Conflicts: the lock dialog and the recovery dialog both kept in
qetdiagrameditor.cpp; the lock and previous-versions settings both kept.
The spacer in the General settings page moves to row 9, below the lock
checkbox, which the merge had left on the same row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgPFANhWgD66V3v9KVKV58