Compare commits

...

21 Commits

Author SHA1 Message Date
Laurent Trinques c3b0e6725e Merge pull request #896 from ispyisail/feature/886-custom-sql-reports
Add read-only enforcement, preview and import/export to custom SQL reports
2026-09-17 12:38:17 +02:00
Laurent Trinques 3f8e6418d0 Merge pull request #881 from Kellermorph/checkbox-master-slave
Add Hide full masters checkbox to slave linking widget
2026-09-17 12:30:42 +02:00
Laurent Trinques a86fe2a46b Merge pull request #902 from ispyisail/fix/901-crash-report-never-offered
Offer the crash report after the recovery prompt, not instead of it
2026-09-17 12:27:10 +02:00
Laurent Trinques 206edcbb7d Merge pull request #899 from arummler/fix-move-insert
Fix paste problems
2026-09-17 12:25:44 +02:00
Laurent Trinques 0a7ea032d9 Revert "bump KDE KF6 frameworks version to 6.28.0 stable release"
Close https://github.com/qelectrotech/qelectrotech-source-mirror/issues/900
This reverts commit ff3c2e0916.
2026-09-17 12:18:10 +02:00
ispyisail 52713704bc Offer the crash report after the recovery prompt, not instead of it (#901)
QETApp::checkBackupFiles() only reached checkCrashDump() when there was
nothing to recover:

    if (stale_files.isEmpty()) {
        checkCrashDump();
        return;
    }

A crash with a project open always leaves a stale KAutoSaveFile, so on the
next launch the recovery prompt won every time and the dump sat on disk
unoffered -- the report was unreachable in exactly the case it is most
wanted. Reported by ChuckNr11 as a side note in #898: "the report appeared
only once despite there being 10 or more crashes". It appears on the runs
that happen to have nothing to recover.

Discussion #644 step 5 asks that the two prompts never show at the same
time, which this keeps: the recovery prompt is answered first, then the
report. The recovery half moves into offerBackupFiles() so both paths fall
through to the same place.

Verified under Xvfb, from a real crash state (SIGABRT with a project open,
leaving both a stale file and a 5.4 KB crash_dump.log): the recovery prompt
appears, and dismissing it now brings up "Rapport de plantage" carrying the
version, git SHA, OS, Qt version and the log ring. Before this change the
report never appeared -- that half rests on the four lines above rather
than on a captured before/after, since re-creating the crash state for a
clean baseline run kept consuming it.

Not addressed: the dump is a single fixed path opened O_TRUNC, so
consecutive crashes still overwrite one another.

ctest 8/8.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:10:03 +12:00
Andre Rummler 923723a9de Fix the following issues:
a) paste
- conductor does not move
- pressing escape does not abort
- pressing anything during the process crashes the program instead of aborting
b) similar issue with the escape not working fixed for graphics items, texxt fields (and probably others)
2026-09-17 11:34:25 +02:00
Laurent Trinques ff3c2e0916 bump KDE KF6 frameworks version to 6.28.0 stable release 2026-09-17 06:43:19 +02:00
Laurent Trinques adca07f26f Merge pull request #880 from arummler/remove-qt5-instruction
Remove Qt5
2026-09-17 06:35:57 +02:00
ispyisail 12ecf6b28f Add read-only enforcement, preview, import/export to custom SQL reports
Refs discussion #886: a saved-report manager built on custom SQL and
nomenclature.json. Most of what was asked for already existed --
Projet > Exporter au format CSV already builds/saves/reuses named
SELECT queries via ElementQueryWidget and nomenclature.json, and
Projet > Ajouter une nomenclature already inserts one into a folio.
This fills the three real gaps.

- projectDataBase::isReadOnlySelect() rejects anything that isn't a
  single SELECT/WITH statement. Checked in newQuery() itself, the one
  choke point every query path already goes through -- including a
  query loaded from a saved nomenclature/summary table's <query>
  element on project open, not just the dialog's own custom-SQL box.
  ElementQueryWidget shows the same check live as you type, and
  BOMExportDialog surfaces it before running or exporting anything.
- BOMExportDialog gains a Preview button + table (QSqlQueryModel),
  so a report can be checked on screen before committing to a CSV file.
- ElementQueryWidget gains Importer.../Exporter... buttons that
  read/write nomenclature.json's saved reports as a JSON file, so a
  report can be handed to a colleague or another install. Import asks
  before overwriting a locally-saved report of the same name.

Verified: Qt 6.10.2, builds clean, ctest 8/8. Drove the real dialog
through Xvfb: typed "DROP TABLE element" into the custom-SQL box and
got the inline warning immediately, then confirmed Preview also
refuses it with a "Requête refusée" dialog rather than running it.
Preview against the real default query returned live column headers
and a row. Export opens a save dialog without crashing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 10:09:14 +12:00
Andre Rummler 1ee828da5b Remove titleblocktemplate cahing remains. 2026-09-17 00:07:46 +02:00
Andre Rummler 837e1e7f09 Remove caching in titleblocktemplaterenderer which has not been used since Qt4.8. 2026-09-17 00:07:46 +02:00
Andre Rummler 25962118a2 Disable cacching which was enabled accidentially during Qt5 removal. 2026-09-17 00:07:46 +02:00
Andre Rummler 35275de4cc Remove blank lines. 2026-09-17 00:07:46 +02:00
Andre Rummler d641b964bd Remove very old Qt4 code path. 2026-09-17 00:07:46 +02:00
Andre Rummler e90dddbbc6 Add missing Qt6 code path and remove Qt5. 2026-09-17 00:07:46 +02:00
Andre Rummler ce8884394a Remove all code switches for Qt<6 with one exception: caching in titlebordertemplate. Function for caching not called since Qt4; might profit from complete removal.
Another exception: one non-converted code path in projectprintwindow.cpp to be followed-up.

