The menu entry and its status tip in every translation file, added by
hand next to "Display or hide the grid of sheet" so that the files are
not rewritten by lupdate. German: "Fadenkreuz über die ganze Seite".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every formula evaluation constructed a QSettings, although only element
formulas read it (border-columns_0, for %c). Constructing QSettings is
costly, on Windows a registry lookup, and conductor labels are evaluated
in bulk: Conductor::refreshText() runs for every wire of a folio when it
is built, and for every wire of every built folio when the project or a
title block changes.
Debug build, 40 folios with 4,280 numbered wires:
- refreshText() of every wire: 1,136-1,264 ms before, 203-208 ms after
- opening the project (database fill): 2.24-2.48 s before, 1.46-1.59 s after
- building 39 lazy folios: 15.0-15.3 s before, 12.3-12.5 s after
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new option in the Display menu, off by default and kept in the
settings: two thin lines cross the whole folio, inside its border,
through the grid point under the mouse, so that one sees at a glance
whether symbols and terminals are in line, also while dragging them.
They are drawn by the view in drawForeground(), on screen only: no item
is added to the folio, so nothing of them is saved, exported or
printed. Only the strips the lines leave and enter are repainted, and
they go when the mouse leaves the view.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While no 3D mouse is plugged in, which for most users is always, the
hidapi backend looks for one every 3 seconds for as long as
QElectroTech runs. It asked hidapi for every HID device,
hid_enumerate(0, 0), and on Windows hidapi reads the serial number,
manufacturer and product strings of each device that matches what it
is asked for from the device itself. So every mouse, keyboard, headset
and webcam on the PC was asked for its names every 3 seconds, on the
GUI thread. Issue #1553 reports the pointer moving by itself on Windows
since the first package with this backend; nothing else in that package
talks to devices. SDL 2.0.9 polled the same way and saw freezes and a
keyboard that kept re-initialising (libsdl-org/SDL#3071).
The scan now asks for 3Dconnexion's vendor id, and for each 3D mouse
made under Logitech's id by its product id, since most other mice and
keyboards carry Logitech's id too. That is twelve hidapi calls instead
of one, but a device that does not match is no longer asked for its
names. On Linux and macOS hidapi takes the names from the system, and
one call is cheaper than twelve (on Linux, median of 200: 5 ms against
36 ms), so the scan there is unchanged.
Where the scan is filtered, a Logitech-id device that is not one of the
eleven 3D mice on the list is no longer found, even if it reports
itself as a multi-axis controller. 3Dconnexion's own mice and keyboards,
such as the CadMouse, are still asked: their vendor id is asked for as
a whole, so that new 3D mice are found without a list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add an automatic import which groups the free terminals of a project
by the part of their label before the first colon ("X1:2" goes to the
strip "X1"), creates the strips missing and sorts the terminals in
natural order, as one undoable step.
The import runs when the terminal block manager opens on a project and
can be run again from a new toolbar action. It only adds : the strips
already known keep their terminals and their order, and terminals
without label are left free.
A project opened before the element collection panel's first load is
queued until the load starts; one opened during a load goes into the
model being loaded. ElementsCollectionWidget::removeProject() only
removed the project from the model already shown, so a project closed
in either window stayed in the collection, and the next reload read the
freed project (crash in XmlElementCollection::root()).
removeProject() now also takes the project out of the queue and out of
the model being loaded. That model waits for its loading threads, which
read the project, and drops the project's items from the list applied
when the loading ends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closing a modified project and choosing Discard left the project in the
project tree. The panel removed its item, but the project still emits
projectInformationsChanged() while it is torn down (setModified()), and
GenericPanel answered by adding the project back, with items for its
sheets. Once the project was freed, a click on one of those sheets
called diagram->showMe() on a freed Diagram: SIGSEGV in
QObjectPrivate::maybeSignalConnected(), the backtrace reported on the
forum (topic 3215).
The panel now stops listening to a project when it is closed, to its
sheets and to its embedded title block collection, before removing its
item.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scorpio810's condition for the database as the model (#1512): a lossless
round trip between the formats, tested in CI. For every example and for
fixtures/every_feature.qet, the save stamps aside:
- a .qetz saved again is the same file;
- a .qetz saved as a .qet is the project saved straight as a .qet;
- that .qet saved as a .qetz is the project saved straight as a .qetz,
part by part, its database row by row;
- lazy folios save what every folio built saves, in both formats.
fixtures/every_feature.qet (arummler's test file, #1512) was built through
QElectroTech's script API: four folios, numbering schemes with every kind
of part, a coil linked to three contacts, folio reports, terminals, a
cable, wires with every property, every symbol information field, symbol
texts of every source, independent texts, shapes, a curve, a picture, a
PLC master with its inputs and outputs, a terminal strip bridged, a parts
list and a folio summary, copied, rotated and mirrored symbols. What the
API cannot set was added in the form QElectroTech saves it (new-folio
defaults, a folio field of the user's, single-line wires, a wire text
moved by hand, a coloured symbol text, a text group, an old information
key, a parts list continued on a fourth folio), then QElectroTech opened
and saved it. Of every tag and attribute QElectroTech wrote saving the
341 projects of our corpus, it holds all those found in 5 or more of them
and 98.3 % of all; it also holds what none of them has (terminal strips,
PLC data, tables).
It failed before the previous commit (the .qetz saved again had its folio
parts in another order).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A .qetz's folios kept as they were (not built since it was opened) had
their parts written first, then those of the folios built: saving the same
.qetz again with another folio built gave a zip with every part the same
and the parts in another order. Write them in the order of the folios,
the names of the parts kept still taken first so a folio built cannot
take one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The .qetz leaves out of its folios what QElectroTech computes when it
builds them (format 4): a symbol's terminal list and information texts,
a wire's description of its ends. A symbol whose definition the project
does not hold is never built, so none of it came back: through a .qetz,
the symbols and wires kept by the previous commit lost their terminal
lists, texts and end labels. QetContainer::dropComputed() now leaves a
symbol whose definition is not in the project's collection
(embeddedDefinitions()), and the wires ending on it, as they are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wire whose terminal is not matched is saved back as it was read (F128),
so the saved file holds it: the cases count the wires joined (saved, less
those QElectroTech says it did not load), and the cases that refuse a
match check the wire is still in the file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three title block templates for Russian-standard drawings, made by
arccis and shared on the forum (topic 3215):
- ESKD_Form1: main inscription for drawings and diagrams (form 1)
- ESKD_Form2_text: main inscription for text documents (form 2)
- ESKD_Form2a: inscription for the following sheets (form 2a)
The cell text is Russian, so it is tagged lang="ru" instead of the
lang="en" the files were saved with. QET shows the only translation a
cell has whatever the interface language, so they display the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a project dropped, with only a debug line, a symbol whose
definition could not be found (missing from the project's collection and
from the library) and every wire on it; and a wire whose terminal could
not be matched, after the symbol's definition was redrawn. The next save
wrote the project without them. One save of a forum upload whose
collection was incomplete kept 1 of 219 symbols and none of its 299 wires.
A folio now keeps what it could not load as it was read
(Diagram::m_kept_symbols, m_kept_wires) and writes it back when it is
saved whole (Diagram::writeKept()):
- a symbol, its terminals given ids after those of the folio's symbols;
- a wire, an end found written from its terminal as Conductor::toXml()
does, an end not found as the symbol and terminal uuids it names; for a
wire saved by legacy ids, the terminal's uuid derived from where it was
saved (Element::savedTerminalUuid(), out of matchMovedTerminals()), so
it joins again once the definition has it there. A wire whose ends
cannot be read, or an end naming no terminal of the file, is kept so
that it joins nothing.
A symbol kept counts as waiting (projectDataBase::waitingSymbols()), so a
partner keeps its link to it. The open warning now lists the symbols not
loaded too, and says both are kept.
Once the definition is back, the symbol and its wires are built again:
741.qet with one transistor definition removed (8 symbols, 33 wires on
them) saves all 67 wires, and with the definition put back opens with
all 65 symbols and 67 wires, the same folio as before.
tst_resaveunchanged::unloadableItemsKept: fixtures/coil_contact_wired.qet
(coil, linked contact, two wires; built with the script API) with the
coil's definition removed, or the contact's terminals moved and given new
uuids: both symbols, wires and links saved, a second save the same, and
the coil's definition put back gives the undamaged project's symbols and
wires. Fails on master (1 symbol; 0 wires).
FINDINGS F127, F128.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A symbol built after opening (lazy folios) links to the partners its
link rows name, and to the symbols whose rows name it; its file's list
only adds what the rows miss. The rows keep a partner on a folio not
built until it is built, in its saved place, so a symbol no longer keeps
a list of its own: the list read from the file is dropped once opening
has filled the database. pendingLinks(), waitingLinks(),
keepsSavedLinks(), linksToSave() and QETProject::waitingLinkTargets()
go; the callers that asked a symbol for its waiting partners ask the
database (waitingPartners()).
When its links change, a symbol's rows are written from its built
partners and the waiting partners its rows held, in their order
(rowsToWrite()), so a full rewrite of the table keeps them too.
A symbol built alone as a partner on a folio not built
(Diagram::buildSymbols()) counts as built: its rows are kept, it is not
waiting (waitingSymbols(), waitingPartners()), so saving does not build
its folio, and removing it does not remove the rows naming it. A row
naming a placed symbol that has not linked yet (its folio being built)
is kept too. At opening the database holds no link until its fill
(forgetLinks()); the symbols read their files' lists until then, and the
fill takes their order: a symbol's own rows, its list, then the rows
naming it. The symbols waiting on folios not built are cached and
updated as folios are built.
QET_CHECK_LINK_ROWS now checks the rows against the links: each built
partner in its order, no other row but to a folio not built.
tst_resaveunchanged::undoKeepsLinksToUnbuiltFolios: m_000 opened with
lazy folios, a contact, its folio or its coil's folio (not built)
removed, then undone: every link saved as before.
QETZ-DB-FIRST stage 2a-3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a HiDPI screen QIcon::pixmap() returns each requested size at the
screen's ratio, so the sizes handed to the tray no longer matched
16 ... 256. Requested at 1.0, as ProjectFileGP in qeticons.cpp does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The folio title was put into the formula before %c, %l, %prefix, the
wire variables, the title block and project variables and the
sequences, so a title such as "Schema %c" had them replaced inside it.
It is replaced after all of them now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
inheritedProperties() cleared the text of a conductor held by a cable,
on the premise that CableManager writes the core label into it. It
writes it into m_cable only (Conductor::setCableReference()); the text
is the potential's real wire number. Clearing it lost a typed number on
the new wire and made distinctProperties() see a choice between ""
and the number where the potentials agreed. The text is now cleared only
when it repeats the core label.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The level spinbox of the terminal strip editor allowed stages up to 6
while the strip drawing handles at most 4 stages (0 to 3), so a stage
entered there was simply not drawn. Cap the spinbox at 3 and keep the
editor consistent with the drawing and the level columns.
Opening the terminal block manager builds the folios not built yet (lazy
folios). This is done in one go without running the event loop, so the
window system said the opening message was "not responding".
- QETProject::setFolioBuildObserver(): a function called after each folio
built by buildFolios(), with the number of folios built and to build.
- The manager uses it to show "Reading folio n of N..." with a real
progress bar, and runs the event loop (without the user input) between
two folios, so the window system keeps getting answers.
- The autosave skips its turn while folios are being built, since the
event loop can now run then.
- The opening message only has a title bar: nothing to minimize, maximize
or close.
One pass of the event loop after show() was not enough: the message box
was not painted yet when the long work started (seen on Wayland), only
the busy cursor was visible.
Wait for the window of the message to be exposed by the window system
(500 ms at most), then paint it before building the manager.
Opening the terminal block manager reads every terminal of the project,
and builds the folios not built yet (lazy folios): on a big project it can
take a while, and the user could think that nothing happens.
TerminalStripEditorWindow::instance(), used by the menu action and by a
double click on a terminal strip of a folio, now shows a busy message
("Opening the terminal block manager...") and a wait cursor while the
window is built or switched to another project. Moved from the header to
the .cpp file.
The redraw on change was connected in setTerminalStrip() only, used when
an item is read from the project. An item added with the "Add terminal
strip" dialog is built with TerminalStripItem(strip), which did not
connect: it was not redrawn after the terminal strip editor was applied,
until the project was reopened.
Both constructors now go through connectStrip().
A spare (reserved) terminal is a terminal of a strip which is drawn on no
folio. RealTerminal already foresaw it ("a terminal set by user but not
present on any folio") but every terminal needed an element.
- RealTerminal::createSpare(): a terminal without element, with its own
data (label, type, function, led, informations: section, width, catalog
terminal, color, manufacturer...). Saved in the strip:
<real_terminal spare_uuid="..." type="..." function="..."><informations>
Older versions ignore these terminals.
- TerminalStrip::insertSpareTerminals() / insertTerminals(): insert at a
position of the strip. Undoable commands AddSpareTerminalsCommand and
ChangeSpareTerminalDataCommand.
- Terminal strip editor: "Add spare terminals..." button. The terminals
are inserted after the selected terminal (at the end if none), numbered
from its label ("X1:12" -> "X1:13", "X1:14", leading zeros kept), with
its properties if asked. The labels can be changed or removed in the
table, like the other cells; the Xref column shows "Spare". Moving a
spare terminal to the independent terminals removes it (undoable).
- Nomenclature: the spare terminals are in the new spare_terminal table,
added to element_nomenclature_view (UNION ALL, same columns), after the
folios, in the order of their strip. exclude_from_bom is honoured.
- The modified cells of the editor model are kept by real terminal, not by
element, since a spare terminal has none.
TerminalStripItem was connected to nothing: after applying the terminal
strip editor (width, catalog terminal, color, type...), undo or redo, the
strip drawn on the folio kept its old drawing until something else made
the scene repaint, and its bounding rect was not updated when the width of
the terminals changed.
- TerminalStrip::dataChanged() is emitted when the data of the strip is
set, and when the data of the element of one of its terminals changes
(Element::setElementData(), Element::setElementInformations()).
- TerminalStripItem connects to dataChanged(), orderChanged(),
bridgeChanged() and bridgeColorChanged() of its strip, and updates its
geometry and redraws itself.