Commit Graph

8858 Commits

Author SHA1 Message Date
Laurent Trinques f0b16fe650 Merge pull request #750 from ispyisail/feature/dxf-paint-device
Export the master-side cross-reference table to DXF
2026-08-15 06:19:34 +02:00
ispyisail d988054aca Export the master-side cross-reference table to DXF
Follow-up to #740, which fixed the slave-side "(n-Xn)" cross-reference
label. The master-side item - the small table/cross drawn next to a
report or master element, listing where each of its slaves is used -
was still missing from DXF export. Measured against examples/
industrial.qet with the PDF export as an oracle (renders the whole
scene, so it shows what should be there):

                          before  after   PDF
  slave xrefs  "(n-Xn)"       41     41    41   (already fixed, #740)
  folio/position strings     358    403   403

DXF now matches the PDF exactly.

## Why this needed a different approach than #740

The slave label is a plain QGraphicsTextItem - one string, trivial to
walk and re-emit as a single DXF TEXT entity, which is what #740 did.

The master-side item (CrossRefItem) is not: it paints itself with
~600 lines of hand-written QPainter calls across three modes
(drawAsCross/drawAsContacts/drawAsPlcTable), including a header
table, contact symbols, and rules. Hand-porting that logic to emit
DXF primitives directly would mean maintaining two divergent
implementations of the same drawing that have to be kept in sync by
hand forever.

## Approach: a QPaintEngine that intercepts CrossRefItem's own paint()

DxfPaintEngine/DxfPaintDevice (sources/dxfpaintdevice.{h,cpp}) is a
QPaintEngine/QPaintDevice pair - the same mechanism QPrinter and
QSvgGenerator use to redirect QPainter output elsewhere. Constructing
a QPainter on a DxfPaintDevice and calling item->paint() on it produces
DXF entities instead of pixels, using the exact same drawing code that
already renders correctly on screen. CrossRefItem::paint() is
unmodified.

Scope is deliberately narrow - only the QPainter calls CrossRefItem's
paint() is observed to make: drawLines -> LINE, drawRects/drawPath's
fill case -> outline-only LWPOLYLINE (no HATCH support in v1 - DXF's
fill primitive is a separate, more involved entity type; documented as
a known limitation rather than attempted here), drawEllipse -> CIRCLE
or a flattened polygon for rotated ellipses, drawPath's arc case (from
drawArc/drawPie) -> chord-flattened LINE segments, drawPolygon ->
LWPOLYLINE, drawTextItem -> TEXT. drawPixmap is intentionally
unimplemented (qWarning + skip) since CrossRefItem never calls it -
this is not a general-purpose DXF paint engine, and isn't meant to be
in this PR.

CrossRefItem::paint() is protected, per the normal QGraphicsItem
contract - added a small paintForExport() wrapper rather than making
paint() itself public, or reaching around access control.

## Explicitly out of scope

QetShapeItem::toDXF() and QetGraphicsTableItem::toDXF() (both already
implemented and working) are untouched. Rewriting working exporters
onto this engine to prove an architectural point would be a large,
unrelated diff with no user-visible benefit - if that consolidation is
wanted later, it's a separate proposal once this engine has shipped
and proven out on the one item that currently has no DXF export at
all.

## Testing

Built clean on Qt5/Linux. Verified via the GUI export dialog
(Fichier > Exporter > DXF) against examples/industrial.qet, 50 folios:
export completes without error or crash, all 50 .dxf files are
structurally well-formed (balanced SECTION/ENDSEC, single EOF each),
and grepping the folio-position pattern gives the before/after/PDF
numbers above. Spot-checked several real label strings (e.g. "18-B18",
"20-A2") present as TEXT entity values in the output, not just an
artifact of the count matching.
2026-08-15 08:39:55 +12:00
Laurent Trinques 1743f342ce Merge pull request #748 from Kellermorph/Fix-Master-Slave
Fix slave contact groups being trimmed to one when reopening element properties
2026-08-14 12:55:12 +02:00
Laurent Trinques 2c69bf0dad Update windows-build.yml
Fix   error: target not found: mingw-w64-ucrt-x86_64-kwidgetsaddons-qt5
error: target not found: mingw-w64-ucrt-x86_64-kcoreaddons-qt5
2026-08-14 11:04:54 +02:00
Laurent Trinques 8bda0a1821 CI(windows): enable KF6 in Qt6 job, fix KF package mismatch in Qt5 job
- build-windows-qt6: install kwidgetsaddons/kcoreaddons/extra-cmake-modules,
  switch -DBUILD_WITH_KF=OFF to ON, add -DBUILD_KF=OFF to use precompiled
  MSYS2 packages instead of building KF6 from source via FetchContent