One issue found in the Qt6 code path of diagramview.cpp which has been fixed.
2026-09-17 00:07:46 +02:00
Andre Rummler 8606cc5328 Removing Qt5/KF5 from build system. 2026-09-17 00:07:46 +02:00
Andre Rummler 60d9299d45 New installation instructions for Qt6/CMake. Proposal: translations as INSTALL_XY.md 2026-09-17 00:07:46 +02:00
Kellermorph e6ffc8b622 Address all review feedback 2026-09-16 11:37:27 +02:00
Kellermorph c2f405c32d Add Hide full masters checkbox to slave linking widget 2026-09-15 21:48:47 +02:00
49 changed files with 980 additions and 788 deletions
+46 -97
View File
@@ -28,25 +28,6 @@ include(cmake/copyright_message.cmake)
set(QET_DIR ${PROJECT_SOURCE_DIR})
# QT_VERSION_MAJOR is chosen explicitly by whoever configures the build,
# via -DQT_VERSION_MAJOR=5 or -DQT_VERSION_MAJOR=6.
# Default to Qt5 (current stable) when not specified, so existing
# CI/scripts that don't pass this option keep working unchanged.
# This must happen BEFORE add_subdirectory(tests) and the fetch_*.cmake
# includes below, so every subdirectory and every FetchContent dependency
# sees a consistent, already-defined value.
if(NOT DEFINED QT_VERSION_MAJOR)
set(QT_VERSION_MAJOR 6)
endif()
# Some third-party CMake projects we pull in via FetchContent (e.g.
# SingleApplication) don't know about QET_VERSION_MAJOR: they follow Qt's
# own "QT_DEFAULT_MAJOR_VERSION" convention instead, and silently default
# to Qt5 if it isn't set. Propagate our choice so they stay in sync,
# otherwise they can end up linked against a different Qt major version
# than the rest of QET, which breaks AUTOMOC with an
# INTERFACE_QT_MAJOR_VERSION mismatch at generate time.
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (5 or 6)" FORCE)
include(cmake/paths_compilation_installation.cmake)
include(cmake/start_options.cmake)
@@ -65,7 +46,7 @@ SET(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(
Qt${QT_VERSION_MAJOR}
Qt6
COMPONENTS
${QET_COMPONENTS}
REQUIRED)
@@ -76,12 +57,9 @@ find_package(
# and only provide the implicit Qt6::GuiPrivate target created alongside
# Qt6::Gui. Try the component quietly, then verify the target below so a
# missing private-headers package fails here instead of at compile time.
# Qt5 has no such component as its GuiPrivate target always exists once Gui is found.
if(QT_VERSION_MAJOR GREATER_EQUAL 6)
find_package(Qt6 QUIET COMPONENTS GuiPrivate)
endif()
find_package(Qt6 QUIET COMPONENTS GuiPrivate)
if(QT_VERSION_MAJOR GREATER_EQUAL 6 AND NOT TARGET Qt6::GuiPrivate)
if(NOT TARGET Qt6::GuiPrivate)
message(FATAL_ERROR
"Qt6::GuiPrivate was not found. It is required for PDF hyperlink "
"support (<private/qpdf_p.h>). Install the Qt6 private headers "
@@ -98,15 +76,13 @@ endif()
# see the QT_VERSION_CHECK / QET_HAS_QTPDF guards in diagrameventaddpdf.*
# and pdfpagesdialog.*.
set(QET_HAS_QTPDF FALSE)
if(QT_VERSION_MAJOR GREATER_EQUAL 6)
find_package(Qt6 QUIET COMPONENTS Pdf)
if(TARGET Qt6::Pdf AND NOT Qt6_VERSION VERSION_LESS 6.4.0)
set(QET_HAS_QTPDF TRUE)
list(APPEND QET_PRIVATE_LIBRARIES Qt::Pdf)
add_compile_definitions(QET_HAS_QTPDF)
else()
message(STATUS "QtPdf module not available (or Qt < 6.4): PDF page import feature disabled")
endif()
find_package(Qt6 QUIET COMPONENTS Pdf)
if(TARGET Qt6::Pdf AND NOT Qt6_VERSION VERSION_LESS 6.4.0)
set(QET_HAS_QTPDF TRUE)
list(APPEND QET_PRIVATE_LIBRARIES Qt::Pdf)
add_compile_definitions(QET_HAS_QTPDF)
else()
message(STATUS "QtPdf module not available (or Qt < 6.4): PDF page import feature disabled")
endif()
# JavaScript scripting (bugtracker #162: `--run script.js`, and later a
@@ -139,11 +115,6 @@ endif()
set(CMAKE_AUTOUIC_SEARCH_PATHS ${QET_DIR}/sources/ui)
if(QT_VERSION_MAJOR EQUAL 6)
set(KF_MAJOR_VERSION 6)
else()
set(KF_MAJOR_VERSION 5)
endif()
include(cmake/fetch_kdeaddons.cmake)
# Add sub directories
@@ -208,71 +179,19 @@ set_source_files_properties(
${TS_FILES}
PROPERTIES OUTPUT_LOCATION "lang"
)
if(QT_VERSION_MAJOR EQUAL 6)
if(Qt6_VERSION VERSION_LESS "6.2")
# Qt 6.06.1
qt6_add_translation(QM_FILES ${TS_FILES})
if(Qt6_VERSION VERSION_LESS "6.2")
# Qt 6.06.1
qt6_add_translation(QM_FILES ${TS_FILES})
# qt6_add_translation() only creates custom commands. Something must
# depend on their outputs for them to run during the default build.
add_custom_target(${PROJECT_NAME}_lrelease ALL
DEPENDS ${QM_FILES}
)
add_custom_target(update_translations
COMMAND
$<TARGET_FILE:Qt6::lupdate>
"${CMAKE_SOURCE_DIR}/sources"
-ts ${TS_FILES}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT
"Updating .ts files from sources/ (lupdate) - developer target"
VERBATIM
)
elseif(Qt6_VERSION VERSION_LESS "6.7")
# Qt 6.26.6: old target-based signature
qt_add_lrelease(
${PROJECT_NAME}
TS_FILES ${TS_FILES}
QM_FILES_OUTPUT_VARIABLE QM_FILES
)
# Automatically creates the update_translations umbrella target.
qt_add_lupdate(
${PROJECT_NAME}
TS_FILES ${TS_FILES}
)
else()
# Qt 6.7+: new signature
qt_add_lrelease(
TS_FILES ${TS_FILES}
LRELEASE_TARGET ${PROJECT_NAME}_lrelease
QM_FILES_OUTPUT_VARIABLE QM_FILES
)
qt_add_lupdate(
SOURCE_TARGETS ${PROJECT_NAME}
TS_FILES ${TS_FILES}
LUPDATE_TARGET update_translations
NO_GLOBAL_TARGET
)
endif()
else()
# Qt 5
qt5_add_translation(QM_FILES ${TS_FILES})
# Likewise, qt5_add_translation() needs a target depending on its outputs,
# unless QM_FILES are already consumed elsewhere.
# qt6_add_translation() only creates custom commands. Something must
# depend on their outputs for them to run during the default build.
add_custom_target(${PROJECT_NAME}_lrelease ALL
DEPENDS ${QM_FILES}
)
add_custom_target(update_translations
COMMAND
$<TARGET_FILE:Qt5::lupdate>
$<TARGET_FILE:Qt6::lupdate>
"${CMAKE_SOURCE_DIR}/sources"
-ts ${TS_FILES}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
@@ -280,8 +199,38 @@ else()
"Updating .ts files from sources/ (lupdate) - developer target"
VERBATIM
)
elseif(Qt6_VERSION VERSION_LESS "6.7")
# Qt 6.26.6: old target-based signature
qt_add_lrelease(
${PROJECT_NAME}
TS_FILES ${TS_FILES}
QM_FILES_OUTPUT_VARIABLE QM_FILES
)
# Automatically creates the update_translations umbrella target.
qt_add_lupdate(
${PROJECT_NAME}
TS_FILES ${TS_FILES}
)
else()
# Qt 6.7+: new signature
qt_add_lrelease(
TS_FILES ${TS_FILES}
LRELEASE_TARGET ${PROJECT_NAME}_lrelease
QM_FILES_OUTPUT_VARIABLE QM_FILES
)
qt_add_lupdate(
SOURCE_TARGETS ${PROJECT_NAME}
TS_FILES ${TS_FILES}
LUPDATE_TARGET update_translations
NO_GLOBAL_TARGET
)
endif()
# Optional precompiled headers -- see QET_ENABLE_PCH in
# cmake/developer_options.cmake for what this trades away.
#
-179
View File
@@ -1,179 +0,0 @@
[Qt6 / CMake]
Building the Qt6 line with CMake additionally requires the private
development headers of QtGui. On Debian/Ubuntu these live in a separate
package that is NOT pulled in by qt6-base-dev:
apt install qt6-base-private-dev
(other distributions: install your Qt 6 "private headers" development
package, e.g. qt6-qtbase-private-devel on Fedora)
QET needs the private QtGui API (QPdfEngine) for clickable hyperlinks in
the PDF export. Without the package, find_package(Qt6 ... GuiPrivate)
succeeds but CMake later fails at generate time with:
"Imported target Qt6::GuiPrivate includes non-existent path".
[ca]
Dependències:
libQt5 (paquets libqt5*)
cupsys-bsd per imprimir
Com compilar:
$ qmake (qmake-qt5 pels sistemes basats en Debian)
$ make
# umask 0022
# make install
[en]
Requirements :
libQt5 (see packages libqt5*)
cupsys-bsd for printing
Howto compile :
$ qmake (qmake-qt5 for Debian-based systems)
$ make
# umask 0022
# make install
[fr]
Pré-requis :
libQt5 (paquets libqt5*)
cupsys-bsd pour l'impression
Comment compiler :
$ qmake (qmake-qt5 pour les systèmes basés sur Debian)
$ make
# umask 0022
# make install
[de]
Abhängigkeiten:
libQt5 (Pakete libqt5*)
cupsys-bsd zum Drucken
Kompilieren:
$ qmake (qmake-qt5 für Debian-basierende Systeme)
$ make
# umask 0022
# make install
[ru]
Зависимости:
libQt5 (пакет libqt5*)
cupsys-bsd для печати
Как компилировать?:
$ qmake (qmake-qt5 для систем основанных на Debian)
$ make
# umask 0022
# make install
[pt]
Dependências:
libQt5 (ver pacotes libqt5*)
cupsys-bsd para impressão
Como compilar:
$ qmake (qmake-qt5 para sistemas baseados em Debian)
$ make
# umask 0022
# make install
[es]
Dependencias:
libQt5 (paquetes libqt5*)
cupsys-bsd para imprimir
Cómo compilar:
$ qmake (qmake-qt5 para los sistemas basados en Debian)
$ make
# umask 0022
# make install
[cs]
Požadavky :
libQt5 (viz balíček libqt5*)
cupsys-bsd kvůli tisku
Jak program sestavit :
$ qmake (qmake-qt5 u systémů založených na Debianu)
$ make
# umask 0022
# make install
[pl]
Zależności:
libQt5 (pakiety libqt5 *)
cupsys-bsd do druku
Sposób kompilacji:
$ qmake (qmake-qt5 dla systemów opartych na Debianie)
$ make
# umask 0022
# make install
[it]
Dipendenze:
libQt5 (vedi pacchetti libqt5*)
cupsys-bsd per la stampa
Come compilare:
$ qmake (qmake-qt5 per sistemi basati su Debian)
$ make
# umask 0022
# make install
[el]
Απαιτήσεις:
libQt5 (δείτε για πακέτα libqt5*)
cupsys-bsd για εκτύπωση
Πως να το μεταγλωττίσετε:
$ qmake (qmake-qt5 για συστήματα βασισμένα σε Debian)
$ make
# umask 0022
# make install
[nl]
vereisten:
libqt5 (zie pakketten libqt5 *)
cupsys-bsd om af te drukken
Hoe te compileren:
$ Qmake (qmake-qt5 voor Debian-gebaseerde systemen)
$ make
# Umask 0022
# Make install
[be]
Vereisten:
libQt5 (pakketten libqt5*)
cupsys-bsd voor het afdrukken
Hoe te compileren :
$ qmake (qmake-qt5 voor systemen op basis van Debian)
$ make
# umask 0022
# make install
[da]
Krav:
libQt5 (se pakke libqt5*)
cupsys-bsd til udskrivning
Hvordan man kompilerer:
$ qmake (qmake-qt5 for Debian baserede systemer)
$ make
# umask 0022
# make install
[ja]
必要条件 :
libQt5 (パッケージ libqt5* を参照)
cupsys-bsd 印刷用
コンパイルの方法 :
$ qmake (Debian ベースのシステムでは qmake-qt5)
$ make
# umask 0022
# make install
+349
View File
@@ -0,0 +1,349 @@
# Building QElectroTech from source
QElectroTech is built with **CMake** and requires the **current Qt6 only**; Qt5 and the
old `qmake`-based build described in earlier versions of this file have
been retired.
## 1. Getting the source
QET uses git submodules, so clone recursively:
```sh
git clone --recursive https://github.com/qelectrotech/qelectrotech-source-mirror.git
cd qelectrotech-source-mirror
```
If you already have a non-recursive clone:
```sh
git submodule update --init --recursive
```
## 2. Requirements
| Component | Status | Notes |
|---|---|---|
| CMake ≥ 3.5 | required | CMake 4.3+ is also fine, see note below |
| C++17 compiler | required | GCC or Clang on Unix-like platforms; MSVC or MinGW-w64 g++ on Windows — see [Choosing a compiler](#3-choosing-a-compiler-unix) / [Building on Windows](#6-building-on-windows-msvc--mingw) |
| Qt6 base + widgets | required | |
| Qt6 **GuiPrivate** headers | required | needed for clickable PDF hyperlinks; **hard build failure** at CMake generate time if missing, see below |
| SQLite3 | required | used by the nomenclature/summary database |
| Qt Linguist tools (`lrelease`) | required | compiles the tracked `.ts` files into `.qm` as part of every normal build |
| pugixml | handled automatically | fetched and built via CMake FetchContent if not already present on the system — see [pugixml](#8-pugixml) below |
| Qt Test module | required if building tests | `PACKAGE_TESTS` is `ON` by default; QtTest ships as part of the base Qt6 dev packages listed below on every platform, no extra package needed |
| KDE Frameworks (KF6) | optional | see [Building without KDE Frameworks](#9-building-without-kde-frameworks) |
| QtPdf module | optional | see [PDF page import](#7-pdf-page-import-qtpdf) |
A note on CMake versions: the project declares a minimum of 3.5 but is
routinely built with much newer releases; if your CMake is older than 4.3 it
simply won't have the newer `SQLite3::SQLite3` target name, which the build
script compensates for automatically. There is nothing you need to do either
way.
## 3. Building (out-of-source build)
Always build in a separate directory from the source tree — in-source builds
are not supported.
```sh
mkdir build
cd build
cmake ..
cmake --build . --parallel
```
For a Unix-like platform you'll usually also want an explicit build type,
since CMake's default (empty) build type means no optimization and no
debug info either:
```sh
cmake .. -DCMAKE_BUILD_TYPE=Release
```
To install (default prefix is `/usr/local`; adjust with
`-DCMAKE_INSTALL_PREFIX=...` at the configure step if needed):
```sh
sudo cmake --install .
```
Useful configure-time options (pass as `-D<OPTION>=ON/OFF`):
| Option | Default | Effect |
|---|---|---|
| `PACKAGE_TESTS` | `ON` | build the test suite (needs the Qt Test module) |
| `BUILD_WITH_KF` | distro-dependent | build against KDE Frameworks; see below |
| `QET_ENABLE_PCH` | `OFF` | precompiled headers for faster rebuilds (needs CMake ≥ 3.16) |
## 3.1 Choosing a compiler (Unix)
Both **GCC** and **Clang** are supported on Unix-like platforms; any
reasonably recent release with solid C++17 support works. Neither is
hard-coded — CMake picks up whichever `cc`/`c++` (or `CC`/`CXX` environment
variables) resolve to by default, and you can force one explicitly at
configure time:
```sh
cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
# or
cmake .. -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
```
or equivalently, by exporting `CC`/`CXX` before calling `cmake`. The
per-distro package lists below install GCC by default (via
`build-essential`/`gcc-c++`/the base toolchain); see each section for the
Clang alternative.
## 4. Distribution packages
Exact package names vary by release/branch; if a name below doesn't resolve,
search your package manager (`apt search`, `pkg search`, `brew search`) for
the closest match.
### Debian / Ubuntu
```sh
sudo apt install \
build-essential cmake ninja-build git \
qt6-base-dev qt6-base-private-dev qt6-tools-dev qt6-tools-dev-tools \
libsqlite3-dev \
libkf6coreaddons-dev libkf6widgetsaddons-dev
```
`qt6-base-private-dev` is **not** pulled in automatically by `qt6-base-dev`
but is mandatory for the build (see
[Qt6 private headers](#6-qt6-private-headers-mandatory) below).
For Clang instead of GCC:
```sh
sudo apt install clang
```
Optional, for PDF page import:
```sh
sudo apt install libqt6pdf6-dev
```
Optional, to use a system pugixml instead of letting CMake fetch it:
```sh
sudo apt install libpugixml-dev
```
### Fedora / RHEL
```sh
sudo dnf install \
cmake gcc-c++ git \
qt6-qtbase-devel qt6-qtbase-private-devel qt6-qttools-devel \
sqlite-devel \
kf6-kcoreaddons-devel kf6-kwidgetsaddons-devel
```
For Clang instead of GCC: `sudo dnf install clang`.
Optional, for PDF page import: `qt6-qtwebengine-devel` provides `QtPdf` on
some Fedora releases — check `dnf provides '*/QPdfDocument'` if unsure.
Optional, for a system pugixml: `sudo dnf install pugixml-devel`.
### FreeBSD
```sh
pkg install \
cmake git \
qt6-base qt6-tools \
sqlite3 \
kf6-kcoreaddons kf6-kwidgetsaddons
```
(from ports: `devel/qt6-base`, `devel/qt6-tools`, `databases/sqlite3`,
`devel/kf6-kcoreaddons`, `x11-toolkits/kf6-kwidgetsaddons`.) Qt6's
`GuiPrivate` headers ship as part of `qt6-base` on FreeBSD, no separate
package is needed. `QtPdf` is not packaged on FreeBSD at the time of writing
— PDF page import will simply be disabled (see below).
Clang is the FreeBSD base system compiler and needs nothing extra; GCC is
available via `pkg install gcc` if you'd rather use it.
Optional, for a system pugixml: `pkg install pugixml` (`devel/pugixml`).
### macOS
Using [Homebrew](https://brew.sh):
```sh
brew install cmake qt sqlite ninja
```
Homebrew's `qt` formula is Qt6 and includes the private headers, so no
extra package is required there. KDE Frameworks are not practically
available via Homebrew, so macOS builds are normally done **without KF**
(`-DBUILD_WITH_KF=OFF`) — see the caveat below.
Apple's Clang (from the Xcode Command Line Tools, `xcode-select --install`)
is the default and needs no extra package; GCC is available via
`brew install gcc` if you specifically want it instead.
Optional, for a system pugixml: `brew install pugixml`.
### Windows
See [Building on Windows](#6-building-on-windows-msvc--mingw) below — the
package sources differ enough from the Unix-like platforms above (no system
package manager, SQLite3 and Qt aren't provided the same way) that it gets
its own section.
## 5. pugixml
QET links against [pugixml](https://pugixml.org) for XML parsing.
`cmake/fetch_pugixml.cmake` handles this automatically: if a suitable
pugixml isn't already available on the system, CMake fetches and builds it
from source via `FetchContent` as part of the normal configure step — you
don't need to do anything to get a working build.
If you'd rather avoid that network fetch and use your distribution's own
pugixml package instead, install it before running `cmake ..` (package
names are listed per platform in the [Distribution
packages](#4-distribution-packages) section above — e.g. `libpugixml-dev`
on Debian/Ubuntu, `pugixml-devel` on Fedora).
## 6. Building on Windows (MSVC / MinGW)
Both toolchains QET's CMake build targets on Windows are covered here:
**MSVC** (Visual Studio 2019/2022) and **MinGW-w64** (gcc). Unlike the
Unix-like platforms above, there's no single system package manager, so
Qt, SQLite3 and (optionally) KDE Frameworks each need to be sourced
separately per toolchain.
One piece of good news either way: unlike Debian/Fedora, the official Qt
Windows kits (both MSVC and MinGW) **already include the GuiPrivate private
headers** — there's no separate "private headers" package to remember on
Windows (see [Qt6 private headers](#7-qt6-private-headers-mandatory) below
for why this matters).
`QtPdf` is available as an optional component in the Qt Online Installer
(look for "Qt PDF" under the Qt 6.4+ node); if you skip it, PDF page import
is silently disabled exactly as on the other platforms. KDE Frameworks are
not prebuilt for Windows by any of the routes below, so Windows builds
normally use `-DBUILD_WITH_KF=OFF` (see the
[autosave caveat](#9-building-without-kde-frameworks)).
### MSVC (Visual Studio 2019 / 2022)
1. Install Visual Studio with the "Desktop development with C++" workload,
and install Qt6 for MSVC (e.g. the `msvc2019_64` or `msvc2022_64` kit)
via the [Qt Online Installer](https://www.qt.io/download-qt-installer).
2. Get SQLite3 — the simplest route is [vcpkg](https://vcpkg.io):
```bat
vcpkg install sqlite3:x64-windows
```
3. Configure and build from an "x64 Native Tools Command Prompt for VS":
```bat
mkdir build && cd build
cmake .. -G "Visual Studio 17 2022" -A x64 ^
-DCMAKE_PREFIX_PATH="C:\Qt\6.x.x\msvc2022_64" ^
-DCMAKE_TOOLCHAIN_FILE="C:\vcpkg\scripts\buildsystems\vcpkg.cmake" ^
-DBUILD_WITH_KF=OFF
cmake --build . --config Release --parallel
```
Ninja works too, in place of the Visual Studio generator, if you prefer
(`-G Ninja -DCMAKE_BUILD_TYPE=Release`, run from the same VS developer
prompt so `cl.exe` is on `PATH`).
### MinGW-w64 (gcc)
Two common ways to get a MinGW toolchain that matches a Qt6 MinGW kit: Qt's
own bundled MinGW (installed alongside a `mingw_64` Qt kit via the Qt
Online Installer), or [MSYS2](https://www.msys2.org).
Using MSYS2, from a **MINGW64** shell:
```sh
pacman -S --needed \
mingw-w64-x86_64-toolchain \
mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools \
mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja \
mingw-w64-x86_64-sqlite3
mkdir build && cd build
cmake .. -G Ninja -DBUILD_WITH_KF=OFF -DCMAKE_BUILD_TYPE=Release
cmake --build .
```
(KF6 isn't packaged in MSYS2 either, hence `-DBUILD_WITH_KF=OFF` again.)
Using the Qt Online Installer's bundled MinGW kit instead: point
`CMAKE_PREFIX_PATH` at that kit (e.g. `C:\Qt\6.x.x\mingw_64`) and make sure
its bundled `g++.exe` comes first on `PATH`, or pass
`-DCMAKE_C_COMPILER`/`-DCMAKE_CXX_COMPILER` explicitly so CMake doesn't pick
up a different MinGW installation. SQLite3 still has to come from elsewhere
in this path — vcpkg with a `mingw`-flavoured triplet, or MSYS2's package as
above.
## 7. Qt6 private headers (mandatory)
QET uses the private `QPdfEngine` API (`<private/qpdf_p.h>`) to produce
clickable hyperlinks in PDF export. This requires Qt's `GuiPrivate` target,
which on several distributions (notably Debian/Ubuntu) is shipped in a
separate "private headers" package rather than the normal `-dev` package —
e.g. `qt6-base-private-dev` on Debian/Ubuntu, or
`qt6-qtbase-private-devel` on Fedora. Without it, CMake's `find_package`
step succeeds but configuration later fails at generate time with:
```
Imported target "Qt6::GuiPrivate" includes non-existent path
```
If you hit this, install your distribution's Qt6 private-headers package
(on Windows this is a non-issue — see [Building on
Windows](#6-building-on-windows-msvc--mingw) above).
## 8. PDF page import (QtPdf)
The toolbar's "Add a PDF" feature (`QPdfDocument::pagePointSize()`) needs
the separate `QtPdf` Qt module, available since Qt 6.4.
**This is optional and soft-fails**: if `QtPdf` isn't found, or is present
but older than 6.4, CMake prints a status message and simply disables the
feature (`QET_HAS_QTPDF` stays `FALSE`) — it does **not** stop the build.
Some Qt6 distributions don't ship `QtPdf` at all (it lives outside Qt's
core module set, alongside QtWebEngine), so this is expected on several
platforms, including FreeBSD as of this writing.
## 9. Building without KDE Frameworks
`BUILD_WITH_KF=OFF` builds QET against a bundled fallback UI layer instead
of KDE Frameworks (`kcoreaddons`/`kwidgetsaddons`). This is a fully
supported configuration and is, in practice, the default on platforms where
KF6 isn't readily packaged (macOS and Windows in particular).
Almost everything works identically either way. The one known caveat:
**autosave behavior is not as polished without KF**, since QET relies on
KCoreAddons' autosave/backup-file handling when it's available. Expect
autosave to work, but less robustly than on a KF-enabled build.
## 10. Translations (developer operation)
Everything in this section is a **maintainer/translator task**, not
something a normal build or a packager needs to touch.
The `.qm` files shipped with the application are compiled from the tracked
`.ts` files automatically as part of every normal build (via `lrelease`) —
regular builds and packages get up-to-date translated UI strings for free,
with no developer action needed.
Refreshing the `.ts` files themselves from the source code (`lupdate`) is
the actual developer operation: it's exposed as its own opt-in build
target, separate from the normal build:
```sh
cmake --build . --target update_translations
```
This is **not** run automatically and should not be added to routine or
parallel CI builds: it rewrites the tracked `.ts` files as a side effect,
and running it concurrently with the normal `lrelease` compilation step (as
would happen under `-j`/parallel builds) can corrupt a `.ts` file mid-write,
causing `lrelease` to fail with "Premature end of document". Run it on its
own, review the diff, and commit the updated `.ts` files separately.
+15 -15
View File
@@ -1,60 +1,60 @@
[ca]
QElectroTech és una aplicació Qt5 per crear esquemes elèctrics.
QElectroTech és una aplicació Qt6 per crear esquemes elèctrics.
QET utilitza el format XML per als seus elements i esquemes i inclou un editor d'esquemes, un editor d'elements i un editor de caixetins.
[en]
QElectroTech is a Qt5 application to design electric diagrams.
QElectroTech is a Qt6 application to design electric diagrams.
It uses XML files for elements and diagrams, and includes both a diagram editor, a element editor, and an titleblock editor.
[fr]
QElectroTech est une application Qt5 pour réaliser des schémas électriques.
QElectroTech est une application Qt6 pour réaliser des schémas électriques.
QET utilise le format XML pour ses éléments et ses schémas et inclut un éditeur de schémas, un éditeur d'élément, ainsi qu'un editeur de cartouche.
[de]
QElectroTech ist eine Qt5 Software, um Schaltpläne zu erstellen.
QElectroTech ist eine Qt6 Software, um Schaltpläne zu erstellen.
QET benutzt das XML Format für seine Bauteile und seine Projekte, und beinhaltet einen Schaltplaneditor, einen Bauteileditor sowie einen Schriftfeldeditor.
[ru]
QElectroTech - приложение написанное на Qt5 и предназначенное для разработки электрических схем.
QElectroTech - приложение написанное на Qt6 и предназначенное для разработки электрических схем.
Оно использует XML-файлы для элементов и схем, и включает, как редактор схем, так и редактор элементов.
[pt]
QElectroTech é uma aplicação baseada em Qt5 para desenhar esquemas eléctricos.
QElectroTech é uma aplicação baseada em Qt6 para desenhar esquemas eléctricos.
QET utiliza ficheiros XML para os elementos e para os esquemas e inclui um editor de esquemas e um editor de elementos.
[es]
QElectroTech es una aplicación Qt5 para diseñar esquemas eléctricos.
QElectroTech es una aplicación Qt6 para diseñar esquemas eléctricos.
Utiliza archivos XML para los elementos y esquemas, e incluye un editor de esquemas y un editor de elementos.
[cs]
QElectroTech je aplikací Qt5 určenou pro návrh nákresů elektrických obvodů.
QElectroTech je aplikací Qt6 určenou pro návrh nákresů elektrických obvodů.
Pro prvky a nákresy používá soubory XML, a zahrnuje v sobě jak editor nákresů, tak editor prvků.
[pl]
QElectroTech to aplikacja napisana w Qt5, przeznaczona do tworzenia schematów elektrycznych.
QElectroTech to aplikacja napisana w Qt6, przeznaczona do tworzenia schematów elektrycznych.
Wykorzystuje XML do zapisywania plików elementów i projektów. Posiada edytor schematów i elementów.
[it]
QElectroTech è una applicazione fatta in Qt5 per disegnare schemi elettrici.
QElectroTech è una applicazione fatta in Qt6 per disegnare schemi elettrici.
QET usa il formato XML per i suoi elementi e schemi, includendo anche un editor per gli stessi.
[el]
Το QElectroTech είναι μια εφαρμογή Qt5 για σχεδίαση ηλεκτρικών διαγραμμάτων.
Το QElectroTech είναι μια εφαρμογή Qt6 για σχεδίαση ηλεκτρικών διαγραμμάτων.
Χρησιμοποιεί αρχεία XML για στοιχεία και διαγράμματα, και περιλαμβάνει επεξεργαστή διαγραμμάτων καθώς και επεξεργαστή στοιχείων.
[nl]
QElectroTech is een Qt5 applicatie om elektrische schema's te ontwerpen.
QElectroTech is een Qt6 applicatie om elektrische schema's te ontwerpen.
Het maakt gebruik van XML-bestanden voor elementen en diagrammen, en omvat zowel een diagram bewerker, een element bewerker, en een bloksjabloon bewerker.
[be]
QElectroTech is een Qt5 toepassing voor het maken en beheren van elektrische schema's.
QElectroTech is een Qt6 toepassing voor het maken en beheren van elektrische schema's.
QET gebruikt XML voor de elementen en schema's en omvat een schematische editor, itemeditor, en een titel sjabloon editor.
[da]
QElectroTech er et Qt5 program til at redigere elektriske diagrammer.
QElectroTech er et Qt6 program til at redigere elektriske diagrammer.
Det bruger XML filer for symboler og diagrammer og inkluderer diagram, symbol og titelblok redigering.
[ja]
QElectroTech は電気回路図を作成する Qt5 アプリケーションです。
QElectroTech は電気回路図を作成する Qt6 アプリケーションです。
QET は要素と回路図に XML 形式を利用し、回路図エディタ、要素エディタ、表題欄エディタを含みます。
+51 -51
View File
@@ -5,9 +5,9 @@
### What it is
QElectroTech, or QET in short, is a libre and open source desktop application to create diagrams and schematics.
The software is primarily intended to create electrical documentation but it can also be used to draw any kinds of diagrams, such as those made in pneumatics, hydraulics, process industries, electronics...
Generally speaking, QET is a **CAD/CAE editor focusing on schematics drawing features**.
QElectroTech, or QET in short, is a libre and open source desktop application to create diagrams and schematics.
The software is primarily intended to create electrical documentation but it can also be used to draw any kinds of diagrams, such as those made in pneumatics, hydraulics, process industries, electronics...
Generally speaking, QET is a **CAD/CAE editor focusing on schematics drawing features**.
This means that there are no embedded simulating or calculating functionalities and it is not planned to implement them.
@@ -24,7 +24,7 @@ Users who want to test and take benefits from the last software implementations
### License
The software is licensed under [GNU/GPL](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
The software is licensed under [GNU/GPL](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
You are free to use, copy, modify and redistribute it under the terms of the license.
Like many other open source software, QElectroTech is provided as is, without any warranty.
@@ -40,13 +40,13 @@ git clone --recursive https://github.com/qelectrotech/qelectrotech-source-mirror
Here are the technical choices made for the software development:
* Integrated development environment: [Qt Framework](https://www.qt.io/ide/)
* Libraries: Qt 5.x
* [KF5 Framework](https://github.com/KDE)
[Cmake](https://cmake.org/install/)
[kcoreaddons](https://github.com/KDE/kcoreaddons/tree/kf5)
[kwidgetsaddons](https://github.com/KDE/kwidgetsaddons/tree/kf5).
* Libraries: Qt 6.x
* [KF6 Framework](https://github.com/KDE)
[Cmake](https://cmake.org/install/)
[kcoreaddons](https://github.com/KDE/kcoreaddons/tree/kf6)
[kwidgetsaddons](https://github.com/KDE/kwidgetsaddons/tree/kf6).
* Coding language: [C++](https://en.wikipedia.org/wiki/C%2B%2B)
* GUI translations: [Qt Linguist](http://doc.qt.io/qt-5/qtlinguist-index.html)
* GUI translations: [Qt Linguist](http://doc.qt.io/qt-6/qtlinguist-index.html)
* Version control: [GIT](https://github.com/qelectrotech/qelectrotech-source-mirror.git)
* Doxygen documentation :[Doxygen](https://download.qelectrotech.org/qet/doxygen/html/)
* QtCreator qch doxygen :[QElectroTech.qch](https://download.qelectrotech.org/qet/doxygen/QElectroTech.qch)
@@ -62,20 +62,20 @@ If you wish to be informed of the latest developments, browse the [archive](http
# Features
QElectroTech is a free and open source software.
No need to worry about restrictive licensing, privacy violation or dependency on a company.
Zero cost and no licensing fees!
QElectroTech is a free and open source software.
No need to worry about restrictive licensing, privacy violation or dependency on a company.
Zero cost and no licensing fees!
But you are welcome to make a donation to support the development
QElectroTech runs on the 3 most widespread operating systems for desktop computers in the world.
Files that were created on an OS can be edited on another OS without any conversion or restriction.
QElectroTech runs on the 3 most widespread operating systems for desktop computers in the world.
Files that were created on an OS can be edited on another OS without any conversion or restriction.
MS Windows users can even run the "ready-to-use" version of QElectroTech from an external medium with no need to install it on an access restricted computer.
Take advantage of the modern GUI
Toolbars and panels can be enabled/disabled, moved and displayed the way you want to work.
Panels can be stacked on each other (as tabs) or docked on the sides (as docks) or completely separated from the main window (as windows).
Toolbars and panels can be enabled/disabled, moved and displayed the way you want to work.
Panels can be stacked on each other (as tabs) or docked on the sides (as docks) or completely separated from the main window (as windows).
The GUI can fit to small or big screens, and even to multi-display configurations.
![](https://download.qelectrotech.org/qet/images-features/0030.png "GUI overview")
@@ -86,31 +86,31 @@ You only need to restart the application for the new selected language to take e
Create technical documentation in professional quality
Size, look and informations of the folios (sheets) are fully configurable.
Size, look and informations of the folios (sheets) are fully configurable.
You can set vertical and horizontal headers (printed rulers) individually on and off, set number of columns and rows, and set width/height of each column/row.
Titlebocks can be created and edited with the embedded titleblock editor to perfectly suit your needs.
Titlebocks can be created and edited with the embedded titleblock editor to perfectly suit your needs.
Custom variables can be defined to display the informations you wish in the titleblock.
![](https://download.qelectrotech.org/qet/images-features/0055.png "Titleblock editor")
With only 2 mouse clicks you can add a full automatic generated table of content.
With only 2 mouse clicks you can add a full automatic generated table of content.
Changes in the documentation are updated on the fly.
![](https://download.qelectrotech.org/qet/images-features/0060.png "Table of content")
Choose from more than 8.200 symbols...
The embedded QET collection contains a rich library of electric, logic, pneumatic, hydraulic and fluid symbols.
The embedded QET collection contains a rich library of electric, logic, pneumatic, hydraulic and fluid symbols.
The library grows at every new release thanks to an active user community.
![](https://download.qelectrotech.org/qet/images-features/0070.png "Collections")
...or create your own collection
The embedded element editor is a nice tool to create your own elements (symbols or anything else).
The embedded element editor is a nice tool to create your own elements (symbols or anything else).
Your own elements are stored in the user collection.
![](https://download.qelectrotech.org/qet/images-features/0080.png "Element editor")
Quickly find what you need
All collections can quickly be searched with the integrated search engine.
All collections can quickly be searched with the integrated search engine.
Furthermore, the search request can be restricted to the folder of your choice.
![](https://download.qelectrotech.org/qet/images-features/0090.png "Search engine")
@@ -128,7 +128,7 @@ And of course, you can accurately zoom with the mouse wheel over the drawing are
Link elements together to create cross references
Several types of element can be linked together to display a cross reference text.
Several types of element can be linked together to display a cross reference text.
All types of cross references are automatically updated on the fly, you don't need to think about them if you make changes.
![](https://download.qelectrotech.org/qet/images-features/0112.png "Cross ref elements")
To speed up your work, linkable elements are easily searched and shown.
@@ -143,7 +143,7 @@ This way, you can make your own parts list or bill of material using the full po
![](https://download.qelectrotech.org/qet/images-features/0125.png "Spreadsheet")
Print to pdf and/or export your work to images
Your whole documentation or only selected parts of it can be printed to a real printer or to a pdf file.
Your whole documentation or only selected parts of it can be printed to a real printer or to a pdf file.
Alternatively, you can export to vector (svg) or pixel (png, jpg, bmp) format images.
### And much more:
@@ -161,44 +161,44 @@ Alternatively, you can export to vector (svg) or pixel (png, jpg, bmp) format im
Nomenclature
A new nomenclature tool appears in the menu: project -> Add a nomenclature.
The nomenclature is presented in the form of a configurable table separated into two parts: the display (the form) and the content (the background).
- Display: the size and position of the table, the margins between text and the table cell, the alignment of the text in the cells and the font. The configuration of the table headers and the table itself are separate.
- Content: the information to display in the table and the order in which it should be displayed.
The nomenclature is presented in the form of a configurable table separated into two parts: the display (the form) and the content (the background).
- Display: the size and position of the table, the margins between text and the table cell, the alignment of the text in the cells and the font. The configuration of the table headers and the table itself are separate.
- Content: the information to display in the table and the order in which it should be displayed.
![](https://download.qelectrotech.org/qet/images_depeche_linuxfr/08/dialogue_nomenclature.png "nomenclature dialogue")
In order to speed up the establishment of a nomenclature, it is possible to export / import the display and content configurations separately. This is the "Configuration" part that can be seen in the photos above.
In order to speed up the establishment of a nomenclature, it is possible to export / import the display and content configurations separately. This is the "Configuration" part that can be seen in the photos above.
Behind the scenes, an SQLite database does the work, so setting up the content is nothing more or less than an SQL query created using a dialog (screenshot by right).
The SQL query is configured as follows (from top to bottom in the screenshot):
- “Available information”: the information to display;
- "Filter": filter the information (is not empty, is empty, contains, does not contain, is equal to, is not equal to) only one filter can be applied per information, it is not possible combine several;
- "Type of elements": allows you to filter on what type of element you want to obtain information.
Behind the scenes, an SQLite database does the work, so setting up the content is nothing more or less than an SQL query created using a dialog (screenshot by right).
The SQL query is configured as follows (from top to bottom in the screenshot):
- “Available information”: the information to display;
- "Filter": filter the information (is not empty, is empty, contains, does not contain, is equal to, is not equal to) only one filter can be applied per information, it is not possible combine several;
- "Type of elements": allows you to filter on what type of element you want to obtain information.
At the bottom, a checkmark "SQL query" allows you to edit a personalized query, if the basic options are not sufficient.
At the bottom, a checkmark "SQL query" allows you to edit a personalized query, if the basic options are not sufficient.
When a nomenclature is too large to be contained in a single folio, it is possible to separate it on several folios, the tables of each folio are then linked together. When creating a nomenclature, this option is activated by default, which has the effect of adding the necessary number of folios, adding a table in each of them and linking them together.
When a nomenclature is too large to be contained in a single folio, it is possible to separate it on several folios, the tables of each folio are then linked together. When creating a nomenclature, this option is activated by default, which has the effect of adding the necessary number of folios, adding a table in each of them and linking them together.
Finally two buttons are available in the property panel:
- "Fit the table to the folio": positions and adjusts the size and determines the number of rows in the table in relation to the folio;
- "Apply geometry to all tables linked to this one": applies the three properties mentioned above to all linked tables in order to save time and maintain aesthetic consistency.
Finally two buttons are available in the property panel:
- "Fit the table to the folio": positions and adjusts the size and determines the number of rows in the table in relation to the folio;
- "Apply geometry to all tables linked to this one": applies the three properties mentioned above to all linked tables in order to save time and maintain aesthetic consistency.
And to finish a table
And to finish a table
![](https://download.qelectrotech.org/qet/images_depeche_linuxfr/08/tableau.png "table")
Summary
The old summary has been completely removed from the code in order to make room for the new one which is exactly the same as the nomenclature (a large amount of the code is common), with the exception of the SQL query (and its dialog to configure it) which offers specific information for editing a summary.
The old summary has been completely removed from the code in order to make room for the new one which is exactly the same as the nomenclature (a large amount of the code is common), with the exception of the SQL query (and its dialog to configure it) which offers specific information for editing a summary.
Export of the internal database
Export of the internal database
The database used by the nomenclature and the summary can be exported in a “.sqlite” file.
Currently this is irrelevant, as the function was created during development for debugging purposes, we left it.
Note that the database will become increasingly important in the future of Qet.
The database used by the nomenclature and the summary can be exported in a “.sqlite” file.
Currently this is irrelevant, as the function was created during development for debugging purposes, we left it.
Note that the database will become increasingly important in the future of Qet.
Export of the wiring list
Export of the wiring list
In order to be able to use the wiring number printers more easily, the names of conductors can be exported in CSV format, the export respects the quantity of conductors in order to print the right quantity of numbers, for example a potential numbered 240 composed of 3 wires will give 6 × 240 (2 numbers per wire × 3 wires) in the CSV.
@@ -206,13 +206,13 @@ In order to be able to use the wiring number printers more easily, the names of
### Story
The QElectroTech project was founded in 2007 by two french students, Xavier and Benoit.
Xavier developed the base application itself and made all technical choices about the development.
The first version of QET (0.1) was released on 09.03.2008.
The QElectroTech project was founded in 2007 by two french students, Xavier and Benoit.
Xavier developed the base application itself and made all technical choices about the development.
The first version of QET (0.1) was released on 09.03.2008.
However, both Xavier and Benoit do not participate anymore in the project since 2013.
Following this period, new developers and contributors took over the project and kept it alive.
The development and the many translations are actively maintained.
Following this period, new developers and contributors took over the project and kept it alive.
The development and the many translations are actively maintained.
New functionalities and evolutions are planned to make QET ever better.
Nowadays, QET is not only used by many individuals, teachers and students but also by professional electricians and companies all over the world.
-1
View File
@@ -68,4 +68,3 @@ else()
add_definitions(-DBUILD_WITHOUT_KF)
endif()
message("QET_COMPONENTS :" ${QET_COMPONENTS})
message("QT_VERSION_MAJOR :" ${QT_VERSION_MAJOR})
+1 -4
View File
@@ -31,10 +31,7 @@ add_definitions(-DQT_MESSAGELOGCONTEXT)
# In order to do so, uncomment the following line.
#add_definitions(-DTODO_LIST)
# Build with KDE Frameworks. The major version (KF5/KF6) is derived
# automatically from QT_VERSION_MAJOR -- KDE Frameworks deliberately
# mirrors Qt's own major version numbering, so there is no independent
# choice to make here. See cmake/fetch_kdeaddons.cmake.
# Build with KDE Frameworks.
option(BUILD_WITH_KF "Build with KDE Frameworks" ON)
# Precompiled headers for the Qt umbrella headers.
+7 -15
View File
@@ -16,7 +16,6 @@
message(" - fetch_kdeaddons")
# TODO remove path as soon as Qt5 gets retired
if(BUILD_WITH_KF)
Include(FetchContent)
@@ -24,16 +23,9 @@ if(BUILD_WITH_KF)
if(BUILD_KF)
if(KF_MAJOR_VERSION EQUAL 5)
if(NOT DEFINED KF_GIT_TAG)
#https://qelectrotech.org/forum/viewtopic.php?pid=13924#p13924
set(KF_GIT_TAG v5.77.0)
endif()
else()
if(NOT DEFINED KF_GIT_TAG)
# this is a more or less random version, taken as an conservative approach
set(KF_GIT_TAG v6.10.0)
endif()
if(NOT DEFINED KF_GIT_TAG)
# this is a more or less random version, taken as an conservative approach
set(KF_GIT_TAG v6.10.0)
endif()
# using a function in order to limit the scope of the variables
# with CMake >=3.25 we could use a block()
@@ -78,12 +70,12 @@ if(BUILD_WITH_KF)
endfunction()
qet_make_kf_available()
else()
find_package(KF${KF_MAJOR_VERSION}CoreAddons REQUIRED)
find_package(KF${KF_MAJOR_VERSION}WidgetsAddons REQUIRED)
find_package(KF6CoreAddons REQUIRED)
find_package(KF6WidgetsAddons REQUIRED)
endif()
set(KF_PRIVATE_LIBRARIES
KF${KF_MAJOR_VERSION}::WidgetsAddons
KF${KF_MAJOR_VERSION}::CoreAddons
KF6::WidgetsAddons
KF6::CoreAddons
)
endif()
+1 -1
View File
@@ -35,5 +35,5 @@ FetchContent_Declare(
SingleApplication
GIT_REPOSITORY https://github.com/itay-grudev/SingleApplication.git
GIT_TAG v3.2.0)
set(QT_DEFAULT_MAJOR_VERSION 6)
FetchContent_MakeAvailable(SingleApplication)
+7 -11
View File
@@ -16,12 +16,11 @@
message(" - qet_compilation_vars")
# Note: GuiPrivate is intentionally NOT in this list. Qt6's CMake config only
# creates the Qt::GuiPrivate target when "GuiPrivate" is explicitly requested
# as a component, but Qt5 has no Qt5GuiPrivate package at all (the target is
# created implicitly with Gui), so requesting it as a component breaks the
# whole Qt5 configure. It is requested separately, guarded by
# QT_VERSION_MAJOR, after the main find_package.
# Note: GuiPrivate is intentionally NOT in this list.
# Qt >= 6.7 ships it as a proper find_package component, but some distro
# packages (e.g. Ubuntu's qt6-base-private-dev) omit Qt6GuiPrivateConfig.cmake
# and only provide the implicit Qt6::GuiPrivate target created alongside
# Qt6::Gui. Checks are done in the main CMakeLists.txt
# (Needed for QPdfEngine::drawHyperlink, the PDF internal links.)
set(QET_COMPONENTS
LinguistTools
@@ -841,15 +840,12 @@ if(NOT BUILD_WITH_KF)
)
endif()
# Qt6-only: PDF page import files
if(QT_VERSION_MAJOR GREATER_EQUAL 6)
list(APPEND QET_SRC_FILES
list(APPEND QET_SRC_FILES
${QET_DIR}/sources/diagramevent/diagrameventaddpdf.cpp
${QET_DIR}/sources/diagramevent/diagrameventaddpdf.h
${QET_DIR}/sources/ui/pdfpagesdialog.cpp
${QET_DIR}/sources/ui/pdfpagesdialog.h
)
endif()
)
# JavaScript scripting (bugtracker #162). Unconditionally in the source
# list, like the QtPdf files above: this file is included before the
@@ -778,30 +778,6 @@ bool ElementsLocation::setXml(const QDomDocument &xml_document) const
//Element doesn't exist, we create the element
else
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
QString path_ = collectionPath(false);
QRegExp rx ("^(.*)/(.*\\.elmt)$");
if (rx.exactMatch(path_)) {
return project()
->embeddedElementCollection()
->addElementDefinition(
rx.cap(1),
rx.cap(2),
xml_document
.documentElement());
}
else {
qDebug() << "ElementsLocation::setXml :"
" rx don't match";
}
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
# pragma message("@TODO ad Core5Compat to Cmake")
#endif
qDebug() << "Help code for QT 6 or later";
QString path_ = collectionPath(false);
QRegularExpression rx("^(.*)/(.*\\.elmt)$");
QRegularExpressionMatch match = rx.match(path_);
@@ -820,7 +796,6 @@ bool ElementsLocation::setXml(const QDomDocument &xml_document) const
qDebug() << "ElementsLocation::setXml :"
" rx don't match";
}
#endif
}
}
@@ -29,15 +29,9 @@
#include "modelTerminalData.h"
//Code to use QColor as key for QHash
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
inline size_t qHash(const QColor &key, size_t seed = 0) {
return qHash(key.rgba(), seed);
}
#else
inline uint qHash(const QColor &key, uint seed) {
return qHash(key.rgba(), seed);
}
#endif
//needed to use QPointer<Element> as key of QHash
inline uint qHash(const QPointer<Element> &key, uint seed) {
-8
View File
@@ -55,12 +55,6 @@ BorderTitleBlock::BorderTitleBlock(QObject *parent) :
m_titleblock_template_renderer = new TitleBlockTemplateRenderer(this);
m_titleblock_template_renderer -> setTitleBlockTemplate(QETApp::defaultTitleBlockTemplate());
// disable the QPicture-based cache from Qt 4.8 to avoid rendering errors and crashes
#if QT_VERSION < QT_VERSION_CHECK(4, 8, 0) // ### Qt 6: remove
#else
m_titleblock_template_renderer -> setUseCache(false);
#endif
// dimensions par defaut du schema
importBorder(BorderProperties());
@@ -410,12 +404,10 @@ QString BorderTitleBlock::titleBlockTemplateName() const
@brief BorderTitleBlock::titleBlockTemplateChanged
This slot may be used to inform this class that the given title block
template has changed.
The title block-dedicated rendering cache will thus be flushed.
@param template_name : Name of the title block template that has changed
*/
void BorderTitleBlock::titleBlockTemplateChanged(const QString &template_name) {
if (titleBlockTemplateName() != template_name) return;
m_titleblock_template_renderer -> invalidateRenderedTemplate();
}
/**
+82 -4
View File
@@ -28,6 +28,7 @@
#include "../qetproject.h"
#include <QLocale>
#include <QRegularExpression>
#include <QSqlError>
#include <QSqlDriver>
@@ -132,11 +133,88 @@ QETProject *projectDataBase::project() const
}
/**
@brief projectDataBase::newQuery
@return a QSqlquery with query as query
and the internal database of this class as database to use.
@brief projectDataBase::isReadOnlySelect
Every query that reaches newQuery() goes through this check first --
including one loaded from a saved nomenclature/summary table's <query>
element (ProjectDBModel::fromXml()), which makes this a defense against
a crafted project file, not just a careless custom-SQL edit
(qelectrotech-source-mirror#886 asked for read-only enforcement on the
custom SQL reports feature; this covers every path into newQuery(), not
just that one dialog).
Deliberately simple rather than a real SQL parser: reject more than one
statement (blocks stacking a write after a leading SELECT with `;`), and
require the query to start with SELECT or WITH. A determined attacker
with arbitrary SQL access to a local SQLite connection can still find
tricks a simple prefix check won't catch; this is meant to stop the
ordinary mistake and the obvious payload, not to be a security boundary
against a hostile file assumed to already run in some other trust
context.
@param query the raw SQL text to check
@param error set to a human-readable reason when this returns false
@return true if @p query looks like a single read-only SELECT/WITH
*/
QSqlQuery projectDataBase::newQuery(const QString &query) {
bool projectDataBase::isReadOnlySelect(const QString &query, QString *error)
{
if (error) {
error->clear();
}
QString trimmed = query.trimmed();
if (trimmed.endsWith(QLatin1Char(';'))) {
trimmed.chop(1);
trimmed = trimmed.trimmed();
}
if (trimmed.isEmpty()) {
if (error) {
*error = projectDataBase::tr("La requête est vide.");
}
return false;
}
if (trimmed.contains(QLatin1Char(';'))) {
if (error) {
*error = projectDataBase::tr("Une seule requête SELECT est autorisée"
" (le caractère ';' ne peut apparaître"
" qu'à la toute fin).");
}
return false;
}
const int first_space = trimmed.indexOf(QRegularExpression(QStringLiteral("\\s")));
const QString first_word = (first_space == -1 ? trimmed : trimmed.left(first_space)).toUpper();
if (first_word != QLatin1String("SELECT") && first_word != QLatin1String("WITH")) {
if (error) {
*error = projectDataBase::tr("Seules les requêtes en lecture seule"
" (SELECT ou WITH ... SELECT) sont"
" autorisées.");
}
return false;
}
return true;
}
/**
@brief projectDataBase::newQuery
@param query the SQL text to run -- must be a single read-only
SELECT/WITH statement, see isReadOnlySelect()
@param error set to a human-readable reason when the query was rejected
before ever reaching the database
@return a QSqlQuery with query as query and the internal database of
this class as database to use, or an unexecuted, harmless QSqlQuery if
the query was rejected
*/
QSqlQuery projectDataBase::newQuery(const QString &query, QString *error) {
QString reason;
if (!isReadOnlySelect(query, &reason)) {
qWarning().noquote() << "projectDataBase::newQuery: rejected query:" << reason << "--" << query;
if (error) {
*error = reason;
}
return QSqlQuery(m_data_base);
}
return QSqlQuery(query, m_data_base);
}
+2 -1
View File
@@ -59,7 +59,8 @@ class projectDataBase : public QObject
*/
void setUpdateBlocked(bool blocked);
QETProject *project() const;
QSqlQuery newQuery(const QString &query = QString());
QSqlQuery newQuery(const QString &query = QString(), QString *error = nullptr);
static bool isReadOnlySelect(const QString &query, QString *error = nullptr);
QSqlDatabase database() const {return m_data_base;}
int excludedConductorCount() const;
+157 -1
View File
@@ -20,8 +20,15 @@
#include "../../properties/elementdata.h"
#include "../../qetapp.h"
#include "../../qetinformation.h"
#include "../projectdatabase.h"
#include "ui_elementquerywidget.h"
#include <QFile>
#include <QFileDialog>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMessageBox>
#include <QRegularExpression>
/**
@@ -94,6 +101,9 @@ ElementQueryWidget::ElementQueryWidget(QWidget *parent) :
setUpItems();
fillSavedQuery();
connect(ui->m_sql_query, &QLineEdit::textChanged, this, &ElementQueryWidget::checkQueryValidity);
checkQueryValidity();
}
/**
@@ -599,6 +609,8 @@ void ElementQueryWidget::on_m_edit_sql_query_cb_clicked()
m_custom_query = ui->m_sql_query->text();
updateQueryLine();
}
checkQueryValidity();
}
/**
@@ -672,6 +684,10 @@ void ElementQueryWidget::on_m_load_pb_clicked()
*/
void ElementQueryWidget::on_m_save_current_conf_pb_clicked()
{
if (!projectDataBase::isReadOnlySelect(queryStr())) {
return;
}
QFile file_(QETApp::configDir() % "/nomenclature.json");
if (file_.open(QFile::ReadWrite))
@@ -698,7 +714,147 @@ void ElementQueryWidget::on_m_save_current_conf_pb_clicked()
}
void ElementQueryWidget::on_m_save_name_le_textChanged(const QString &arg1) {
ui->m_save_current_conf_pb->setDisabled(arg1.isEmpty());
Q_UNUSED(arg1)
checkQueryValidity();
}
/**
@brief ElementQueryWidget::checkQueryValidity
Live feedback for the custom-SQL box: only the free-text path can carry
anything other than a SELECT (the column/filter builder always produces
one), so this only actually restricts something once "Requête SQL
personnalisée" is checked. Same rule projectDataBase::isReadOnlySelect()
enforces at execution time -- this just tells the user *before* they hit
Preview/Export/OK instead of after (qelectrotech-source-mirror#886).
*/
void ElementQueryWidget::checkQueryValidity()
{
if (!ui->m_edit_sql_query_cb->isChecked()) {
ui->m_query_warning_label->clear();
ui->m_save_current_conf_pb->setEnabled(!ui->m_save_name_le->text().isEmpty());
return;
}
QString reason;
const bool valid = projectDataBase::isReadOnlySelect(ui->m_sql_query->text(), &reason);
ui->m_query_warning_label->setText(valid ? QString() : reason);
ui->m_save_current_conf_pb->setEnabled(valid && !ui->m_save_name_le->text().isEmpty());
}
/**
@brief ElementQueryWidget::on_m_export_reports_pb_clicked
Write every saved report in nomenclature.json to a file the user picks,
so it can be handed to a colleague or another install
(qelectrotech-source-mirror#886's "import and export report definitions
for sharing between users or company installations").
*/
void ElementQueryWidget::on_m_export_reports_pb_clicked()
{
QFile source(QETApp::configDir() % "/nomenclature.json");
if (!source.open(QFile::ReadOnly)) {
QMessageBox::information(this, tr("Exporter"), tr("Aucun rapport enregistré à exporter."));
return;
}
const auto content = source.readAll();
source.close();
if (QJsonDocument::fromJson(content).object().isEmpty()) {
QMessageBox::information(this, tr("Exporter"), tr("Aucun rapport enregistré à exporter."));
return;
}
const QString file_path = QFileDialog::getSaveFileName(
this, tr("Exporter les rapports"), QStringLiteral("rapports_qet.json"),
tr("Fichiers JSON (*.json)"));
if (file_path.isEmpty()) {
return;
}
QFile dest(file_path);
if (!dest.open(QFile::WriteOnly) || dest.write(content) == -1) {
QMessageBox::critical(this, tr("Erreur"), tr("Impossible d'écrire dans %1.").arg(file_path));
}
}
/**
@brief ElementQueryWidget::on_m_import_reports_pb_clicked
Merge a previously-exported reports file into this install's
nomenclature.json. A name already used locally is not overwritten
silently -- the user is asked, per report, whether to replace it.
*/
void ElementQueryWidget::on_m_import_reports_pb_clicked()
{
const QString file_path = QFileDialog::getOpenFileName(
this, tr("Importer des rapports"), QString(), tr("Fichiers JSON (*.json)"));
if (file_path.isEmpty()) {
return;
}
QFile source(file_path);
if (!source.open(QFile::ReadOnly)) {
QMessageBox::critical(this, tr("Erreur"), tr("Impossible de lire %1.").arg(file_path));
return;
}
QJsonParseError parse_error;
const auto incoming_doc = QJsonDocument::fromJson(source.readAll(), &parse_error);
source.close();
if (parse_error.error != QJsonParseError::NoError || !incoming_doc.isObject()) {
QMessageBox::critical(
this, tr("Erreur"),
tr("%1 ne contient pas des rapports QElectroTech valides.").arg(file_path));
return;
}
const auto incoming = incoming_doc.object();
if (incoming.isEmpty()) {
QMessageBox::information(this, tr("Importer"), tr("Ce fichier ne contient aucun rapport."));
return;
}
QFile dest_file(QETApp::configDir() % "/nomenclature.json");
QJsonObject existing;
if (dest_file.open(QFile::ReadOnly)) {
existing = QJsonDocument::fromJson(dest_file.readAll()).object();
dest_file.close();
}
int imported = 0, skipped = 0;
for (auto it = incoming.begin(); it != incoming.end(); ++it)
{
if (existing.contains(it.key()))
{
const auto answer = QMessageBox::question(
this, tr("Rapport déjà existant"),
tr("Un rapport nommé « %1 » existe déjà. Le remplacer ?").arg(it.key()),
QMessageBox::Yes | QMessageBox::No);
if (answer != QMessageBox::Yes) {
++skipped;
continue;
}
}
existing[it.key()] = it.value();
++imported;
}
if (dest_file.open(QFile::WriteOnly | QFile::Truncate))
{
dest_file.write(QJsonDocument(existing).toJson());
dest_file.close();
}
else
{
QMessageBox::critical(this, tr("Erreur"), tr("Impossible d'écrire la configuration locale."));
return;
}
ui->m_conf_cb->clear();
fillSavedQuery();
QMessageBox::information(
this, tr("Importer"),
tr("%1 rapport(s) importé(s), %2 ignoré(s).").arg(imported).arg(skipped));
}
void ElementQueryWidget::on_m_choosen_list_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
+3
View File
@@ -59,6 +59,9 @@ class ElementQueryWidget : public QWidget
void on_m_load_pb_clicked();
void on_m_save_current_conf_pb_clicked();
void on_m_save_name_le_textChanged(const QString &arg1);
void on_m_import_reports_pb_clicked();
void on_m_export_reports_pb_clicked();
void checkQueryValidity();
void on_m_choosen_list_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
void on_m_var_list_itemDoubleClicked(QListWidgetItem *item);
void on_m_choosen_list_itemDoubleClicked(QListWidgetItem *item);
+33
View File
@@ -388,6 +388,26 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="m_import_reports_pb">
<property name="toolTip">
<string>Importer des rapports depuis un fichier</string>
</property>
<property name="text">
<string>Importer...</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="m_export_reports_pb">
<property name="toolTip">
<string>Exporter tous les rapports enregistrés vers un fichier</string>
</property>
<property name="text">
<string>Exporter...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -429,6 +449,19 @@
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="m_query_warning_label">
<property name="styleSheet">
<string notr="true">color: red;</string>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
+1
View File
@@ -216,6 +216,7 @@ class Diagram : public QGraphicsScene
// methods related to graphics items addition/removal on the diagram
virtual void addItem (QGraphicsItem *item);
virtual void removeItem (QGraphicsItem *item);
bool hasEventInterface() const { return m_event_interface != nullptr; }
// methods related to graphics options
ExportProperties applyProperties(const ExportProperties &);
+14 -10
View File
@@ -87,12 +87,12 @@ DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &star
*/
DiagramEventAddPaste::~DiagramEventAddPaste()
{
if (!m_finished && m_diagram) {
cancel();
}
if (m_status_bar) {
m_status_bar->clearMessage();
if (!m_finished) {
removeItems();
m_finished = true;
m_running = false;
}
if (m_status_bar) m_status_bar->clearMessage();
}
/**
@@ -139,6 +139,10 @@ void DiagramEventAddPaste::moveTo(const QPointF &scene_pos)
it.key()->setPos(anchor + it.value());
}
}
const auto conductors = m_content.conductors(); // AnyConductor by default
for (auto *cond : conductors) {
cond->updatePath();
}
}
void DiagramEventAddPaste::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
@@ -216,13 +220,14 @@ void DiagramEventAddPaste::commit()
void DiagramEventAddPaste::cancel()
{
if (m_finished || !m_diagram) return;
removeItems();
m_finished = true;
m_running = false;
emit finish(); // only the user-driven path signals
}
//Conductors first: they hold pointers to the terminals of the
//elements below, so removing an element out from under one would
//leave it pointing at freed memory for as long as it is still in the
//scene.
void DiagramEventAddPaste::removeItems()
{
const QList<Conductor *> conductors = m_content.conductors(DiagramContent::AnyConductor);
for (auto *conductor : conductors) {
m_diagram->removeItem(conductor);
@@ -237,5 +242,4 @@ void DiagramEventAddPaste::cancel()
m_content.clear();
m_relative_pos.clear();
emit finish();
}
@@ -73,6 +73,7 @@ class DiagramEventAddPaste : public DiagramEventInterface
void commit();
void cancel();
void showHint();
void removeItems();
DiagramContent m_content;
///Each movable item's position relative to the group's top left,
@@ -317,10 +317,16 @@ void DiagramEventAddShape::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
did anything. Re-running the last known mouse position through
applyPosition() here makes the key press or release itself the
trigger, giving immediate feedback instead of waiting on chance.
Any other key falls back to standard handling, e.g. pressing ESC aborts the operation.
*/
void DiagramEventAddShape::keyPressEvent(QKeyEvent *event)
{
reapplyLastPosition(event);
if (event->key() == Qt::Key_Shift || event->key() == Qt::Key_Control) {
reapplyLastPosition(event);
event->setAccepted(true);
} else {
DiagramEventInterface::keyPressEvent(event);
}
}
void DiagramEventAddShape::keyReleaseEvent(QKeyEvent *event)
+5 -20
View File
@@ -216,11 +216,7 @@ void DiagramView::handleElementDrop(QDropEvent *event)
}
QPointF drop_pos;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
drop_pos = mapToScene(event->pos());
#else
drop_pos = mapToScene(event->position().toPoint());
#endif
if (location.path().endsWith(".qetmak")) {
diagram()->setEventInterface(new DiagramEventAddMacro(location, diagram(), drop_pos));
@@ -295,13 +291,8 @@ void DiagramView::handleTextDrop(QDropEvent *e) {
iti -> setHtml (e -> mimeData() -> text());
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
m_diagram->undoStack().push(new AddGraphicsObjectCommand(
iti, m_diagram, mapToScene(e->pos())));
#else
m_diagram->undoStack().push(new AddGraphicsObjectCommand(
iti, m_diagram, mapToScene(e->position().toPoint())));
#endif
}
/**
@@ -611,14 +602,7 @@ void DiagramView::mouseReleaseEvent(QMouseEvent *e)
QMenu *menu = new QMenu(this);
menu->addAction(act);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
menu->popup(e->globalPos());
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
menu->popup(e->pos());
#endif
menu->popup(e->globalPosition().toPoint());
}
m_free_rubberbanding = false;
@@ -749,6 +733,10 @@ void DiagramView::keyPressEvent(QKeyEvent *e)
//way off the canvas for someone working without a mouse.
//Escape steps back out: first it drops the selection, then it
//hands focus to the next widget.
if (m_diagram && m_diagram->hasEventInterface()) {
QGraphicsView::keyPressEvent(e); // let the active tool see it
return;
}
if (m_diagram && !m_diagram->selectedItems().isEmpty()) {
m_diagram->clearSelection();
} else {
@@ -1416,9 +1404,6 @@ void DiagramView::createTemplateFromSelection()
QFile file(full_path);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
out.setCodec("UTF-8"); // Qt6 QTextStream defaults to UTF-8
#endif
out << macro_doc.toString(4);
file.close();
qDebug() << "Template successfully saved to:" << full_path;
-38
View File
@@ -55,43 +55,6 @@ void MachineInfo::send_info_to_debug()
<< QLibraryInfo::isDebugBuild();
qInfo()<< "Qt library version:"
<< QLibraryInfo::version();
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
qInfo()<< "Qt library location default prefix:"
<< QLibraryInfo::location(QLibraryInfo::PrefixPath);
qInfo()<< "Qt library location documentation:"
<< QLibraryInfo::location(QLibraryInfo::DocumentationPath);
qInfo()<< "Qt library location headers:"
<< QLibraryInfo::location(QLibraryInfo::HeadersPath);
qInfo()<< "Qt library location libraries:"
<< QLibraryInfo::location(QLibraryInfo::LibrariesPath);
qInfo()<< "Qt library location executables:"
<< QLibraryInfo::location(QLibraryInfo::LibraryExecutablesPath);
qInfo()<< "Qt library location Qt binaries:"
<< QLibraryInfo::location(QLibraryInfo::BinariesPath);
qInfo()<< "Qt library location Qt plugins:"
<< QLibraryInfo::location(QLibraryInfo::PluginsPath);
qInfo()<< "Qt library location installed QML extensions:"
<< QLibraryInfo::location(QLibraryInfo::ImportsPath);
qInfo()<< "Qt library location installed QML extensions:"
<< QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
qInfo()<< "Qt library location dependent Qt data:"
<< QLibraryInfo::location(QLibraryInfo::ArchDataPath);
qInfo()<< "Qt library location independent Qt data:"
<< QLibraryInfo::location(QLibraryInfo::DataPath);
qInfo()<< "Qt library location translation:"
<< QLibraryInfo::location(QLibraryInfo::TranslationsPath);
qInfo()<< "Qt library location examples:"
<< QLibraryInfo::location(QLibraryInfo::ExamplesPath);
qInfo()<< "Qt library location Qt testcases:"
<< QLibraryInfo::location(QLibraryInfo::TestsPath);
#ifndef Q_OS_WIN
qInfo()<< "Qt library location Qt settings:"
<< QLibraryInfo::location(QLibraryInfo::SettingsPath);
#endif
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
qInfo()<< "Qt library path default prefix:"
<< QLibraryInfo::path(QLibraryInfo::PrefixPath);
qInfo()<< "Qt library path documentation:"
@@ -123,7 +86,6 @@ void MachineInfo::send_info_to_debug()
#ifndef Q_OS_WIN
qInfo()<< "Qt library path Qt settings:"
<< QLibraryInfo::path(QLibraryInfo::SettingsPath);
#endif
#endif
if (strlen(GIT_COMMIT_SHA)) {
qInfo() << "GitRevision " + QString(GIT_COMMIT_SHA);
+2 -9
View File
@@ -107,15 +107,8 @@ int main(int argc, char **argv)
QDomImplementation::ReturnNullNode);
//Creation and execution of the application
//HighDPI
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
qputenv("QT_ENABLE_HIGHDPI_SCALING", "1");
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(QetSettings::hdpiScaleFactorRoundingPolicy());
qputenv("QT_ENABLE_HIGHDPI_SCALING", "1");
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(QetSettings::hdpiScaleFactorRoundingPolicy());
// Headless command-line export: render a project to PDF/PNG/SVG without
+2 -23
View File
@@ -34,13 +34,6 @@
// Availability of Qt::GuiPrivate is verified at configure time in CMakeLists.txt.
#include <private/qpdf_p.h>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
# include <QDesktopWidget>
#else
# if TODO_LIST
# pragma message("@TODO remove code for QT 6 or later")
# endif
#endif
#include <QMarginsF>
#include <QPageSetupDialog>
#include <QPainter>
@@ -74,14 +67,7 @@ void ProjectPrintWindow::launchDialog(QETProject *project, QPrinter::OutputForma
print_dialog.setWindowFlags(Qt::Sheet);
#endif
print_dialog.setWindowTitle(tr("Options d'impression", "window title"));
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
print_dialog.setEnabledOptions(QAbstractPrintDialog::PrintShowPageSize);
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
qDebug()<<"Help code for QT 6 or later";
#endif
print_dialog.setOption(QAbstractPrintDialog::PrintShowPageSize, true);
if (print_dialog.exec() == QDialog::Rejected) {
delete printer_;
return;
@@ -204,17 +190,10 @@ void ProjectPrintWindow::requestPaint()
#ifdef QT_DEBUG
qDebug() << "--";
qDebug() << "DiagramPrintDialog::print printer_->resolution() before " << m_printer->resolution();
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qDebug() << "DiagramPrintDialog::print screennumber " << QApplication::desktop()->screenNumber();
#endif
qDebug() << "DiagramPrintDialog::print screen " << screen()->name();
#endif
// QApplication::desktop() was removed in Qt6; use QWidget::screen().
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QScreen *srn = QApplication::screens().at(QApplication::desktop()->screenNumber());
#else
QScreen *srn = screen();
#endif
qreal dotsPerInch = (qreal)srn->logicalDotsPerInch();
m_printer->setResolution(dotsPerInch);
-14
View File
@@ -705,14 +705,7 @@ bool QET::writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString *
}
QTextStream out(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
out.setCodec("UTF-8");
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
out.setEncoding(QStringConverter::Utf8);
#endif
out.setGenerateByteOrderMark(false);
out << xml_doc.toString(4);
if (!file.commit())
@@ -843,14 +836,7 @@ bool QET::writeToFile(QDomDocument &xml_doc, QFile *file, QString *error_message
QTextStream out(file);
out.seek(0);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
out.setCodec("UTF-8");
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
out.setEncoding(QStringConverter::Utf8);
#endif
out.setGenerateByteOrderMark(false);
out << xml_doc.toString(4);
if (opened_here) {
+20 -13
View File
@@ -231,14 +231,7 @@ void QETApp::setLanguage(const QString &desired_language) {
QString languages_path = languagesPath();
// load Qt library translations
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
QString qt_l10n_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
QString qt_l10n_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath);
#endif
if (!qtTranslator.load("qt_" + desired_language, qt_l10n_path))
{
qWarning() << "failed to load"
@@ -2667,14 +2660,28 @@ void QETApp::checkBackupFiles()
}
}
if (stale_files.isEmpty()) {
// Only offer an unretrieved crash dump when there's no project
// to recover this run -- discussion #644 step 5 is explicit
// that the two prompts must never both show at once.
checkCrashDump();
return;
if (!stale_files.isEmpty()) {
offerBackupFiles(stale_files);
}
// Discussion #644 step 5 asks that the recovery prompt and the crash
// report never show at the same time -- not that the report be dropped
// whenever there is something to recover. Offering it here, once the
// recovery prompt has been answered, keeps the two sequential without
// losing the report after the most common crash there is: one with a
// project open, which always leaves a stale file behind, so the report
// was unreachable in exactly the case it is most wanted (issue #901).
checkCrashDump();
}
/**
@brief QETApp::offerBackupFiles
Ask whether to reopen the recovery files left by a previous run, and
open or discard them accordingly.
@param stale_files : the recovery files to offer
*/
void QETApp::offerBackupFiles(const QList<KAutoSaveFile *> &stale_files)
{
QString text;
if(stale_files.size() == 1) {
text.append(tr("<b>Le fichier de restauration suivant a été trouvé,<br>"
+2
View File
@@ -31,6 +31,7 @@ class QSplashScreen;
class QMenu;
class QAction;
class QMainWindow;
class KAutoSaveFile;
#define QETAPP_COMMON_TBT_PROTOCOL "commontbt"
#define QETAPP_COMPANY_TBT_PROTOCOL "companytbt"
@@ -290,6 +291,7 @@ class QETApp : public QObject
void initSystemTray();
void buildSystemTrayMenu();
void checkBackupFiles();
void offerBackupFiles(const QList<KAutoSaveFile *> &stale_files);
void checkCrashDump();
void fetchWindowStats(
const QList<QETDiagramEditor *> &,
+4
View File
@@ -1754,6 +1754,10 @@ void QETDiagramEditor::addItemGroupTriggered(QAction *action)
// here makes the button's appearance match its actual state
// regardless of whether Qt's own change notification fired
// correctly.
if (DiagramView *dv = currentDiagramView())
dv->setFocus(); // so the view (and the active tool) actually receives Escape etc
if (QWidget *button = m_add_item_tool_bar->widgetForAction(action))
button->update();
}
-7
View File
@@ -279,14 +279,7 @@ bool QETXML::writeXmlFile(
}
QTextStream out(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
out.setCodec("UTF-8");
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
out.setEncoding(QStringConverter::Utf8);
#endif
out.setGenerateByteOrderMark(false);
out << xml_document.toString(4);
file.close();
+4 -16
View File
@@ -74,11 +74,11 @@ void QGIManager::release(QGraphicsItem *qgi) {
Demande au QGIManager de gerer plusieurs QGI
@param qgis QGraphicsItems a gerer
*/
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void QGIManager::manage(const QList<QGraphicsItem *> &qgis) {
foreach(QGraphicsItem *qgi, qgis) manage(qgi);
void QGIManager::manage(const QVector<QGraphicsItem *> &items) {
for (const auto &qgi : items) {
manage(qgi);
}
}
#endif
/**
Indique au QGIManager que pour chaque QGI fourni, une reference vers celui-ci
@@ -87,18 +87,6 @@ void QGIManager::manage(const QList<QGraphicsItem *> &qgis) {
sur la scene de ce QGIManager, alors il sera detruit.
@param qgis QGraphicsItems a ne plus gerer
*/
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void QGIManager::release(const QList<QGraphicsItem *> &qgis) {
foreach(QGraphicsItem *qgi, qgis) release(qgi);
}
#endif
void QGIManager::manage(const QVector<QGraphicsItem *> &items) {
for (const auto &qgi : items) {
manage(qgi);
}
}
void QGIManager::release(const QVector<QGraphicsItem *> &items) {
for (const auto &qgi : items) {
release(qgi);
-8
View File
@@ -45,14 +45,6 @@ class QGIManager {
public:
void manage(QGraphicsItem *);
void release(QGraphicsItem *);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// In Qt6 QVector is an alias for QList, so these overloads would collide
// with the QVector ones below (same signature). Keep them on Qt5 only.
QT_DEPRECATED_X("Use QGIManager::manage(const QVector<QGraphicsItem *> &) instead")
void manage(const QList<QGraphicsItem *> &);
QT_DEPRECATED_X("Use QGIManager::release(const QVector<QGraphicsItem *> &) instead")
void release(const QList<QGraphicsItem *> &);
#endif
void manage(const QVector<QGraphicsItem *> &items);
void release(const QVector<QGraphicsItem *> &items);
void setDestroyQGIOnDelete(bool);
-14
View File
@@ -255,14 +255,7 @@ void QTextOrientationWidget::paintEvent(QPaintEvent *event) {
*/
void QTextOrientationWidget::mouseMoveEvent(QMouseEvent *event) {
if (read_only_) return;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
bool drawn_angle_hovered = positionIsASquare(event -> localPos(), &highlight_angle_);
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
bool drawn_angle_hovered = positionIsASquare(event -> position(), &highlight_angle_);
#endif
if (must_highlight_angle_ != drawn_angle_hovered) {
must_highlight_angle_ = drawn_angle_hovered;
@@ -278,14 +271,7 @@ void QTextOrientationWidget::mouseReleaseEvent(QMouseEvent *event) {
if (read_only_) return;
double clicked_angle;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
bool drawn_angle_clicked = positionIsASquare(event -> localPos(), &clicked_angle);
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
bool drawn_angle_clicked = positionIsASquare(event -> position(), &clicked_angle);
#endif
if (drawn_angle_clicked) {
setOrientation(clicked_angle);
emit(orientationChanged(clicked_angle));
+4 -66
View File
@@ -8,9 +8,7 @@
*/
TitleBlockTemplateRenderer::TitleBlockTemplateRenderer(QObject *parent) :
QObject(parent),
m_titleblock_template(nullptr),
m_use_cache(true),
m_last_known_titleblock_width(-1)
m_titleblock_template(nullptr)
{
}
@@ -39,7 +37,6 @@ void TitleBlockTemplateRenderer::setTitleBlockTemplate(
const TitleBlockTemplate *titleblock_template) {
if (titleblock_template != m_titleblock_template) {
m_titleblock_template = titleblock_template;
invalidateRenderedTemplate();
}
}
@@ -49,7 +46,6 @@ void TitleBlockTemplateRenderer::setTitleBlockTemplate(
*/
void TitleBlockTemplateRenderer::setContext(const DiagramContext &context) {
m_context = context;
invalidateRenderedTemplate();
}
/**
@@ -84,21 +80,9 @@ void TitleBlockTemplateRenderer::render(QPainter *provided_painter,
int titleblock_width) {
if (!m_titleblock_template) return;
if (m_use_cache) {
// Do we really need to calculate all this again?
if (titleblock_width != m_last_known_titleblock_width
|| m_rendered_template.isNull()) {
renderToQPicture(titleblock_width);
}
provided_painter -> save();
m_rendered_template.play(provided_painter);
provided_painter -> restore();
} else {
m_titleblock_template -> render(*provided_painter,
m_context,
titleblock_width);
}
m_titleblock_template -> render(*provided_painter,
m_context,
titleblock_width);
}
/**
@@ -119,49 +103,3 @@ void TitleBlockTemplateRenderer::renderDxf(QRectF &title_block_rect,
file_path, color);
}
/**
@brief TitleBlockTemplateRenderer::renderToQPicture
Renders the titleblock to the internal QPicture
@param titleblock_width : Width of the titleblock to render
*/
void TitleBlockTemplateRenderer::renderToQPicture(int titleblock_width) {
if (!m_titleblock_template) return;
// we render the template on our internal QPicture
QPainter painter(&m_rendered_template);
m_titleblock_template -> render(painter, m_context, titleblock_width);
// memorize the last known width
m_last_known_titleblock_width = titleblock_width;
}
/**
@brief TitleBlockTemplateRenderer::invalidateRenderedTemplate
Invalidates the previous rendering of the template
by resetting the internal QPicture.
*/
void TitleBlockTemplateRenderer::invalidateRenderedTemplate()
{
m_rendered_template = QPicture();
}
/**
@brief TitleBlockTemplateRenderer::setUseCache
@param use_cache :
true for this renderer to use its QPicture-based cache, false otherwise.
*/
void TitleBlockTemplateRenderer::setUseCache(bool use_cache) {
m_use_cache = use_cache;
}
/**
@brief TitleBlockTemplateRenderer::useCache
@return true if this renderer uses its QPicture-based cache,
false otherwise.
*/
bool TitleBlockTemplateRenderer::useCache() const
{
return(m_use_cache);
}
-9
View File
@@ -41,18 +41,9 @@ class TitleBlockTemplateRenderer : public QObject
int height() const;
void render(QPainter *, int);
void renderDxf(QRectF &, int, QString &, int);
void invalidateRenderedTemplate();
void setUseCache(bool);
bool useCache() const;
private:
void renderToQPicture(int);
private:
const TitleBlockTemplate *m_titleblock_template;
bool m_use_cache;
QPicture m_rendered_template;
DiagramContext m_context;
int m_last_known_titleblock_width;
};
#endif
+49 -1
View File
@@ -26,6 +26,7 @@
#include <QMessageBox>
#include <QSqlError>
#include <QSqlQueryModel>
#include <QSqlRecord>
/**
@@ -44,6 +45,9 @@ BOMExportDialog::BOMExportDialog(QETProject *project, QWidget *parent) :
ui->m_main_layout->insertWidget(0, m_query_widget);
m_query_widget->setQuery(BomExport::defaultQuery());
on_m_format_as_bom_clicked(false);
m_preview_model = new QSqlQueryModel(this);
ui->m_preview_table->setModel(m_preview_model);
}
/**
@@ -89,7 +93,15 @@ QByteArray BOMExportDialog::getBom(QString *error)
error->clear();
}
m_project->dataBase()->updateDB();
auto query_ = m_project->dataBase()->newQuery(m_query_widget->queryStr());
QString rejection;
auto query_ = m_project->dataBase()->newQuery(m_query_widget->queryStr(), &rejection);
if (!rejection.isEmpty()) {
if (error) {
*error = rejection;
}
return {};
}
if (!query_.exec()) {
qDebug() << "BOMExportDialog::getBom : query errir : " << query_.lastError();
@@ -130,3 +142,39 @@ void BOMExportDialog::on_m_format_as_bom_clicked(bool checked) {
m_query_widget->setGroupBy("designation", checked);
m_query_widget->setCount("COUNT(*) AS designation_qty", checked);
}
/**
@brief BOMExportDialog::on_m_preview_pb_clicked
Run the current query and show its result live, without going through
the CSV round-trip -- lets a report be checked and adjusted before
committing to a file (qelectrotech-source-mirror#886).
*/
void BOMExportDialog::on_m_preview_pb_clicked()
{
m_project->dataBase()->updateDB();
QString rejection;
auto query_ = m_project->dataBase()->newQuery(m_query_widget->queryStr(), &rejection);
if (!rejection.isEmpty()) {
QMessageBox::warning(this, tr("Requête refusée"), rejection);
return;
}
if (!query_.exec()) {
QMessageBox::warning(
this, tr("Erreur"),
tr("Erreur dans la requête :\n%1").arg(query_.lastError().text()));
return;
}
m_preview_model->setQuery(std::move(query_));
for (int i = 0; i < m_preview_model->columnCount(); ++i)
{
const auto field_name = m_preview_model->record().fieldName(i);
const auto translated = QETInformation::translatedInfoKey(field_name);
if (!translated.isEmpty()) {
m_preview_model->setHeaderData(i, Qt::Horizontal, translated);
}
}
ui->m_preview_table->resizeColumnsToContents();
}
+3
View File
@@ -23,6 +23,7 @@
class QETProject;
class ElementQueryWidget;
class QSqlQueryModel;
namespace Ui {
class BOMExportDialog;
@@ -44,11 +45,13 @@ class BOMExportDialog : public QDialog
private slots:
void on_m_format_as_bom_clicked(bool checked);
void on_m_preview_pb_clicked();
private:
Ui::BOMExportDialog *ui;
ElementQueryWidget *m_query_widget = nullptr;
QETProject *m_project = nullptr;
QSqlQueryModel *m_preview_model = nullptr;
};
#endif // BOMEXPORTDIALOG_H
+32 -1
View File
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>610</width>
<height>232</height>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
@@ -56,6 +56,37 @@
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="m_preview_layout">
<item>
<widget class="QPushButton" name="m_preview_pb">
<property name="text">
<string>Aperçu</string>
</property>
</widget>
</item>
<item>
<spacer name="m_preview_spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="m_preview_table">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
@@ -65,15 +65,7 @@ ClickableImageLabel::ClickableImageLabel(const QImage &sourceImage, QWidget *par
*/
void ClickableImageLabel::mousePressEvent(QMouseEvent *event)
{
// QLabel::pixmap() returns a pointer in Qt5 and a value in Qt6.
// Qt 5.15 offers the by-value form behind Qt::ReturnByValue; the
// pointer overload is deprecated there, so take the by-value one
// on both and the difference reduces to the argument.
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
const QPixmap label_pixmap = pixmap(Qt::ReturnByValue);
#else
const QPixmap label_pixmap = pixmap();
#endif
if (event->button() != Qt::LeftButton || label_pixmap.isNull())
return;
+62 -18
View File
@@ -25,10 +25,14 @@
#include "../undocommand/linkelementcommand.h"
#include "../qetinformation.h"
#include "../qetproject.h"
#include "../qetgraphicsitem/masterelement.h"
#include "../ui_linksingleelementwidget.h"
#include <QTreeWidgetItem>
#include <QInputDialog>
#include <QCheckBox>
#include <QSettings>
#include <QSignalBlocker>
/**
@@ -52,6 +56,22 @@ LinkSingleElementWidget::LinkSingleElementWidget(Element *elmt,
m_show_qtwi = new QAction(tr("Montrer l'élément"), this);
m_show_element = new QAction(tr("Montrer l'élément esclave"), this);
m_save_header_state = new QAction(tr("Enregistrer la disposition"), this);
// Hide the full-masters checkbox for non-slave elements
const bool is_slave = (elmt && elmt->elementData().m_type == ElementData::Slave);
ui->m_hide_full_masters_cb->setVisible(is_slave);
// Restore persisted state before connecting to avoid toggled firing buildTree()
// while m_element is still null.
{
const QSignalBlocker blocker(ui->m_hide_full_masters_cb);
QSettings settings;
ui->m_hide_full_masters_cb->setChecked(
settings.value(QStringLiteral("link-element-widget/hideFullMasters"), false).toBool());
}
connect(ui->m_hide_full_masters_cb, &QCheckBox::toggled,
this, &LinkSingleElementWidget::hideFullMastersToggled);
connect(m_show_qtwi, &QAction::triggered, this, [=]()
{
@@ -249,6 +269,7 @@ void LinkSingleElementWidget::buildTree()
if (m_element->elementData().m_type == ElementData::Slave)
{
m_full_masters.clear();
for(const auto &elmt : elmt_vector)
{
@@ -288,6 +309,16 @@ void LinkSingleElementWidget::buildTree()
QTreeWidgetItem *qtwi = new QTreeWidgetItem(ui->m_tree_widget, str_list);
m_qtwi_elmt_hash.insert(qtwi, elmt);
m_qtwi_strl_hash.insert(qtwi, search_list);
// Check if this master is full
if (elmt->linkType() == Element::Master) {
MasterElement *me = qobject_cast<MasterElement*>(elmt);
if (me && me->isFull()) {
m_full_masters.insert(qtwi);
if (ui->m_hide_full_masters_cb->isChecked())
qtwi->setHidden(true);
}
}
}
@@ -455,6 +486,8 @@ void LinkSingleElementWidget::setUpCompleter()
*/
void LinkSingleElementWidget::clearTreeWidget()
{
m_pending_qtwi = nullptr;
while(ui->m_tree_widget->topLevelItemCount())
{
QTreeWidgetItem *qtwi = ui->m_tree_widget->takeTopLevelItem(0);
@@ -467,6 +500,7 @@ void LinkSingleElementWidget::clearTreeWidget()
m_qtwi_elmt_hash.clear();
m_qtwi_strl_hash.clear();
m_full_masters.clear();
}
void LinkSingleElementWidget::setUpHeaderLabels()
@@ -675,6 +709,7 @@ void LinkSingleElementWidget::hideButtons()
ui->m_show_linked_pb->hide();
ui->m_show_this_pb->hide();
ui->m_search_field->show();
ui->m_hide_full_masters_cb->show();
}
/**
@@ -688,6 +723,7 @@ void LinkSingleElementWidget::showButtons()
ui->m_show_linked_pb->show();
ui->m_show_this_pb->show();
ui->m_search_field->hide();
ui->m_hide_full_masters_cb->hide();
}
void LinkSingleElementWidget::headerCustomContextMenuRequested(
@@ -783,27 +819,35 @@ void LinkSingleElementWidget::on_m_show_this_pb_clicked()
If arg1 is empty, show all items.
@param arg1
*/
void LinkSingleElementWidget::on_m_search_field_textEdited(const QString &arg1)
void LinkSingleElementWidget::on_m_search_field_textEdited(const QString &)
{
//Show all items if arg1 is empty, if not hide all items
foreach(QTreeWidgetItem *qtwi, m_qtwi_elmt_hash.keys())
qtwi->setHidden(!arg1.isEmpty());
QList <QTreeWidgetItem *> qtwi_list;
foreach(QTreeWidgetItem *qtwi, m_qtwi_strl_hash.keys())
updateItemsVisibility();
}
void LinkSingleElementWidget::hideFullMastersToggled(bool checked)
{
QSettings settings;
settings.setValue(QStringLiteral("link-element-widget/hideFullMasters"), checked);
updateItemsVisibility();
}
void LinkSingleElementWidget::updateItemsVisibility()
{
const QString text = ui->m_search_field->text();
const bool hide_full = ui->m_hide_full_masters_cb->isChecked();
for (auto it = m_qtwi_strl_hash.cbegin(); it != m_qtwi_strl_hash.cend(); ++it)
{
foreach(QString str, m_qtwi_strl_hash.value(qtwi))
{
if(str.contains(arg1, Qt::CaseInsensitive))
{
qtwi_list << qtwi;
continue;
bool match = text.isEmpty();
if (!match) {
for (const QString &str : it.value()) {
if (str.contains(text, Qt::CaseInsensitive)) {
match = true;
break;
}
}
}
it.key()->setHidden(!match
|| (hide_full && m_full_masters.contains(it.key())));
}
//Show items which match with arg1
foreach(QTreeWidgetItem *qtwi, qtwi_list)
qtwi->setHidden(false);
}
+5
View File
@@ -26,6 +26,7 @@
#include <QComboBox>
class QTreeWidgetItem;
class QCheckBox;
class Element;
class QMenu;
@@ -71,6 +72,7 @@ class LinkSingleElementWidget : public AbstractElementPropertiesEditorWidget
void setUpCompleter();
void clearTreeWidget();
void setUpHeaderLabels();
void updateItemsVisibility();
private slots:
void diagramWasRemovedFromProject();
@@ -87,6 +89,7 @@ class LinkSingleElementWidget : public AbstractElementPropertiesEditorWidget
void on_m_show_this_pb_clicked();
void on_m_search_field_textEdited(const QString &arg1);
void hideFullMastersToggled(bool checked);
private:
Ui::LinkSingleElementWidget *ui;
@@ -103,6 +106,8 @@ class LinkSingleElementWidget : public AbstractElementPropertiesEditorWidget
Element *m_showed_element = nullptr,
*m_element_to_link = nullptr;
QSet<QTreeWidgetItem*> m_full_masters;
int m_pending_group_index = -1;
QMenu *m_context_menu{nullptr};
+5 -15
View File
@@ -21,7 +21,7 @@
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<item row="4" column="0" colspan="3">
<widget class="QTreeWidget" name="m_tree_widget">
<property name="indentation">
<number>5</number>
@@ -57,30 +57,20 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<item row="3" column="0" colspan="3">
<widget class="QLineEdit" name="m_search_field">
<property name="placeholderText">
<string>Recherche</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QLabel" name="m_hidden_masters_label">
<item row="2" column="0" colspan="3">
<widget class="QCheckBox" name="m_hide_full_masters_cb">
<property name="text">
<string>Remarque : les éléments maîtres ayant atteint leur nombre maximal d'esclaves sont masqués.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="font">
<font>
<italic>true</italic>
</font>
<string>Masquer les éléments maîtres pleins</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
-4
View File
@@ -200,9 +200,6 @@ namespace
*/
QString QETUtils::fontToString(const QFont &font)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
return font.toString();
#else
const int legacy_weight = legacyFontWeight(font.weight());
const QChar comma(QLatin1Char(','));
@@ -220,7 +217,6 @@ QString QETUtils::fontToString(const QFont &font)
description += QChar(',') + font.styleName();
}
return description;
#endif
}
/**
+1 -19
View File
@@ -40,31 +40,13 @@ if(NOT DEFINED QET_COMPONENTS)
message(".. QET_COMPONENTS is not set !!! I set them up !!!")
include(../../cmake/qet_compilation_vars.cmake)
endif()
if(NOT DEFINED QT_VERSION_MAJOR)
find_package(
QT
NAMES
Qt6
Qt5
COMPONENTS
${QET_COMPONENTS}
REQUIRED
)
endif()
message(".. QT_VERSION_MAJOR :" ${QT_VERSION_MAJOR})
find_package(
Qt${QT_VERSION_MAJOR}
Qt6
COMPONENTS
${QET_COMPONENTS}
REQUIRED)
# Qt6-only: the tests link ${QET_PRIVATE_LIBRARIES}, which contains
# Qt::GuiPrivate; Qt6 only creates that target when the component is
# explicitly requested (Qt5 creates it implicitly and has no such package).
if(QT_VERSION_MAJOR GREATER_EQUAL 6)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS GuiPrivate)
endif()
Include(FetchContent)
+1 -13
View File
@@ -40,21 +40,9 @@ if(NOT DEFINED QET_COMPONENTS)
message(".. QET_COMPONENTS is not set !!! I set them up !!!")
include(../../cmake/qet_compilation_vars.cmake)
endif()
if(NOT DEFINED QT_VERSION_MAJOR)
find_package(
QT
NAMES
Qt6
Qt5
COMPONENTS
${QET_COMPONENTS}
REQUIRED
)
endif()
message(".. QT_VERSION_MAJOR :" ${QT_VERSION_MAJOR})
find_package(
Qt${QT_VERSION_MAJOR}
Qt6
COMPONENTS
${QET_COMPONENTS}
REQUIRED)
+1 -13
View File
@@ -40,21 +40,9 @@ if(NOT DEFINED QET_COMPONENTS)
message(".. QET_COMPONENTS is not set !!! I set them up !!!")
include(../../cmake/qet_compilation_vars.cmake)
endif()
if(NOT DEFINED QT_VERSION_MAJOR)
find_package(
QT
NAMES
Qt6
Qt5
COMPONENTS
${QET_COMPONENTS}
REQUIRED
)
endif()
message(".. QT_VERSION_MAJOR :" ${QT_VERSION_MAJOR})
find_package(
Qt${QT_VERSION_MAJOR}
Qt6
COMPONENTS
${QET_COMPONENTS}
REQUIRED)
-15
View File
@@ -83,21 +83,6 @@ for tool in Xvfb openbox xdotool; do
command -v "$tool" >/dev/null || { echo "missing required tool: $tool" >&2; exit 2; }
done
# Qt 5 cannot prove anything here. An unfixed Qt 5 build of master survives
# this scenario every time -- measured, not assumed -- so a Qt 5 run reports
# PASS whether or not the bug is present. Refusing is the only honest answer:
# a green that cannot go red is worse than no test.
#
# Reproduced on Qt 6.10.2, which is also the version in the original report.
# Whether older Qt 6 reproduces it is UNVERIFIED -- if you run this on Qt 6.2
# or 6.4 and it passes, confirm against a deliberately unfixed build before
# believing it.
if ldd "$BINARY" 2>/dev/null | grep -q "libQt5Core"; then
echo "INCONCLUSIVE: $BINARY links Qt 5." >&2
echo " This crash only reproduces on Qt 6; an unfixed Qt 5 build" >&2
echo " survives every attempt. Build against Qt 6 to use this gate." >&2
exit 2
fi
SANDBOX="$(mktemp -d /tmp/qet-ipc-regression.XXXXXX)"
LOG_DIR="$SANDBOX/logs"; mkdir -p "$LOG_DIR"
+1 -14
View File
@@ -40,22 +40,9 @@ if(NOT DEFINED QET_COMPONENTS)
message(".. QET_COMPONENTS is not set !!! I set them up !!!")
include(../../cmake/qet_compilation_vars.cmake)
endif()
if(NOT DEFINED QT_VERSION_MAJOR)
find_package(
QT
NAMES
Qt6
Qt5
COMPONENTS
${QET_COMPONENTS}
Test
REQUIRED
)
endif()
message(".. QT_VERSION_MAJOR :" ${QT_VERSION_MAJOR})
find_package(
Qt${QT_VERSION_MAJOR}
Qt6
COMPONENTS
${QET_COMPONENTS}
Test