- build-windows: swap unsuffixed kwidgetsaddons/kcoreaddons (actually KF6
  packages after MSYS2's renaming) for the -qt5 suffixed ones, add
  -DBUILD_KF=OFF so the installed packages are actually consumed instead
  of being ignored by the default FetchContent-from-source build
2026-08-14 10:43:21 +02:00
Kellermorph fe6191f26d Fix slave contact groups being trimmed to one when reopening element properties 2026-08-14 10:31:15 +02:00
Laurent Trinques 8a71649e87 Merge pull request #744 from ispyisail/fix/bugtracker-335-dark-theme-collection-icons
Fix bugtracker #335: element icons invisible on dark themes
2026-08-14 10:02:39 +02:00
Laurent Trinques 46aed59b8b Merge pull request #743 from ispyisail/fix/bugtracker-291-cancel-during-collection-load
Fix bugtracker #291: crash on cancelling open-element dialog before collection load finishes
2026-08-14 09:55:10 +02:00
ispyisail 6326cb3789 Fix bugtracker #335: element icons invisible on dark themes
https://qelectrotech.org/bugtracker/view.php?id=335

## Bug

Element library icons (collection tree thumbnails, drag icon, preview
panels) render with a fully transparent background. Element definitions
almost always hardcode a black stroke color, on the assumption of the
white diagram sheet they are normally drawn on. Against a dark widget/
tree-view background (e.g. KDE Plasma dark theme), that black stroke
disappears entirely - reported as icons being "black and almost
invisible". scorpio810_mantis linked this to the same recurring family
as #231, #247, #267.

## Fix

ElementPictureFactory::pixmap() is the single shared point where every
consumer of these icons gets its QPixmap (collection tree via
ElementsCollectionCache -> Element::pixmap(), master/slave properties
tree, element properties preview, drag icon). Change its background
fill from fully transparent to opaque white - exactly what the element
already visually assumes in every context this pixmap is used, so it
is correct regardless of the surrounding widget's palette.

## Testing

Built both variants and compared under Xvfb using a simple, decisive
visual test: select the tree row (giving it a highlighted/colored
background) and compare what shows immediately around the icon's
glyph.

- Before: the icon's background matches the row's selection color -
  confirms it is transparent, so on a dark unselected row the black
  strokes would have the same problem.
- After: a solid white square is visible behind the glyph regardless
  of the row's background color.

Note for the on-disk pixmap cache used by ElementsCollectionCache
(~/.local/share/QElectroTech/QElectroTech/elements_cache.sqlite):
existing cached PNGs predate this fix and will keep their transparent
background until regenerated. That cache already keys strictly on
path+uuid with no invalidation on QET version, so this is an existing
characteristic of that cache, not something introduced here.
2026-08-14 14:22:01 +12:00
ispyisail 39ac5716c7 Fix crash on cancelling the open-element dialog before collection load finishes
Bugtracker #291: clicking Cancel on the open/save-element dialog before
the user collection finishes loading crashes the whole application with
an unhandled pointer exception.

ElementsCollectionModel::loadCollections() loads collections in the
background via QtConcurrent::map(m_items_list_to_setUp, setUpData) -
worker threads call setUpData() on each ElementCollectionItem
(a QStandardItem), which does setFlags()/setData() on it.

ElementDialog::execConfiguredDialog() deletes the dialog immediately
after exec() returns:

  element_dialog->exec();
  ...
  delete element_dialog;

That destroys the tree view and its ElementsCollectionModel, which as
a QStandardItemModel frees all its items in its destructor. Nothing
waited for the QtConcurrent::map() to finish first, so on Cancel before
loading completes, background threads were still calling setUpData()
on items the main thread had just freed - a use-after-free race.

Add an ElementsCollectionModel destructor that waits for the future
before QStandardItemModel's destructor runs. QFuture::waitForFinished()
on a default-constructed (never-started) future returns immediately, so
this is a no-op whenever loading already completed - the crash path is
the only one affected.
2026-08-14 13:08:45 +12:00
Laurent Trinques f83ed508e3 Merge pull request #705 from arummler/master-update-more-signal-slot
Continued signal/slot migration
2026-08-13 21:40:30 +02:00
Laurent Trinques ab88937ac9 git submodule update --remote elements 2026-08-13 21:35:58 +02:00
plc-user 1cf58e3caf Merge pull request #740 from ispyisail/fix/dxf-export-slave-xref
Export slave cross-reference labels to DXF
2026-08-13 18:40:08 +02:00
plc-user 26f6f6d36e Merge pull request #727 from zi-mozhuang/zh
Fix print window clipping diagram when titleblock on right edge is hidden.

Fixes a frequently made mistake: confusing width and height when rotating something...   😉
2026-08-13 18:04:19 +02:00
plc-user 293dc92b41 Merge pull request #738 from ispyisail/fix/exclude-from-bom-ghost-row
Fix nameless "false" row in the element Informations panel
2026-08-13 17:56:40 +02:00
Andre Rummler 6d05bc2f21 Remove all Qt version checks and branches for <5.15.12 as such versions are no longer supported. 2026-08-13 16:20:30 +02:00
Andre Rummler 7ba295a339 Remove all Qt version checks and branches for <5.14.0 as such versions are no longer supported. 2026-08-13 15:45:15 +02:00
Andre Rummler deba8d0e4e Merge branch 'master' into master-update-more-signal-slot 2026-08-13 13:07:53 +02:00
ispyisail 2e1ba46430 Export slave cross-reference labels to DXF
Cross-references were missing from DXF exports, as reported on the
forum: https://qelectrotech.org/forum/viewtopic.php?id=2481

generateDxf() walks the scene and collects items by cast. A slave
element's cross-reference label ("(6-G15)", pointing back to its master)
is a plain QGraphicsTextItem hung off a DynamicElementTextItem as a
child, so it matches neither the IndependentTextItem nor the
DynamicElementTextItem branch and was dropped on the floor. Nothing was
wrong with the label itself; it was simply never collected.

Collect it through the existing DynamicElementTextItem::slaveXrefItem()
accessor and draw it with the same placement, rotation and multi-line
handling as the other text items, using defaultTextColor() since a bare
QGraphicsTextItem has no DiagramTextItem::color().

Measured on examples/industrial.qet, comparing against the PDF export
(which renders the whole scene and so shows everything):

                          before   after   PDF
  slave xrefs "(n-Xn)"         0      41    41
  folio/position strings     317     358   403

The slave cross-references now match the PDF exactly.

Still missing, and not addressed here: the master-side cross-reference
table drawn by CrossRefItem, which accounts for the remaining 45
strings. CrossRefItem is a QGraphicsObject that renders itself with
custom QPainter code in three different modes (drawAsCross,
drawAsContacts, drawAsPlcTable) including contact symbols and rules, so
giving it a DXF representation is a larger piece of work than this.
2026-08-13 22:01:37 +12:00
ispyisail 61a509e5d8 Fix nameless "false" row in the element Informations panel
"exclude_from_bom" is listed in QETInformation::elementInfoKeys() so the
project database can build the element_info table column for it, but it
is not a free-text property: ElementInfoWidget already gives it its own
"Exclure de la nomenclature" check box.

Because buildInterface() creates one ElementInfoPartWidget per key in
that list, the key also got a second, generic edit row. And since
translatedInfoKey() has no case for it and falls through to
"return QString()", that row carries no label at all - an anonymous edit
line at the bottom of the panel. currentInfo() then writes
exclude_from_bom unconditionally, so as soon as the user edits anything
the nameless row fills with "true"/"false".

Drop the key from the list buildInterface() iterates. The check box
remains the only way to set it, currentInfo() still writes it exactly as
before, elementInfoKeys() is untouched so the database schema and
elementquerywidget are unaffected, and predefinedKeys() already excluded
it from the custom-property rows.

Reported by plc-user on #642.
2026-08-13 21:24:50 +12:00
Laurent Trinques 2186d2733f Merge pull request #709 from arummler/master-continue-qt6-migration
Qt6 migration beyond signal/slot
2026-08-13 10:55:47 +02:00
plc-user b7efbdc323 Merge pull request #726 from ispyisail/fix/bugtracker-333-multiselect-text-color
Fix bugtracker #333: selecting several dynamic texts overwrites their colours
2026-08-13 09:39:36 +02:00
plc-user b00e053dab Merge pull request #725 from ispyisail/fix/pdf-export-filename-multiple-dots-forum3005
Fix PDF export truncating project filenames at the first dot
2026-08-13 09:22:48 +02:00
子墨庄 b0172f4dd7 Change comment style for title block edge method
Updated comment to use Doxygen style for documentation.
2026-08-13 08:50:24 +08:00
Andre Rummler 1de9f7eaf0 The Qt6 CMake signatures for translation handling changed multiple times with minor versions. 2026-08-12 22:37:46 +02:00
Andre Rummler d2a921ea80 Fixing MacOS only GUI adjustment relying on QWheelEvent->delta() which was removed in Qt6. Some simplification was possible but no test due to lack of OS.
Discovered due to new CI.
2026-08-12 12:29:56 +02:00
Andre Rummler a4d23fe312 QUuid and QHash no longer transititve. Adding includes explicitly. 2026-08-12 12:29:56 +02:00
Andre Rummler 8cfb777fe1 Fixing the translation installation for Qt5. Messed up the order of two lines. 2026-08-12 12:29:56 +02:00
Andre Rummler 8d08c3fd56 Replacing depreciated qAsConst with std::as_const 2026-08-12 12:29:56 +02:00
Andre Rummler 766a6b981d QUuid not transitively included in Qt6; added explicit includes. 2026-08-12 12:29:56 +02:00
Andre Rummler 8b40134a47 sources/qetgraphicsitem/ViewItem/projectdbmodel.cpp
QVector<int>(Qt::DisplayRole) creates a vector of size 0 (since  Qt::DisplayRole == 0), not a vector containing DisplayRole. Fixed
to {Qt::DisplayRole}.

The sam applies to QVector<int>(role) which gets changed to {role}.
2026-08-12 12:29:56 +02:00
Andre Rummler cc032c9c76 Fixing comment removing the long dash that AIs tend to use. 2026-08-12 12:29:56 +02:00
Andre Rummler 92b2608bfb Comments in CMake with # and not //... always remember the language you are currently using. 2026-08-12 12:29:56 +02:00
Andre Rummler af87d28e33 Fix several parameters after Qt6 migration. 2026-08-12 12:29:56 +02:00
Andre Rummler c5d714089f Replacing KF5 missed in shell print out. 2026-08-12 12:29:56 +02:00
Shane Ringrose 206c37f620 cmake(qt6): verify Qt6::GuiPrivate at configure time, drop #warning
<private/qpdf_p.h> (QPdfEngine::drawHyperlink) needs Qt's private GUI
module, previously flagged only by a #warning at compile time.

Qt >= 6.7 ships GuiPrivate as a proper find_package component, but some
distro packages (e.g. Ubuntu's qt6-base-private-dev, Qt 6.8.3) do not
install Qt6GuiPrivateConfig.cmake and only provide the implicit
Qt6::GuiPrivate target created alongside Qt6::Gui. Requesting the
component unconditionally would therefore break distro-Qt builds.

Instead: try the component quietly, then hard-verify the Qt6::GuiPrivate
target exists after the main find_package, failing at configure time
with an actionable message if the private headers are missing. The
compile-time #warning in pdf_links.cpp and projectprintwindow.cpp is
now redundant and removed.

Verified: cmake configure + compile of both translation units on
Ubuntu 25.04 / Qt 6.8.3 (system KF6), cmake configure on Qt 5.15.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 12:29:56 +02:00
Andre Rummler c14d6a6dd6 In order to migrate to Qt6 all options for KF6 were added:
a) using a system provided KF6
b) downloading and compiling KF6
c) using the vendored-in re-creation of the functionality

The behaviour for both Qt5 and Qt6 is steered with the same two variables which were renamed to become version agnostic:
a) BUILD_WITH_KF=ON BUILD_KF=OFF
b) BUILD_WITH_KF=ON BUILD_KF=ON
c) BUILD_WITH_KF=OFF

The version is automatically derived from the chosen Qt major version.
2026-08-12 12:29:56 +02:00
zi-mozhuang e32e5595c0 syn 2026-08-12 15:20:03 +08:00
zi-mozhuang 1684d2cfe6 syn 2026-08-12 15:13:52 +08:00
zi-mozhuang 6f66745ce3 Apply minimal title block fix to zh (from c77f78e4a) 2026-08-12 15:07:52 +08:00
ispyisail 762bd7febf Fix bugtracker #333: selecting several dynamic texts overwrites their colours
Selecting more than one dynamic text field in the element editor silently
replaced every selected field's colour with the colour of the first one.
Nothing was clicked -- merely extending the selection destroyed the others'
colours, and the change went onto the undo stack as if the user had asked
for it.

Cause: updateForm() loads the current part's colour into the colour button
with m_color_kpb->setColor(). KColorButton::changed is emitted for a
programmatic setColor() just as it is for user interaction, and it is
connected to m_color_kpb_changed(), which applies the new colour to *every*
part in m_parts. So simply displaying the first part's colour wrote that
colour to all the others.

Every other widget in updateForm() is immune because it is wired to a
user-only signal -- on_m_x_sb_editingFinished(), on_m_frame_cb_clicked() --
which setValue() and setChecked() do not emit. The colour button is the one
control whose signal cannot distinguish the two, so block it while loading.

This also explains why the reporter saw it only when rubber-band selecting
bottom-to-top: the write happens only when the first part's colour differs
from what the button already shows, which depends on selection order.

Verified in the element editor with two dynamic texts, one red and one blue:
select the red one, then ctrl-click the blue one. Before: the blue text
turned red. After: both keep their colours. Changing the colour deliberately
with the button still applies to all selected texts, as intended.
2026-08-12 15:53:30 +12:00
ispyisail 162d43e1c9 Fix PDF export truncating project filenames at the first dot
Forum report (qelectrotech.org/forum, topic 3005, reporter oc67): a
project named e.g. "Mon_projet.avec_un_point.qet" exported to PDF as
"Mon_projet.pdf" - everything after the first "." in the filename was
silently dropped.

Root cause: ProjectPrintWindow::docName() used QFileInfo::baseName(),
which returns the filename up to the FIRST "." rather than stripping
only the final suffix. docName() feeds both the PDF print job's
setOutputFileName() and the QFileDialog::getSaveFileName default in
exportToPDF(), so the truncation showed up as the actual exported
file's name, not just a dialog suggestion.

sources/exportdialog.cpp's SVG/PNG/DXF export path already uses the
correct QFileInfo::completeBaseName() (strips only the last suffix) -
this fix brings the PDF/print path in line with that existing,
correct pattern rather than introducing a new approach.

Verified the exact before/after behavior with a standalone QFileInfo
test: baseName() on "Mon_projet.avec_un_point.qet" returns
"Mon_projet" (the bug); completeBaseName() returns
"Mon_projet.avec_un_point" (correct). Also did a clean incremental
build with no new warnings/errors.
2026-08-12 10:31:04 +12:00
plc-user 10cc162c4e Merge pull request #715 from ispyisail/fix/properties-dialog-mac-sheet-bug275
Fix bugtracker #275: element properties window stuck centered on macOS

That addition definitely makes sense!
2026-08-11 17:33:56 +02:00
plc-user 3a3182862c move "diagnostics_action_" upwards in help-menu 2026-08-11 14:11:00 +02:00
plc-user 0d432f6159 Merge pull request #714 from ispyisail/fix/newpart-wizard-window-focus-bug281
Fix bugtracker #281: new-part wizard's element editor opens behind main window
Cannot reproduce on Debian/GNU Linux, but implementation is reasonable and clean!
2026-08-11 13:02:22 +02:00
plc-user 45458c2f2a Merge pull request #717 from ispyisail/fix/titleblock-invalid-name-bug251
Fix bugtracker #251: title block template with slash in name fails silently
There are some characters that are not allowed in filenames.
Absolutely correct to mark a filename containing (one of) them as invalid!
2026-08-11 12:52:52 +02:00
plc-user 472786fd23 Merge pull request #712 from ispyisail/fix/xref-default-alignment-bug296
Fix bugtracker #296: cross-reference text overlaps element label by default
Sounds and looks reasonable!
2026-08-11 12:47:42 +02:00
plc-user 0732bf64a7 Merge pull request #716 from ispyisail/fix/saveas-qet-extension-bug270
Fix bugtracker #270: Save As on Snap produces file with no .qet extension

Absolutely correct implementation for portability!
2026-08-11 12:42:24 +02:00
ispyisail 2d889568a5 Fix bugtracker #251: title block template with slash in name fails silently
Saving a new user title block template (right-click "Cartouches
utilisateur" > "Nouveau modèle" > "Enregistrer sous") with a name
containing a slash (or other filesystem-reserved character) silently
did nothing, with no error shown. The entered name is turned directly
into a filename (TitleBlockTemplatesFilesCollection::toFileName()), so
e.g. "foo/bar" becomes a path "foo/bar.titleblock" -- since "foo/"
essentially never exists as a directory, the underlying file write
fails, but that failure was never surfaced:

- TitleBlockTemplateLocation::isValid() only checked for an empty
  name, so an invalid name still counted as "valid" and got passed
  through to save.
- QETTitleBlockTemplateEditor::saveAs(const TitleBlockTemplateLocation&)
  discarded the bool result of setTemplateXmlDescription() and
  unconditionally returned true, marking the undo stack clean as if
  the save had actually succeeded.

Fix:
- isValid() now also rejects names containing \ / : * ? " < > |,
  matching the character set that's actually unsafe once the name
  becomes a filename.
- saveAs() (the no-arg entry point that asks the user for a location)
  now shows a clear error dialog when the entered name is rejected,
  distinguishing "user cancelled" (location.name() empty) from
  "name was invalid" (non-empty but rejected by isValid()).
- saveAs(location) now checks setTemplateXmlDescription()'s return
  value and shows an error dialog instead of reporting false success
  on any future/other write failure, not just this one.

Verified: clean rebuild, only the intended files recompiled and
linked successfully. Live-tested under Xvfb: creating a new template
and using "Enregistrer sous" with the name "foo/bar" now shows
"Le nom « foo/bar » n'est pas valide : il ne doit pas contenir les
caractères suivants : \ / : * ? " < > |" instead of silently doing
nothing; reopening the save-as dialog afterward showed the name field
correctly empty (nothing was partially written). Saving again with a
valid name ("mytemplate_valid") completed with no error dialog, and
the resulting mytemplate_valid.titleblock file was confirmed present
on disk in the user's title-block collection directory.
2026-08-11 12:36:31 +12:00
ispyisail 55250a6de9 Fix bugtracker #270: Save As on Snap produces file with no .qet extension
ProjectView::askUserForFilePath() only appended the .qet extension when
FLATPAK_ID/SNAP_NAME were NOT set, on the assumption that the
xdg-desktop-portal file dialog used by sandboxed Snap/Flatpak builds
always appends the selected filter's extension itself (avoiding a
double ".qet.qet"). In practice, on the reporter's Snap/Ubuntu 22.04
setup the portal dialog does not append it, so the environment-based
skip left Save As producing a file with no extension at all.

Portal behavior isn't something QET controls or can reliably detect via
environment variables -- it depends on the desktop's actual portal
implementation/version. Rather than guessing per-environment, normalize
unconditionally: strip any existing .qet suffix (case-insensitive) and
re-append exactly one. This produces the correct single extension
whether or not the dialog already added it, on every environment.

Verified: clean rebuild, only the intended object file recompiled and
linked successfully. Wrote a standalone test of the normalization logic
covering no-extension, already-has-extension, uppercase-extension, and
a literal dot in the base filename -- all four produced exactly one
correct ".qet" suffix with no double-extension and no missing extension.

Not verified: the actual Snap-sandboxed portal dialog behavior itself,
since building/running the Snap package and testing its file-save
dialog under a portal is outside what's practical to set up in this
sandbox. Confidence rests on the fix removing the environment-guessing
entirely in favor of unconditional, dialog-implementation-agnostic
normalization, which is correct regardless of what the underlying
dialog does.
2026-08-11 12:01:24 +12:00