Compare commits

..

1 Commits

Author SHA1 Message Date
scorpio810 6132cc0503 ci: remove auto-doxygen workflow
Le workflow régénérait un fichier doc/QElectroTech.qch de ~500 Mo à
chaque push de tag (y compris le tag nightly), ce qui a fait grimper
la conso mensuelle de bande passante Git LFS de l'organisation
qelectrotech à 90% du quota.
2026-09-21 16:25:31 +02:00
192 changed files with 881 additions and 140061 deletions
+1
View File
@@ -0,0 +1 @@
*.qch filter=lfs diff=lfs merge=lfs -text
+2 -4
View File
@@ -28,7 +28,7 @@ jobs:
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates build-essential cmake ninja-build git pkg-config \ ca-certificates build-essential cmake ninja-build git pkg-config \
qt6-base-dev qt6-base-private-dev qt6-tools-dev qt6-tools-dev-tools \ qt6-base-dev qt6-base-private-dev qt6-tools-dev qt6-tools-dev-tools \
libqt6svg6-dev libqt6sql6-sqlite libcups2-dev libsqlite3-dev \ libqt6svg6-dev libqt6sql6-sqlite libsqlite3-dev libcups2-dev \
libxkbcommon-x11-0 \ libxkbcommon-x11-0 \
xvfb openbox xdotool x11-utils xvfb openbox xdotool x11-utils
# extra-cmake-modules and the KF6 libraries are installed rather than # extra-cmake-modules and the KF6 libraries are installed rather than
@@ -53,9 +53,7 @@ jobs:
git config --global --add safe.directory "$GITHUB_WORKSPACE" git config --global --add safe.directory "$GITHUB_WORKSPACE"
cmake -B build -G Ninja \ cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_BUILD_TYPE=Debug \
-DQT_VERSION_MAJOR=6 \ -DQT_VERSION_MAJOR=6
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DQET_EXPORT_PROJECT_DB=ON
- name: Build - name: Build
run: cmake --build build --parallel $(nproc) run: cmake --build build --parallel $(nproc)
-10
View File
@@ -57,7 +57,6 @@ jobs:
mingw-w64-ucrt-x86_64-nsis mingw-w64-ucrt-x86_64-nsis
mingw-w64-ucrt-x86_64-angleproject mingw-w64-ucrt-x86_64-angleproject
mingw-w64-ucrt-x86_64-qt6-declarative mingw-w64-ucrt-x86_64-qt6-declarative
mingw-w64-ucrt-x86_64-hidapi
- name: Cache ccache - name: Cache ccache
uses: actions/cache@v5 uses: actions/cache@v5
@@ -121,8 +120,6 @@ jobs:
-DCMAKE_POLICY_DEFAULT_CMP0077=NEW \ -DCMAKE_POLICY_DEFAULT_CMP0077=NEW \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DQET_EXPORT_PROJECT_DB=ON \ -DQET_EXPORT_PROJECT_DB=ON \
-DQET_ENABLE_SPACEMOUSE=ON \
-DQET_SPACEMOUSE_BACKEND=hid \
-DCMAKE_C_COMPILER_LAUNCHER=/ucrt64/bin/ccache \ -DCMAKE_C_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=/ucrt64/bin/ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=/ucrt64/bin/ccache \
-DSQLite3_INCLUDE_DIR=/ucrt64/include \ -DSQLite3_INCLUDE_DIR=/ucrt64/include \
@@ -149,11 +146,6 @@ jobs:
SIZE=$(stat -c%s "$EXE") SIZE=$(stat -c%s "$EXE")
echo "Exe found: $EXE ($SIZE bytes)" echo "Exe found: $EXE ($SIZE bytes)"
[ "$SIZE" -gt 100000 ] || { echo "ERROR: exe too small"; exit 1; } [ "$SIZE" -gt 100000 ] || { echo "ERROR: exe too small"; exit 1; }
# 3D mouse support (discussion #599): CMake only warns when hidapi
# is missing, which would ship a package without it -- fail here.
ldd "$EXE" | grep -qi 'libhidapi' \
|| { echo "ERROR: exe not linked against hidapi -- 3D mouse support missing"; exit 1; }
echo "3D mouse support: linked against $(ldd "$EXE" | grep -io 'libhidapi[^ ]*' | head -1)"
- name: Deploy — copy exe + windeployqt + DLLs - name: Deploy — copy exe + windeployqt + DLLs
shell: msys2 {0} shell: msys2 {0}
@@ -199,8 +191,6 @@ jobs:
echo "=== $DLL_COUNT DLLs present after scan ===" echo "=== $DLL_COUNT DLLs present after scan ==="
ls -lh "$BIN/QElectroTech.exe" || { echo "ERROR: exe missing from bin/"; exit 1; } ls -lh "$BIN/QElectroTech.exe" || { echo "ERROR: exe missing from bin/"; exit 1; }
[ "$DLL_COUNT" -gt 5 ] || { echo "ERROR: too few DLLs"; exit 1; } [ "$DLL_COUNT" -gt 5 ] || { echo "ERROR: too few DLLs"; exit 1; }
# Without it QElectroTech.exe would not start at all.
[ -f "$BIN/libhidapi-0.dll" ] || { echo "ERROR: libhidapi-0.dll not deployed"; exit 1; }
cd "$GITHUB_WORKSPACE" cd "$GITHUB_WORKSPACE"
cp /ucrt64/bin/libgcc_s_seh-1.dll "$BIN/" cp /ucrt64/bin/libgcc_s_seh-1.dll "$BIN/"
+10 -53
View File
@@ -59,41 +59,17 @@ jobs:
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# 2. Download the portable artifact for this flavor # 2. Download the portable artifact for this flavor
#
# Wrapped in nick-fields/retry: actions/download-artifact@v8 has
# shown repeated "Artifact download failed after 5 retries"
# failures on this cross-workflow download (via run-id) — not a
# real content/digest problem, just flaky Azure blob delivery.
# Two separate occurrences observed within days of each other
# (different artifact IDs/digests, same failure signature), each
# one enough to fail build-msi outright and block the rest of the
# pipeline. Retrying the whole download 3x is cheap insurance.
# Switched to `gh run download` here because nick-fields/retry
# can only retry a shell command, not re-invoke a `uses:` step.
# ---------------------------------------------------------------- # ----------------------------------------------------------------
- name: Download portable artifact - name: Download portable artifact
uses: nick-fields/retry@v3 uses: actions/download-artifact@v8
with: with:
timeout_minutes: 10 name: ${{ matrix.portable_artifact }}
max_attempts: 3 path: artifact\files
retry_wait_seconds: 30 # workflow_run => use the triggering run's ID
shell: pwsh # workflow_dispatch => use input run_id if provided, otherwise current run
command: | run-id: ${{ github.event.workflow_run.id || github.event.inputs.run_id || github.run_id }}
if (Test-Path "artifact\files") { Remove-Item -Recurse -Force "artifact\files" } github-token: ${{ secrets.GITHUB_TOKEN }}
New-Item -ItemType Directory -Force -Path "artifact\files" | Out-Null repository: ${{ github.repository }}
$runId = "${{ github.event.workflow_run.id || github.event.inputs.run_id || github.run_id }}"
gh run download $runId `
--repo "${{ github.repository }}" `
--name "${{ matrix.portable_artifact }}" `
--dir "artifact\files"
if ($LASTEXITCODE -ne 0) {
Write-Error "gh run download failed (exit $LASTEXITCODE)"
exit $LASTEXITCODE
}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# 3. Extract version # 3. Extract version
@@ -360,13 +336,7 @@ jobs:
if-no-files-found: error if-no-files-found: error
- name: Delete old nightly .msi asset - name: Delete old nightly .msi asset
# Only run if a new MSI actually exists in dist/ — otherwise the old if: always()
# (still working) nightly .msi would be deleted without anything to
# replace it, leaving the nightly release with no MSI at all until
# the next successful build (see windows-msi-pipeline notes,
# run 35694391567: an upstream artifact-download failure meant
# dist\*.msi never existed for that run).
if: always() && hashFiles('dist/*.msi') != ''
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }} REPO: ${{ github.repository }}
@@ -411,20 +381,7 @@ jobs:
deploy-pages: deploy-pages:
needs: build-msi needs: build-msi
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Regenerate the page whenever the MSI job actually ran (success OR if: always() && needs.build-msi.result == 'success'
# packaging/signing failure) — not only on full success.
# generate-page.py queries the published assets on the "nightly"
# release itself (line 407: `gh release view nightly --json assets`),
# so it already handles a missing MSI natively (empty MSI_NAME -> no
# MSI button on the page). The page only needs the exe/zip already
# published by windows-build.yml, independent of the MSI's fate.
# Only "skipped"/"cancelled" are excluded: if build-msi never ran at
# all (e.g. Windows Build itself failed), there is nothing new to
# publish and regenerating the page would be pointless.
if: >
always() &&
needs.build-msi.result != 'skipped' &&
needs.build-msi.result != 'cancelled'
permissions: permissions:
contents: write contents: write
pages: write pages: write
+9 -3
View File
@@ -1,7 +1,5 @@
*.snap *.snap
.flatpak-builder .flatpak-builder
__pycache__/
*.pyc
# Qt build output # Qt build output
*.user *.user
# doxygen Doxyfile output # doxygen Doxyfile output
@@ -9,4 +7,12 @@ doc/*
!doc/QElectroTech.qch !doc/QElectroTech.qch
QElectroTech.tag QElectroTech.tag
!doc/doc-utils !doc/doc-utils
ui_*.h lang/*.qm
# -- IDE settings/intermediate files --
# zed
.zed
.cache
# VS Code
.vscode
+1 -9
View File
@@ -36,7 +36,6 @@ include(cmake/git_update_submodules.cmake)
include(cmake/git_last_commit_sha.cmake) include(cmake/git_last_commit_sha.cmake)
include(cmake/fetch_singleapplication.cmake) include(cmake/fetch_singleapplication.cmake)
include(cmake/fetch_pugixml.cmake) include(cmake/fetch_pugixml.cmake)
include(cmake/find_spacemouse.cmake)
include(cmake/qet_compilation_vars.cmake) include(cmake/qet_compilation_vars.cmake)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
@@ -170,13 +169,6 @@ else()
) )
endif() endif()
if(QET_SPACEMOUSE_BACKEND_SPNAV_ENABLED)
list(APPEND QET_SPACEMOUSE_LIBRARIES PkgConfig::SPNAV)
endif()
if(QET_SPACEMOUSE_BACKEND_HID_ENABLED)
list(APPEND QET_SPACEMOUSE_LIBRARIES PkgConfig::HIDAPI)
endif()
if(APPLE) if(APPLE)
# CFBundleIdentifier must not be empty. CMake's default Info.plist # CFBundleIdentifier must not be empty. CMake's default Info.plist
# template fills it from MACOSX_BUNDLE_GUI_IDENTIFIER; with that unset # template fills it from MACOSX_BUNDLE_GUI_IDENTIFIER; with that unset
@@ -295,7 +287,6 @@ target_link_libraries(
SQLite3::SQLite3 SQLite3::SQLite3
${KF_PRIVATE_LIBRARIES} ${KF_PRIVATE_LIBRARIES}
${QET_PRIVATE_LIBRARIES} ${QET_PRIVATE_LIBRARIES}
${QET_SPACEMOUSE_LIBRARIES}
) )
target_include_directories( target_include_directories(
@@ -327,6 +318,7 @@ target_include_directories(
${QET_DIR}/sources/NameList ${QET_DIR}/sources/NameList
${QET_DIR}/sources/NameList/ui ${QET_DIR}/sources/NameList/ui
${QET_DIR}/sources/utils ${QET_DIR}/sources/utils
${QET_DIR}/pugixml/src
${QET_DIR}/sources/dataBase ${QET_DIR}/sources/dataBase
${QET_DIR}/sources/dataBase/ui ${QET_DIR}/sources/dataBase/ui
${QET_DIR}/sources/factory/ui ${QET_DIR}/sources/factory/ui
+1 -4
View File
@@ -50,7 +50,6 @@ parts:
- python3-lxml - python3-lxml
- python3-tk - python3-tk
- libtk8.6 - libtk8.6
- libspnav0
kde-sdk-setup: kde-sdk-setup:
plugin: nil plugin: nil
@@ -93,7 +92,6 @@ parts:
# runs FindCups at configure time and fails without the CUPS headers. # runs FindCups at configure time and fails without the CUPS headers.
# Build-time only: nothing from it is staged into the snap. # Build-time only: nothing from it is staged into the snap.
- libcups2-dev - libcups2-dev
- libspnav-dev
override-build: | override-build: |
displayed_version=$(cat sources/qetversion.cpp | grep "return QVersionNumber{"| head -n 1| awk -F "{" '{ print $2 }' | awk -F "}" '{ print $1 }' | sed -e 's/,/./g' -e 's/ //g') displayed_version=$(cat sources/qetversion.cpp | grep "return QVersionNumber{"| head -n 1| awk -F "{" '{ print $2 }' | awk -F "}" '{ print $1 }' | sed -e 's/,/./g' -e 's/ //g')
snap_version="${displayed_version}-g$(git rev-parse --short=8 HEAD)" snap_version="${displayed_version}-g$(git rev-parse --short=8 HEAD)"
@@ -108,8 +106,7 @@ parts:
-DBUILD_KF=OFF \ -DBUILD_KF=OFF \
-DPACKAGE_TESTS=OFF \ -DPACKAGE_TESTS=OFF \
-DBUILD_PUGIXML=ON \ -DBUILD_PUGIXML=ON \
-DQET_EXPORT_PROJECT_DB=ON \ -DQET_EXPORT_PROJECT_DB=ON
-DQET_ENABLE_SPACEMOUSE=ON
ninja -C build -j${CRAFT_PARALLEL_BUILD_COUNT} ninja -C build -j${CRAFT_PARALLEL_BUILD_COUNT}
DESTDIR="$CRAFT_PART_INSTALL" ninja -C build install DESTDIR="$CRAFT_PART_INSTALL" ninja -C build install
override-stage: | override-stage: |
-5
View File
@@ -47,8 +47,3 @@ option(BUILD_WITH_KF "Build with KDE Frameworks" ON)
# compile for everyone else. Leaving it off keeps CI and contributors on the # compile for everyone else. Leaving it off keeps CI and contributors on the
# strict behaviour, and only developers who opt in trade that for the speed. # strict behaviour, and only developers who opt in trade that for the speed.
option(QET_ENABLE_PCH "Use precompiled headers (developer build speed; may mask missing #includes)" OFF) option(QET_ENABLE_PCH "Use precompiled headers (developer build speed; may mask missing #includes)" OFF)
# Discussion #599: 3Dconnexion SpaceMouse/SpacePilot pan/zoom support. Off by
# default -- see cmake/find_spacemouse.cmake for the backends and what happens
# when it is on but no library is found.
option(QET_ENABLE_SPACEMOUSE "Build with 3D mouse (3Dconnexion SpaceMouse) support for pan/zoom; needs libspnav or hidapi, see QET_SPACEMOUSE_BACKEND" OFF)
-95
View File
@@ -1,95 +0,0 @@
# Copyright 2006 The QElectroTech Team
# This file is part of QElectroTech.
#
# QElectroTech is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# QElectroTech is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
message(" - find_spacemouse")
# QET_ENABLE_SPACEMOUSE (cmake/developer_options.cmake) is off by default,
# so none of this runs, and the default build is entirely unaffected: no new
# dependency, no new source files, no new symbols.
#
# When it is on, QET_SPACEMOUSE_BACKEND picks how the device is read -- see
# sources/spacemouse/spacemousebackend.h for what a backend is:
# spnav Linux, through the spacenavd daemon (libspnav)
# hid any platform, directly over USB (hidapi), no 3Dconnexion driver
# auto spnav on Linux when libspnav is found, hid otherwise
# On macOS the 3DxWare backend is added to whichever of these is chosen.
# A backend whose library is not found downgrades the option to off with a
# warning, rather than failing configure for an opt-in feature.
set(QET_SPACEMOUSE_BACKEND "auto" CACHE STRING "3D mouse backend: auto, spnav or hid")
set_property(CACHE QET_SPACEMOUSE_BACKEND PROPERTY STRINGS auto spnav hid)
set(QET_SPACEMOUSE_ENABLED FALSE)
set(QET_SPACEMOUSE_BACKEND_SPNAV_ENABLED FALSE)
set(QET_SPACEMOUSE_BACKEND_HID_ENABLED FALSE)
set(QET_SPACEMOUSE_BACKEND_CONNEXION_ENABLED FALSE)
if(QET_ENABLE_SPACEMOUSE)
find_package(PkgConfig)
set(_qet_spacemouse_try_spnav FALSE)
set(_qet_spacemouse_try_hid FALSE)
if(QET_SPACEMOUSE_BACKEND STREQUAL "spnav")
set(_qet_spacemouse_try_spnav TRUE)
elseif(QET_SPACEMOUSE_BACKEND STREQUAL "hid")
set(_qet_spacemouse_try_hid TRUE)
else()
if(UNIX AND NOT APPLE)
set(_qet_spacemouse_try_spnav TRUE)
endif()
set(_qet_spacemouse_try_hid TRUE)
endif()
# libspnav: Debian/Ubuntu's libspnav-dev ships spnav.pc.
if(_qet_spacemouse_try_spnav AND PkgConfig_FOUND)
pkg_check_modules(SPNAV IMPORTED_TARGET spnav)
if(SPNAV_FOUND)
set(QET_SPACEMOUSE_ENABLED TRUE)
set(QET_SPACEMOUSE_BACKEND_SPNAV_ENABLED TRUE)
add_definitions(-DQET_SPACEMOUSE_BACKEND_SPNAV)
message("QET_ENABLE_SPACEMOUSE ON (backend: libspnav ${SPNAV_VERSION})")
endif()
endif()
# hidapi: hidapi-hidraw.pc on Linux (libhidapi-dev), hidapi.pc from
# MSYS2 (mingw-w64-ucrt-x86_64-hidapi) and Homebrew (hidapi).
if(NOT QET_SPACEMOUSE_ENABLED AND _qet_spacemouse_try_hid AND PkgConfig_FOUND)
pkg_search_module(HIDAPI IMPORTED_TARGET hidapi-hidraw hidapi)
if(HIDAPI_FOUND)
set(QET_SPACEMOUSE_ENABLED TRUE)
set(QET_SPACEMOUSE_BACKEND_HID_ENABLED TRUE)
add_definitions(-DQET_SPACEMOUSE_BACKEND_HID)
message("QET_ENABLE_SPACEMOUSE ON (backend: hidapi ${HIDAPI_VERSION})")
endif()
endif()
# macOS: with 3DxWare installed the device can only be read through
# 3DxWare, so its backend comes too, whichever backend was asked for.
# It loads 3DxWare's library at run time and needs nothing to build.
if(APPLE)
set(QET_SPACEMOUSE_ENABLED TRUE)
set(QET_SPACEMOUSE_BACKEND_CONNEXION_ENABLED TRUE)
add_definitions(-DQET_SPACEMOUSE_BACKEND_CONNEXION)
message("QET_ENABLE_SPACEMOUSE ON (backend: 3DxWare when installed)")
endif()
if(QET_SPACEMOUSE_ENABLED)
add_definitions(-DQET_SPACEMOUSE_SUPPORT)
else()
message(WARNING "QET_ENABLE_SPACEMOUSE is ON but no library was found for the "
"'${QET_SPACEMOUSE_BACKEND}' backend (libspnav-dev for spnav, "
"hidapi for hid, via pkg-config) -- building WITHOUT 3D mouse support.")
endif()
endif()
@@ -62,9 +62,7 @@ if(WIN32)
# puts the .qm files (see build-aux/windows/QElectroTech.wxs and the # puts the .qm files (see build-aux/windows/QElectroTech.wxs and the
# windows-build workflow), and what the shortcuts pass as --lang-dir. # windows-build workflow), and what the shortcuts pass as --lang-dir.
set(QET_LANG_PATH "lang/") set(QET_LANG_PATH "lang/")
set(QET_EXAMPLES_PATH "examples/")
set(QET_LICENSE_PATH "./") set(QET_LICENSE_PATH "./")
set(QET_ICONS_PATH "icons/hicolor/")
# Liste des ressources Windows # Liste des ressources Windows
#RC_FILE = qelectrotech.rc #RC_FILE = qelectrotech.rc
endif() endif()
+4 -45
View File
@@ -297,8 +297,6 @@ set(QET_SRC_FILES
${QET_DIR}/sources/autoNum/numerotationcontextcommands.h ${QET_DIR}/sources/autoNum/numerotationcontextcommands.h
${QET_DIR}/sources/autoNum/numerotationcontext.cpp ${QET_DIR}/sources/autoNum/numerotationcontext.cpp
${QET_DIR}/sources/autoNum/numerotationcontext.h ${QET_DIR}/sources/autoNum/numerotationcontext.h
${QET_DIR}/sources/autoNum/renumberelementscommand.cpp
${QET_DIR}/sources/autoNum/renumberelementscommand.h
${QET_DIR}/sources/autoNum/ui/autonumberingdockwidget.cpp ${QET_DIR}/sources/autoNum/ui/autonumberingdockwidget.cpp
${QET_DIR}/sources/autoNum/ui/autonumberingdockwidget.h ${QET_DIR}/sources/autoNum/ui/autonumberingdockwidget.h
${QET_DIR}/sources/autoNum/ui/autonumberingmanagementw.cpp ${QET_DIR}/sources/autoNum/ui/autonumberingmanagementw.cpp
@@ -309,15 +307,11 @@ set(QET_SRC_FILES
${QET_DIR}/sources/autoNum/ui/formulaautonumberingw.h ${QET_DIR}/sources/autoNum/ui/formulaautonumberingw.h
${QET_DIR}/sources/autoNum/ui/numparteditorw.cpp ${QET_DIR}/sources/autoNum/ui/numparteditorw.cpp
${QET_DIR}/sources/autoNum/ui/numparteditorw.h ${QET_DIR}/sources/autoNum/ui/numparteditorw.h
${QET_DIR}/sources/autoNum/ui/renumberelementsdialog.cpp
${QET_DIR}/sources/autoNum/ui/renumberelementsdialog.h
${QET_DIR}/sources/autoNum/ui/selectautonumw.cpp ${QET_DIR}/sources/autoNum/ui/selectautonumw.cpp
${QET_DIR}/sources/autoNum/ui/selectautonumw.h ${QET_DIR}/sources/autoNum/ui/selectautonumw.h
${QET_DIR}/sources/dataBase/projectdatabase.cpp ${QET_DIR}/sources/dataBase/projectdatabase.cpp
${QET_DIR}/sources/dataBase/projectdatabase.h ${QET_DIR}/sources/dataBase/projectdatabase.h
${QET_DIR}/sources/dataBase/sqlreadonly.cpp
${QET_DIR}/sources/dataBase/sqlreadonly.h
${QET_DIR}/sources/dataBase/ui/elementquerywidget.cpp ${QET_DIR}/sources/dataBase/ui/elementquerywidget.cpp
${QET_DIR}/sources/dataBase/ui/elementquerywidget.h ${QET_DIR}/sources/dataBase/ui/elementquerywidget.h
@@ -513,6 +507,10 @@ set(QET_SRC_FILES
${QET_DIR}/sources/PropertiesEditor/propertieseditorwidget.cpp ${QET_DIR}/sources/PropertiesEditor/propertieseditorwidget.cpp
${QET_DIR}/sources/PropertiesEditor/propertieseditorwidget.h ${QET_DIR}/sources/PropertiesEditor/propertieseditorwidget.h
${QET_DIR}/pugixml/src/pugiconfig.hpp
${QET_DIR}/pugixml/src/pugixml.cpp
${QET_DIR}/pugixml/src/pugixml.hpp
${QET_DIR}/sources/qetgraphicsitem/conductor.cpp ${QET_DIR}/sources/qetgraphicsitem/conductor.cpp
${QET_DIR}/sources/qetgraphicsitem/conductor.h ${QET_DIR}/sources/qetgraphicsitem/conductor.h
${QET_DIR}/sources/qetgraphicsitem/conductortextitem.cpp ${QET_DIR}/sources/qetgraphicsitem/conductortextitem.cpp
@@ -727,8 +725,6 @@ set(QET_SRC_FILES
${QET_DIR}/sources/ui/backupdialog.h ${QET_DIR}/sources/ui/backupdialog.h
${QET_DIR}/sources/ui/dialogwaiting.cpp ${QET_DIR}/sources/ui/dialogwaiting.cpp
${QET_DIR}/sources/ui/dialogwaiting.h ${QET_DIR}/sources/ui/dialogwaiting.h
${QET_DIR}/sources/ui/duplicateoffsetdialog.cpp
${QET_DIR}/sources/ui/duplicateoffsetdialog.h
${QET_DIR}/sources/ui/dynamicelementtextitemeditor.cpp ${QET_DIR}/sources/ui/dynamicelementtextitemeditor.cpp
${QET_DIR}/sources/ui/dynamicelementtextitemeditor.h ${QET_DIR}/sources/ui/dynamicelementtextitemeditor.h
${QET_DIR}/sources/ui/dynamicelementtextmodel.cpp ${QET_DIR}/sources/ui/dynamicelementtextmodel.cpp
@@ -875,43 +871,6 @@ list(APPEND QET_SRC_FILES
${QET_DIR}/sources/scripting/qetscripting.h ${QET_DIR}/sources/scripting/qetscripting.h
) )
if(QET_SPACEMOUSE_ENABLED)
list(APPEND QET_SRC_FILES
${QET_DIR}/sources/spacemouse/spacemousebackend.h
${QET_DIR}/sources/spacemouse/spacemousebuttonmap.cpp
${QET_DIR}/sources/spacemouse/spacemousebuttonmap.h
${QET_DIR}/sources/spacemouse/spacemouselistener.cpp
${QET_DIR}/sources/spacemouse/spacemouselistener.h
${QET_DIR}/sources/spacemouse/spacemousemotion.cpp
${QET_DIR}/sources/spacemouse/spacemousemotion.h
${QET_DIR}/sources/ui/configpage/spacemouseconfigpage.cpp
${QET_DIR}/sources/ui/configpage/spacemouseconfigpage.h
)
endif()
if(QET_SPACEMOUSE_BACKEND_SPNAV_ENABLED)
list(APPEND QET_SRC_FILES
${QET_DIR}/sources/spacemouse/spnavbackend.cpp
${QET_DIR}/sources/spacemouse/spnavbackend.h
)
endif()
if(QET_SPACEMOUSE_BACKEND_HID_ENABLED)
list(APPEND QET_SRC_FILES
${QET_DIR}/sources/spacemouse/hidbackend.cpp
${QET_DIR}/sources/spacemouse/hidbackend.h
${QET_DIR}/sources/spacemouse/spacemousehid.cpp
${QET_DIR}/sources/spacemouse/spacemousehid.h
)
endif()
if(QET_SPACEMOUSE_BACKEND_CONNEXION_ENABLED)
list(APPEND QET_SRC_FILES
${QET_DIR}/sources/spacemouse/connexionbackend.cpp
${QET_DIR}/sources/spacemouse/connexionbackend.h
)
endif()
set(TS_FILES set(TS_FILES
${QET_DIR}/lang/qet_ar.ts ${QET_DIR}/lang/qet_ar.ts
${QET_DIR}/lang/qet_ca.ts ${QET_DIR}/lang/qet_ca.ts
File diff suppressed because it is too large Load Diff
-222
View File
@@ -1,222 +0,0 @@
<!DOCTYPE QPH>
<QPH sourcelanguage="fr_FR" language="ca">
<phrase>
<source>Réf. croisée</source>
<target>Ref. creuada</target>
</phrase>
<phrase>
<source>armoire</source>
<target>armari</target>
<definition>cabinet / enclosure</definition>
</phrase>
<phrase>
<source>bobine</source>
<target>bobina</target>
<definition>coil</definition>
</phrase>
<phrase>
<source>borne</source>
<target>born</target>
<definition>terminal - a connection point on a symbol</definition>
</phrase>
<phrase>
<source>bornier</source>
<target>bornes</target>
<definition>terminal strip</definition>
</phrase>
<phrase>
<source>cable</source>
<target>cable</target>
<definition>cable</definition>
</phrase>
<phrase>
<source>calibre</source>
<target>calibre</target>
<definition>rating - the current rating of a device</definition>
</phrase>
<phrase>
<source>cartouche</source>
<target>cartutx</target>
<definition>title block - the framed information panel</definition>
</phrase>
<phrase>
<source>champ de texte</source>
<target>camp de text</target>
<definition>text field</definition>
</phrase>
<phrase>
<source>collection</source>
<target>col·lecció</target>
<definition>collection - the symbol library</definition>
</phrase>
<phrase>
<source>conducteur</source>
<target>conductor</target>
<definition>conductor - the drawn wire between terminals</definition>
</phrase>
<phrase>
<source>contact</source>
<target>contacte</target>
<definition>contact</definition>
</phrase>
<phrase>
<source>contacteur</source>
<target>contactor</target>
<definition>contactor</definition>
</phrase>
<phrase>
<source>courant</source>
<target>corrent</target>
<definition>current</definition>
</phrase>
<phrase>
<source>disjoncteur</source>
<target>disjuntor</target>
<definition>circuit breaker</definition>
</phrase>
<phrase>
<source>dossier</source>
<target>carpeta</target>
<definition>folder</definition>
</phrase>
<phrase>
<source>element</source>
<target>element</target>
<definition>element - a symbol placed on a sheet</definition>
</phrase>
<phrase>
<source>esclave</source>
<target>esclau</target>
<definition>slave - the contact block belonging to a master</definition>
</phrase>
<phrase>
<source>fil</source>
<target>fil</target>
<definition>wire</definition>
</phrase>
<phrase>
<source>folio</source>
<target>plec</target>
<definition>sheet - one drawing of a set</definition>
</phrase>
<phrase>
<source>fusible</source>
<target>fusible</target>
<definition>fuse</definition>
</phrase>
<phrase>
<source>grille</source>
<target>graella</target>
<definition>grid</definition>
</phrase>
<phrase>
<source>interrupteur</source>
<target>interruptor</target>
<definition>switch</definition>
</phrase>
<phrase>
<source>maitre</source>
<target>mestre</target>
<definition>master - the element a cross-reference points from</definition>
</phrase>
<phrase>
<source>modele</source>
<target>plantilla</target>
<definition>template</definition>
</phrase>
<phrase>
<source>moteur</source>
<target>motor</target>
<definition>motor</definition>
</phrase>
<phrase>
<source>neutre</source>
<target>neutre</target>
<definition>neutral</definition>
</phrase>
<phrase>
<source>nomenclature</source>
<target>nomenclatura</target>
<definition>parts list / bill of materials</definition>
</phrase>
<phrase>
<source>numerotation</source>
<target>numeració</target>
<definition>numbering - automatic wire and part numbering</definition>
</phrase>
<phrase>
<source>phase</source>
<target>fase</target>
<definition>phase</definition>
</phrase>
<phrase>
<source>potentiel</source>
<target>potencial</target>
<definition>potential - conductors electrically joined as one node</definition>
</phrase>
<phrase>
<source>projet</source>
<target>projecte</target>
<definition>project - the .qet file holding the sheets</definition>
</phrase>
<phrase>
<source>puissance</source>
<target>potència</target>
<definition>power</definition>
</phrase>
<phrase>
<source>relais</source>
<target>relé</target>
<definition>relay</definition>
</phrase>
<phrase>
<source>renvoi de folio</source>
<target>referència de plec</target>
<definition>sheet cross-reference</definition>
</phrase>
<phrase>
<source>repere</source>
<target>referència</target>
<definition>designation - the identifying mark on a part</definition>
</phrase>
<phrase>
<source>report de folio</source>
<target>remissió de plec</target>
<definition>sheet cross-reference (the arrow symbol)</definition>
</phrase>
<phrase>
<source>schema</source>
<target>esquema</target>
<definition>diagram</definition>
</phrase>
<phrase>
<source>section</source>
<target>secció</target>
<definition>cross-section - conductor size in mm2</definition>
</phrase>
<phrase>
<source>sommaire</source>
<target>sumari</target>
<definition>summary / table of contents</definition>
</phrase>
<phrase>
<source>tension</source>
<target>tensió</target>
<definition>voltage</definition>
</phrase>
<phrase>
<source>terre</source>
<target>terra</target>
<definition>earth / ground</definition>
</phrase>
<phrase>
<source>texte</source>
<target>text</target>
<definition>text</definition>
</phrase>
<phrase>
<source>transformateur</source>
<target>transformador</target>
<definition>transformer</definition>
</phrase>
</QPH>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-4
View File
@@ -100,8 +100,4 @@
<source>traduction</source> <source>traduction</source>
<target>Übersetzung</target> <target>Übersetzung</target>
</phrase> </phrase>
<phrase>
<source>Modifier le nom de la borne</source>
<target>Klemmenname ändern</target>
</phrase>
</QPH> </QPH>
File diff suppressed because it is too large Load Diff
-918
View File
@@ -1,918 +0,0 @@
<!DOCTYPE QPH>
<QPH sourcelanguage="fr_FR" language="en">
<phrase>
<source>Blue : Blue</source>
<target>Blue : Blue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : CornflowerBlue</source>
<target>Blue : CornflowerBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : DarkBlue</source>
<target>Blue : DarkBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : DeepSkyBlue</source>
<target>Blue : DeepSkyBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : DodgerBlue</source>
<target>Blue : DodgerBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : LightBlue</source>
<target>Blue : LightBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : LightSkyBlue</source>
<target>Blue : LightSkyBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : LightSteelBlue</source>
<target>Blue : LightSteelBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : MediumBlue</source>
<target>Blue : MediumBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : MidnightBlue</source>
<target>Blue : MidnightBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : Navy</source>
<target>Blue : Navy</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : PowderBlue</source>
<target>Blue : PowderBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : RoyalBlue</source>
<target>Blue : RoyalBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : SkyBlue</source>
<target>Blue : SkyBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Blue : SteelBlue</source>
<target>Blue : SteelBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Bisque</source>
<target>Brown : Bisque</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : BlanchedAlmond</source>
<target>Brown : BlanchedAlmond</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Brown</source>
<target>Brown : Brown</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Burlywood</source>
<target>Brown : Burlywood</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Chocolate</source>
<target>Brown : Chocolate</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Cornsilk</source>
<target>Brown : Cornsilk</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : DarkGoldenrod</source>
<target>Brown : DarkGoldenrod</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Goldenrod</source>
<target>Brown : Goldenrod</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Maroon</source>
<target>Brown : Maroon</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : NavajoWhite</source>
<target>Brown : NavajoWhite</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Peru</source>
<target>Brown : Peru</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : RosyBrown</source>
<target>Brown : RosyBrown</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : SaddleBrown</source>
<target>Brown : SaddleBrown</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : SandyBrown</source>
<target>Brown : SandyBrown</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Sienna</source>
<target>Brown : Sienna</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Tan</source>
<target>Brown : Tan</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Brown : Wheat</source>
<target>Brown : Wheat</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : Aqua</source>
<target>Cyan : Aqua</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : Aquamarine</source>
<target>Cyan : Aquamarine</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : CadetBlue</source>
<target>Cyan : CadetBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : Cyan</source>
<target>Cyan : Cyan</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : DarkCyan</source>
<target>Cyan : DarkCyan</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : DarkTurquoise</source>
<target>Cyan : DarkTurquoise</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : LightCyan</source>
<target>Cyan : LightCyan</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : LightSeaGreen</source>
<target>Cyan : LightSeaGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : MediumTurquoise</source>
<target>Cyan : MediumTurquoise</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : PaleTurquoise</source>
<target>Cyan : PaleTurquoise</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : Teal</source>
<target>Cyan : Teal</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Cyan : Turquoise</source>
<target>Cyan : Turquoise</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Gray : Black</source>
<target>Gray : Black</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Gray : DarkGray</source>
<target>Gray : DarkGray</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Gray : DarkSlateGray</source>
<target>Gray : DarkSlateGray</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Gray : DimGray</source>
<target>Gray : DimGray</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Gray : Gainsboro</source>
<target>Gray : Gainsboro</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Gray : Gray</source>
<target>Gray : Gray</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Gray : LightGray</source>
<target>Gray : LightGray</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Gray : LightSlateGray</source>
<target>Gray : LightSlateGray</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Gray : Silver</source>
<target>Gray : Silver</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Gray : SlateGray</source>
<target>Gray : SlateGray</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : Chartreuse</source>
<target>Green : Chartreuse</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : DarkGreen</source>
<target>Green : DarkGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : DarkOliveGreen</source>
<target>Green : DarkOliveGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : DarkSeaGreen</source>
<target>Green : DarkSeaGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : ForestGreen</source>
<target>Green : ForestGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : Green</source>
<target>Green : Green</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : GreenYellow</source>
<target>Green : GreenYellow</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : LawnGreen</source>
<target>Green : LawnGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : LightGreen</source>
<target>Green : LightGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : Lime</source>
<target>Green : Lime</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : LimeGreen</source>
<target>Green : LimeGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : MediumAquamarine</source>
<target>Green : MediumAquamarine</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : MediumSeaGreen</source>
<target>Green : MediumSeaGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : MediumSpringGreen</source>
<target>Green : MediumSpringGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : Olive</source>
<target>Green : Olive</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : OliveDrab</source>
<target>Green : OliveDrab</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : PaleGreen</source>
<target>Green : PaleGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : SeaGreen</source>
<target>Green : SeaGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : SpringGreen</source>
<target>Green : SpringGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Green : YellowGreen</source>
<target>Green : YellowGreen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Orange : Coral</source>
<target>Orange : Coral</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Orange : DarkOrange</source>
<target>Orange : DarkOrange</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Orange : Orange</source>
<target>Orange : Orange</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Orange : OrangeRed</source>
<target>Orange : OrangeRed</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Orange : Tomato</source>
<target>Orange : Tomato</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Pink : DeepPink</source>
<target>Pink: DeepPink</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Pink : HotPink</source>
<target>Pink: HotPink</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Pink : LightPink</source>
<target>Pink: LightPink</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Pink : MediumVioletRed</source>
<target>Pink: MediumVioletRed</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Pink : PaleVioletRed</source>
<target>Pink: PaleVioletRed</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Pink : Pink</source>
<target>Pink: Pink</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : BlueViolet</source>
<target>Purple : BlueViolet</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : DarkMagenta</source>
<target>Purple : DarkMagenta</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : DarkOrchid</source>
<target>Purple : DarkOrchid</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : DarkSlateBlue</source>
<target>Purple : DarkSlateBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : DarkViolet</source>
<target>Purple : DarkViolet</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : Fuchsia</source>
<target>Purple : Fuchsia</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : Indigo</source>
<target>Purple : Indigo</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : Lavender</source>
<target>Purple : Lavender</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : Magenta</source>
<target>Purple : Magenta</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : MediumOrchid</source>
<target>Purple : MediumOrchid</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : MediumPurple</source>
<target>Purple : MediumPurple</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : MediumSlateBlue</source>
<target>Purple : MediumSlateBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : Orchid</source>
<target>Purple : Orchid</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : Plum</source>
<target>Purple : Plum</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : Purple</source>
<target>Purple : Purple</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : SlateBlue</source>
<target>Purple : SlateBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : Thistle</source>
<target>Purple : Thistle</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Purple : Violet</source>
<target>Purple : Violet</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Red : Crimson</source>
<target>Red : Crimson</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Red : DarkRed</source>
<target>Red : DarkRed</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Red : DarkSalmon</source>
<target>Red: DarkSalmon</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Red : Firebrick</source>
<target>Red : Firebrick</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Red : IndianRed</source>
<target>Red: IndianRed</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Red : LightCoral</source>
<target>Red: LightCoral</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Red : LightSalmon</source>
<target>Red: LightSalmon</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Red : Red</source>
<target>Red : Red</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Red : Salmon</source>
<target>Red: Salmon</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : AliceBlue</source>
<target>White : AliceBlue</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : AntiqueWhite</source>
<target>White : AntiqueWhite</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : Azure</source>
<target>White : Azure</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : Beige</source>
<target>White : Beige</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : FloralWhite</source>
<target>White : FloralWhite</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : GhostWhite</source>
<target>White : GhostWhite</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : Honeydew</source>
<target>White : Honeydew</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : Ivory</source>
<target>White : Ivory</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : LavenderBlush</source>
<target>White : LavenderBlush</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : Linen</source>
<target>White : Linen</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : MintCream</source>
<target>White : MintCream</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : MistyRose</source>
<target>White : MistyRose</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : OldLace</source>
<target>White : OldLace</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : Seashell</source>
<target>White : Seashell</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : Snow</source>
<target>White : Snow</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : White</source>
<target>White : White</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>White : WhiteSmoke</source>
<target>White : WhiteSmoke</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : DarkKhaki</source>
<target>Yellow : DarkKhaki</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : Gold</source>
<target>Yellow : Gold</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : Khaki</source>
<target>Yellow : Khaki</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : LemonChiffon</source>
<target>Yellow : LemonChiffon</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : LightGoldenrodYellow</source>
<target>Yellow : LightGoldenrodYellow</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : LightYellow</source>
<target>Yellow : LightYellow</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : Moccasin</source>
<target>Yellow : Moccasin</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : PaleGoldenrod</source>
<target>Yellow : PaleGoldenrod</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : PapayaWhip</source>
<target>Yellow : PapayaWhip</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : PeachPuff</source>
<target>Yellow : PeachPuff</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>Yellow : Yellow</source>
<target>Yellow : Yellow</target>
<definition>element part filling</definition>
</phrase>
<phrase>
<source>armoire</source>
<target>cabinet</target>
<definition>cabinet / enclosure</definition>
</phrase>
<phrase>
<source>bobine</source>
<target>coil</target>
<definition>coil</definition>
</phrase>
<phrase>
<source>borne</source>
<target>terminal</target>
<definition>terminal - a connection point on a symbol</definition>
</phrase>
<phrase>
<source>bornier</source>
<target>terminal strip</target>
<definition>terminal strip</definition>
</phrase>
<phrase>
<source>cable</source>
<target>cable</target>
<definition>cable</definition>
</phrase>
<phrase>
<source>calibre</source>
<target>rating</target>
<definition>rating - the current rating of a device</definition>
</phrase>
<phrase>
<source>cartouche</source>
<target>title block</target>
<definition>title block - the framed information panel</definition>
</phrase>
<phrase>
<source>champ de texte</source>
<target>text field</target>
<definition>text field</definition>
</phrase>
<phrase>
<source>collection</source>
<target>collection</target>
<definition>collection - the symbol library</definition>
</phrase>
<phrase>
<source>conducteur</source>
<target>conductor</target>
<definition>conductor - the drawn wire between terminals</definition>
</phrase>
<phrase>
<source>contact</source>
<target>contact</target>
<definition>contact</definition>
</phrase>
<phrase>
<source>contacteur</source>
<target>contactor</target>
<definition>contactor</definition>
</phrase>
<phrase>
<source>courant</source>
<target>current</target>
<definition>current</definition>
</phrase>
<phrase>
<source>disjoncteur</source>
<target>circuit breaker</target>
<definition>circuit breaker</definition>
</phrase>
<phrase>
<source>dossier</source>
<target>folder</target>
<definition>folder</definition>
</phrase>
<phrase>
<source>element</source>
<target>element</target>
<definition>element - a symbol placed on a sheet</definition>
</phrase>
<phrase>
<source>esclave</source>
<target>slave</target>
<definition>slave - the contact block belonging to a master</definition>
</phrase>
<phrase>
<source>fil</source>
<target>wire</target>
<definition>wire</definition>
</phrase>
<phrase>
<source>folio</source>
<target>sheet</target>
<definition>sheet - one drawing of a set</definition>
</phrase>
<phrase>
<source>fusible</source>
<target>fuse</target>
<definition>fuse</definition>
</phrase>
<phrase>
<source>grille</source>
<target>grid</target>
<definition>grid</definition>
</phrase>
<phrase>
<source>interrupteur</source>
<target>switch</target>
<definition>switch</definition>
</phrase>
<phrase>
<source>maitre</source>
<target>master</target>
<definition>master - the element a cross-reference points from</definition>
</phrase>
<phrase>
<source>modele</source>
<target>template</target>
<definition>template</definition>
</phrase>
<phrase>
<source>moteur</source>
<target>motor</target>
<definition>motor</definition>
</phrase>
<phrase>
<source>neutre</source>
<target>neutral</target>
<definition>neutral</definition>
</phrase>
<phrase>
<source>nomenclature</source>
<target>parts list</target>
<definition>parts list / bill of materials</definition>
</phrase>
<phrase>
<source>numerotation</source>
<target>numbering</target>
<definition>numbering - automatic wire and part numbering</definition>
</phrase>
<phrase>
<source>phase</source>
<target>phase</target>
<definition>phase</definition>
</phrase>
<phrase>
<source>potentiel</source>
<target>potential</target>
<definition>potential - conductors electrically joined as one node</definition>
</phrase>
<phrase>
<source>projet</source>
<target>project</target>
<definition>project - the .qet file holding the sheets</definition>
</phrase>
<phrase>
<source>puissance</source>
<target>power</target>
<definition>power</definition>
</phrase>
<phrase>
<source>relais</source>
<target>relay</target>
<definition>relay</definition>
</phrase>
<phrase>
<source>renvoi de folio</source>
<target>sheet cross-reference</target>
<definition>sheet cross-reference</definition>
</phrase>
<phrase>
<source>repere</source>
<target>designation</target>
<definition>designation - the identifying mark on a part</definition>
</phrase>
<phrase>
<source>report de folio</source>
<target>sheet cross-reference</target>
<definition>sheet cross-reference (the arrow symbol)</definition>
</phrase>
<phrase>
<source>schema</source>
<target>diagram</target>
<definition>diagram</definition>
</phrase>
<phrase>
<source>section</source>
<target>cross-section</target>
<definition>cross-section - conductor size in mm2</definition>
</phrase>
<phrase>
<source>sommaire</source>
<target>summary</target>
<definition>summary / table of contents</definition>
</phrase>
<phrase>
<source>tension</source>
<target>voltage</target>
<definition>voltage</definition>
</phrase>
<phrase>
<source>terre</source>
<target>earth</target>
<definition>earth / ground</definition>
</phrase>
<phrase>
<source>texte</source>
<target>text</target>
<definition>text</definition>
</phrase>
<phrase>
<source>transformateur</source>
<target>transformer</target>
<definition>transformer</definition>
</phrase>
</QPH>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-941
View File
@@ -1,941 +0,0 @@
<!DOCTYPE QPH>
<QPH sourcelanguage="fr_FR" language="nl_BE">
<phrase>
<source> Contacts : NO : %1, NC : %2, inverseurs : %3, autres : %4
</source>
<target>Contacten: NO: %1, NC: %2, wisselcontacten: %3, andere: %4
</target>
</phrase>
<phrase>
<source> Contacts : NO : %1/%2, NC : %3/%4, inverseurs : %5/%6, autres : %7/%8
</source>
<target>Contacten: NO: %1/%2, NC: %3/%4, wisselcontacten: %5/%6, andere: %7/%8
</target>
</phrase>
<phrase>
<source> %</source>
<target>%</target>
</phrase>
<phrase>
<source> (Ctrl pendant le glissement = position libre, sans accrochage à la grille)</source>
<target>(Ctrl tijdens het slepen = vrije positie, zonder vastklikken aan het raster)</target>
</phrase>
<phrase>
<source> -- %1 : mode %2</source>
<target>-- %1 : modus %2</target>
</phrase>
<phrase>
<source> ; point rouge : glisser pour repositionner le centre de rotation</source>
<target>; rood punt: slepen om het draaipunt te verplaatsen</target>
</phrase>
<phrase>
<source> ; point turquoise : arc</source>
<target>; turquoise punt: boog</target>
</phrase>
<phrase>
<source> ; un bord : inclinaison</source>
<target>; een rand: kanteling</target>
</phrase>
<phrase>
<source> °</source>
<target>°</target>
</phrase>
<phrase>
<source> — Cliquer : mode %1</source>
<target>— Klikken: modus %1</target>
</phrase>
<phrase>
<source>, Alt = créer des poignées</source>
<target>, Alt = handgrepen creëren</target>
</phrase>
<phrase>
<source>, Alt = détacher en polyligne</source>
<target>, Alt = losmaken als polylijn</target>
</phrase>
<phrase>
<source>Ajoute une courbe de Bézier sur le folio actuel</source>
<target>Voegt een Béziercurve toe op het huidige blad</target>
</phrase>
<phrase>
<source>Ajouter la borne %1</source>
<target>Klem %1 toevoegen</target>
</phrase>
<phrase>
<source>Ajouter un point à une courbe</source>
<target>Een punt aan een curve toevoegen</target>
</phrase>
<phrase>
<source>Ajouter une courbe</source>
<target>Een curve toevoegen</target>
</phrase>
<phrase>
<source>Angle</source>
<target>Hoek</target>
</phrase>
<phrase>
<source>Anguleux</source>
<target>Hoekig</target>
</phrase>
<phrase>
<source>Aperçu</source>
<target>Voorbeeld</target>
</phrase>
<phrase>
<source>Arrondir les coins d&apos;%1</source>
<target>Hoeken van %1 afronden</target>
</phrase>
<phrase>
<source>Borne 1</source>
<target>Klem 1</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Borne 2</source>
<target>Klem 2</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Catégorie</source>
<target>Categorie</target>
</phrase>
<phrase>
<source>Ce format ne prend pas en charge la transparence : l&apos;image sera enregistrée telle qu&apos;elle était avant l&apos;application de la couleur transparente. Continuer ?</source>
<target>Dit formaat ondersteunt geen transparantie: de afbeelding wordt opgeslagen zoals ze was vóór het toepassen van de transparante kleur. Doorgaan?</target>
</phrase>
<phrase>
<source>Champ de texte</source>
<target>Tekstveld</target>
</phrase>
<phrase>
<source>Clic : positionner à la taille d&apos;origine. Cliquer-glisser : positionner et redimensionner. Clic droit : pivoter de 90°. Ctrl+molette : ajuster la taille.</source>
<target>Klik: op oorspronkelijke grootte plaatsen. Klikken en slepen: plaatsen en formaat wijzigen. Rechtsklik: 90° draaien. Ctrl+muiswiel: formaat aanpassen.</target>
</phrase>
<phrase>
<source>Clic gauche : point suivant ; double-clic ou Entrée : terminer ; clic droit : annuler le dernier point</source>
<target>Linkerklik: volgend punt; dubbelklik of Enter: beëindigen; rechtsklik: laatste punt annuleren</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le coin opposé (Maj = carré, Ctrl = depuis le centre + position libre, Ctrl+Maj = carré centré) ; clic droit : annuler</source>
<target>Linkerklik: tegenoverliggende hoek plaatsen (Shift = vierkant, Ctrl = vanuit het midden + vrije positie, Ctrl+Shift = gecentreerd vierkant); rechtsklik: annuleren</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le coin opposé (Maj = cercle, Ctrl = depuis le centre + position libre, Ctrl+Maj = cercle centré) ; clic droit : annuler</source>
<target>Linkerklik: tegenoverliggende hoek plaatsen (Shift = cirkel, Ctrl = vanuit het midden + vrije positie, Ctrl+Shift = gecentreerde cirkel); rechtsklik: annuleren</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le point de départ (Ctrl = position libre)</source>
<target>Linkerklik: startpunt plaatsen (Ctrl = vrije positie)</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le point final (Ctrl = position libre) ; clic droit : annuler</source>
<target>Linkerklik: eindpunt plaatsen (Ctrl = vrije positie); rechtsklik: annuleren</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le premier coin (Ctrl = point central, position libre)</source>
<target>Linkerklik: eerste hoek plaatsen (Ctrl = middelpunt, vrije positie)</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le premier point (Ctrl = position libre)</source>
<target>Linkerklik: eerste punt plaatsen (Ctrl = vrije positie)</target>
</phrase>
<phrase>
<source>Clic: point anguleux. Cliquer-glisser: point courbe. Clic sur le premier point: fermer. Échap/Entrée: terminer. Clic droit: annuler le dernier point.</source>
<target>Klik: hoekpunt. Klikken en slepen: curvepunt. Klik op het eerste punt: sluiten. Esc/Enter: beëindigen. Rechtsklik: laatste punt annuleren.</target>
</phrase>
<phrase>
<source>Cliquer</source>
<target>Klikken</target>
</phrase>
<phrase>
<source>Cliquer : mode %1</source>
<target>Klikken: modus %1</target>
</phrase>
<phrase>
<source>Cliquez pour choisir une couleur</source>
<target>Klik om een kleur te kiezen</target>
</phrase>
<phrase>
<source>Cliquez sur l&apos;image pour ajouter une couleur. Ajustez la tolérance de chaque couleur avec son curseur, ou cliquez sur × pour la retirer.</source>
<target>Klik op de afbeelding om een kleur toe te voegen. Pas de tolerantie van elke kleur aan met de schuifregelaar, of klik op × om ze te verwijderen.</target>
</phrase>
<phrase>
<source>Cliquez sur l&apos;image pour choisir une couleur</source>
<target>Klik op de afbeelding om een kleur te kiezen</target>
</phrase>
<phrase>
<source>Coins arrondis</source>
<target>Afgeronde hoeken</target>
</phrase>
<phrase>
<source>Composant 1</source>
<target>Component 1</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Composant 2</source>
<target>Component 2</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Convertir %1 en courbe de Bézier</source>
<target>%1 omzetten naar Béziercurve</target>
</phrase>
<phrase>
<source>Convertir %1 en polyligne</source>
<target>%1 omzetten naar polylijn</target>
</phrase>
<phrase>
<source>Convertir en courbe de Bézier</source>
<target>Omzetten naar Béziercurve</target>
</phrase>
<phrase>
<source>Convertir en polyligne</source>
<target>Omzetten naar polylijn</target>
</phrase>
<phrase>
<source>Couleur transparente</source>
<target>Transparante kleur</target>
</phrase>
<phrase>
<source>Couleur transparente...</source>
<target>Transparante kleur...</target>
</phrase>
<phrase>
<source>Courant nominal</source>
<target>Nominale stroom</target>
</phrase>
<phrase>
<source>Deplacer le centre de rotation</source>
<target>Draaipunt verplaatsen</target>
</phrase>
<phrase>
<source>Description</source>
<target>Beschrijving</target>
</phrase>
<phrase>
<source>Distance in pixels between the label and the slave cross reference</source>
<target>Afstand in pixels tussen het label en de slave-kruisverwijzing</target>
</phrase>
<phrase>
<source>Distance label - slave :</source>
<target>Afstand label - slave:</target>
</phrase>
<phrase>
<source>Définir une couleur transparente</source>
<target>Een transparante kleur instellen</target>
</phrase>
<phrase>
<source>Déformer une courbe</source>
<target>Een curve vervormen</target>
</phrase>
<phrase>
<source>Déplacer le centre de rotation d&apos;une image</source>
<target>Het draaipunt van een afbeelding verplaatsen</target>
</phrase>
<phrase>
<source>Déverrouillé : largeur et hauteur peuvent être modifiées indépendamment. Cliquer pour verrouiller.</source>
<target>Ontgrendeld: breedte en hoogte kunnen onafhankelijk worden gewijzigd. Klik om te vergrendelen.</target>
</phrase>
<phrase>
<source>Enregistrer l&apos;image d&apos;origine sous...</source>
<target>Oorspronkelijke afbeelding opslaan als...</target>
</phrase>
<phrase>
<source>Enregistrer l&apos;image sous...</source>
<target>Afbeelding opslaan als...</target>
</phrase>
<phrase>
<source>Erreur</source>
<target>Fout</target>
</phrase>
<phrase>
<source>Exclure de la nomenclature</source>
<target>Uitsluiten van de stuklijst</target>
</phrase>
<phrase>
<source>Faire pivoter %1</source>
<target>%1 roteren</target>
</phrase>
<phrase>
<source>Faire pivoter une image</source>
<target>Een afbeelding draaien</target>
</phrase>
<phrase>
<source>Faites glisser les poignées, ou l&apos;intérieur du cadre, pour ajuster la zone à conserver.</source>
<target>Sleep de handgrepen, of het binnenste van het kader, om het te behouden gebied aan te passen.</target>
</phrase>
<phrase>
<source>Fil</source>
<target>Draad</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Folio</source>
<target>Blad</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Forme fermée</source>
<target>Gesloten vorm</target>
</phrase>
<phrase>
<source>Glisser %1 : rotation (Maj = 15°)</source>
<target>Sleep %1: rotatie (Shift = 15°)</target>
</phrase>
<phrase>
<source>Glisser : ajuster l&apos;arc (Ctrl = position libre, Maj = 15°)</source>
<target>Slepen: de boog aanpassen (Ctrl = vrije positie, Shift = 15°)</target>
</phrase>
<phrase>
<source>Glisser : arrondir les coins (Ctrl = position libre)</source>
<target>Slepen: hoeken afronden (Ctrl = vrije positie)</target>
</phrase>
<phrase>
<source>Glisser : déformer la courbe (Ctrl = position libre, Alt = briser la tangente)</source>
<target>Slepen: de curve vervormen (Ctrl = vrije positie, Alt = de raaklijn breken)</target>
</phrase>
<phrase>
<source>Glisser : déplacer ce point</source>
<target>Slepen: dit punt verplaatsen</target>
</phrase>
<phrase>
<source>Glisser : déplacer le centre de rotation</source>
<target>Slepen: draaipunt verplaatsen</target>
</phrase>
<phrase>
<source>Glisser : déplacer le point (Ctrl = position libre</source>
<target>Slepen: het punt verplaatsen (Ctrl = vrije positie</target>
</phrase>
<phrase>
<source>Glisser : inclinaison (Ctrl = position libre, Maj = 15°)</source>
<target>Slepen: kanteling (Ctrl = vrije positie, Shift = 15°)</target>
</phrase>
<phrase>
<source>Glisser : incliner (Maj = par pas de 15°)</source>
<target>Slepen: kantelen (Shift = stappen van 15°)</target>
</phrase>
<phrase>
<source>Glisser : pivoter (Maj = par pas de 15°)</source>
<target>Slepen: draaien (Shift = stappen van 15°)</target>
</phrase>
<phrase>
<source>Glisser : redimensionner (Ctrl = depuis le centre + position libre, Maj = proportions</source>
<target>Slepen: formaat wijzigen (Ctrl = vanuit het midden + vrije positie, Shift = verhoudingen</target>
</phrase>
<phrase>
<source>Glisser : redimensionner (Maj = conserver les proportions, Ctrl = depuis le centre)</source>
<target>Slepen: formaat wijzigen (Shift = verhoudingen behouden, Ctrl = vanuit het midden)</target>
</phrase>
<phrase>
<source>Glisser : repositionner le centre de rotation (Ctrl = position libre)</source>
<target>Slepen: het draaipunt verplaatsen (Ctrl = vrije positie)</target>
</phrase>
<phrase>
<source>Glisser : rotation (Ctrl = position libre, Maj = 15°)</source>
<target>Slepen: rotatie (Ctrl = vrije positie, Shift = 15°)</target>
</phrase>
<phrase>
<source>Glisser le point violet : arrondir les coins</source>
<target>Sleep het paarse punt: hoeken afronden</target>
</phrase>
<phrase>
<source>Glisser un coin : pivoter (Maj = par pas de 15°) ; glisser un bord : incliner (Maj = par pas de 15°) ; point rouge : déplacer le centre de rotation</source>
<target>Sleep een hoek: draaien (Shift = stappen van 15°); sleep een rand: kantelen (Shift = stappen van 15°); rood punt: draaipunt verplaatsen</target>
</phrase>
<phrase>
<source>Glisser un coin/bord : redimensionner (Ctrl = depuis le centre, Maj = conserver les proportions)</source>
<target>Sleep een hoek/rand: formaat wijzigen (Ctrl = vanuit het midden, Shift = verhoudingen behouden)</target>
</phrase>
<phrase>
<source>Glisser un coin/bord : redimensionner (Ctrl = depuis le centre, Maj = proportions, Alt = détacher en polyligne)</source>
<target>Sleep een hoek/rand: formaat wijzigen (Ctrl = vanuit het midden, Shift = verhoudingen, Alt = losmaken als polylijn)</target>
</phrase>
<phrase>
<source>Glisser un point : le déplacer</source>
<target>Sleep een punt: het verplaatsen</target>
</phrase>
<phrase>
<source>Glisser une extrémité : la déplacer</source>
<target>Sleep een uiteinde: het verplaatsen</target>
</phrase>
<phrase>
<source>Glisser une poignée ou la courbe : déformer (Alt = briser la tangente) ; Alt+glisser un point anguleux : créer des poignées ; clic droit : menu du nœud le plus proche</source>
<target>Sleep een handgreep of de curve: vervormen (Alt = de raaklijn breken); Alt+sleep een hoekpunt: handgrepen creëren; rechtsklik: menu van het dichtstbijzijnde knooppunt</target>
</phrase>
<phrase>
<source>Géométrie</source>
<target>Geometrie</target>
</phrase>
<phrase>
<source>Hauteur</source>
<target>Hoogte</target>
</phrase>
<phrase>
<source>Image BMP (*.bmp)</source>
<target>BMP-afbeelding (*.bmp)</target>
</phrase>
<phrase>
<source>Image Files (*.png *.jpg *.jpeg *.bmp *.svg)</source>
<target>Afbeeldingsbestanden (*.png *.jpg *.jpeg *.bmp *.svg)</target>
</phrase>
<phrase>
<source>Image JPEG (*.jpg *.jpeg)</source>
<target>JPEG-afbeelding (*.jpg *.jpeg)</target>
</phrase>
<phrase>
<source>Image PNG (*.png)</source>
<target>PNG-afbeelding (*.png)</target>
</phrase>
<phrase>
<source>Image source</source>
<target>Bronafbeelding</target>
</phrase>
<phrase>
<source>Image SVG (*.svg)</source>
<target>SVG-afbeelding (*.svg)</target>
</phrase>
<phrase>
<source>Images non incluses dans l&apos;export DXF</source>
<target>Afbeeldingen niet inbegrepen in de DXF-export</target>
<definition>message box title</definition>
</phrase>
<phrase>
<source>Impossible d&apos;enregistrer l&apos;image à cet emplacement.</source>
<target>Kan de afbeelding niet op die locatie opslaan.</target>
</phrase>
<phrase>
<source>Impossible d&apos;enregistrer la nomenclature dans %1.
%2</source>
<target>Kan de stuklijst niet opslaan in %1.
%2</target>
</phrase>
<phrase>
<source>Impossible de charger l&apos;image.</source>
<target>Kan de afbeelding niet laden.</target>
</phrase>
<phrase>
<source>Inclinaison X</source>
<target>Kanteling X</target>
</phrase>
<phrase>
<source>Inclinaison Y</source>
<target>Kanteling Y</target>
</phrase>
<phrase>
<source>Incliner %1</source>
<target>%1 kantelen</target>
</phrase>
<phrase>
<source>Incliner une image</source>
<target>Een afbeelding kantelen</target>
</phrase>
<phrase>
<source>La limite fixée pour cet élément maître est atteinte (Limite: %1).
Voulez-vous tout de même lier ce contact esclave ?</source>
<target>De limiet voor dit hoofdelement is bereikt (Limiet: %1).
Wilt u dit hulpcontact toch koppelen?</target>
</phrase>
<phrase>
<source>Largeur</source>
<target>Breedte</target>
</phrase>
<phrase>
<source>Le format DXF utilisé ici (AC1006) ne permet pas d&apos;inclure d&apos;image. Les images seront représentées uniquement par un rectangle de contour (position, taille, rotation et inclinaison conservées), sans le contenu de l&apos;image.</source>
<target>Het hier gebruikte DXF-formaat (AC1006) laat niet toe afbeeldingen op te nemen. Afbeeldingen worden enkel voorgesteld door een omtrekrechthoek (positie, grootte, rotatie en kanteling behouden), zonder de inhoud van de afbeelding.</target>
<definition>message box content</definition>
</phrase>
<phrase>
<source>Lisse</source>
<target>Vloeiend</target>
</phrase>
<phrase>
<source>Liste de câblage</source>
<target>Bekabelingslijst</target>
<definition>window title</definition>
</phrase>
<phrase>
<source>Liste de câblage (base de données)</source>
<target>Bekabelingslijst (database)</target>
</phrase>
<phrase>
<source>Localisation :</source>
<target>Locatie:</target>
</phrase>
<phrase>
<source>Longueur</source>
<target>Lengte</target>
</phrase>
<phrase>
<source>margin: 5px; font-weight: bold;</source>
<target>margin: 5px; font-weight: bold;</target>
</phrase>
<phrase>
<source>max:</source>
<target>max:</target>
</phrase>
<phrase>
<source>min:</source>
<target>min:</target>
</phrase>
<phrase>
<source>Miroir horizontal</source>
<target>Horizontaal spiegelen</target>
</phrase>
<phrase>
<source>Miroir horizontal d&apos;une image</source>
<target>Horizontaal spiegelen van een afbeelding</target>
</phrase>
<phrase>
<source>Miroir horizontal de %1</source>
<target>Horizontaal spiegelen van %1</target>
</phrase>
<phrase>
<source>Miroir impossible : inclinaison trop extrême pour cette forme</source>
<target>Spiegelen onmogelijk: kanteling te extreem voor deze vorm</target>
</phrase>
<phrase>
<source>Miroir vertical</source>
<target>Verticaal spiegelen</target>
</phrase>
<phrase>
<source>Miroir vertical d&apos;une image</source>
<target>Verticaal spiegelen van een afbeelding</target>
</phrase>
<phrase>
<source>Miroir vertical de %1</source>
<target>Verticaal spiegelen van %1</target>
</phrase>
<phrase>
<source>Modifier l&apos;angle d&apos;un arc</source>
<target>Hoek van een boog wijzigen</target>
</phrase>
<phrase>
<source>Modifier l&apos;angle d&apos;une forme</source>
<target>Hoek van een vorm wijzigen</target>
</phrase>
<phrase>
<source>Modifier l&apos;angle d&apos;une image</source>
<target>De hoek van een afbeelding wijzigen</target>
</phrase>
<phrase>
<source>Modifier l&apos;inclinaison d&apos;une image</source>
<target>De kanteling van een afbeelding wijzigen</target>
</phrase>
<phrase>
<source>Modifier la courbure d&apos;%1</source>
<target>Kromming van %1 wijzigen</target>
</phrase>
<phrase>
<source>Modifier la forme d&apos;%1</source>
<target>Vorm van %1 wijzigen</target>
</phrase>
<phrase>
<source>Modifier la hauteur d&apos;une image</source>
<target>De hoogte van een afbeelding wijzigen</target>
</phrase>
<phrase>
<source>Modifier la largeur d&apos;une image</source>
<target>De breedte van een afbeelding wijzigen</target>
</phrase>
<phrase>
<source>Modifier la longueur d&apos;une ligne</source>
<target>Lengte van een lijn wijzigen</target>
</phrase>
<phrase>
<source>Modifier la taille d&apos;une forme</source>
<target>Grootte van een vorm wijzigen</target>
</phrase>
<phrase>
<source>Modifier le type d&apos;un nœud</source>
<target>Het type van een knooppunt wijzigen</target>
</phrase>
<phrase>
<source>Modifier une image</source>
<target>Een afbeelding wijzigen</target>
</phrase>
<phrase>
<source>Modèle</source>
<target>Model</target>
</phrase>
<phrase>
<source>Nombre maximum de contacts esclaves définis : non défini
</source>
<target>Maximumaantal gedefinieerde hulpcontacten: niet gedefinieerd
</target>
</phrase>
<phrase>
<source>Notes</source>
<target>Notities</target>
</phrase>
<phrase>
<source>Nœud le plus proche</source>
<target>Dichtstbijzijnde knooppunt</target>
</phrase>
<phrase>
<source>pivoter/incliner</source>
<target>draaien/kantelen</target>
</phrase>
<phrase>
<source>Ponter des bornes entre-elles</source>
<target>Klemmen onderling doorverbinden</target>
</phrase>
<phrase>
<source>Rayon X</source>
<target>Straal X</target>
</phrase>
<phrase>
<source>Rayon Y</source>
<target>Straal Y</target>
</phrase>
<phrase>
<source>redimensionner</source>
<target>formaat wijzigen</target>
</phrase>
<phrase>
<source>Redimensionner %1</source>
<target>%1 vergroten/verkleinen</target>
</phrase>
<phrase>
<source>Redimensionner une image</source>
<target>Formaat van een afbeelding wijzigen</target>
</phrase>
<phrase>
<source>Remplacer l&apos;image...</source>
<target>Afbeelding vervangen...</target>
</phrase>
<phrase>
<source>Remplacer une image</source>
<target>Een afbeelding vervangen</target>
</phrase>
<phrase>
<source>Restaurer les proportions</source>
<target>Verhoudingen herstellen</target>
</phrase>
<phrase>
<source>Restaurer les proportions d&apos;une image</source>
<target>De verhoudingen van een afbeelding herstellen</target>
</phrase>
<phrase>
<source>Retirer cette couleur</source>
<target>Deze kleur verwijderen</target>
</phrase>
<phrase>
<source>Revenir à l&apos;image complète, sans rognage</source>
<target>Terug naar de volledige afbeelding, zonder bijsnijden</target>
</phrase>
<phrase>
<source>rgb(%1, %2, %3)</source>
<target>rgb(%1, %2, %3)</target>
</phrase>
<phrase>
<source>Rogner l&apos;image</source>
<target>Afbeelding bijsnijden</target>
</phrase>
<phrase>
<source>Rogner une image</source>
<target>Een afbeelding bijsnijden</target>
</phrase>
<phrase>
<source>Rogner...</source>
<target>Bijsnijden...</target>
</phrase>
<phrase>
<source>Rotation/Inclinaison</source>
<target>Rotatie/kanteling</target>
</phrase>
<phrase>
<source>Réinitialiser</source>
<target>Herinitialiseren</target>
</phrase>
<phrase>
<source>Selectionner une image...</source>
<target>Een afbeelding selecteren...</target>
</phrase>
<phrase>
<source>Supprimer le nœud le plus proche</source>
<target>Het dichtstbijzijnde knooppunt verwijderen</target>
</phrase>
<phrase>
<source>Supprimer un point d&apos;une courbe</source>
<target>Een punt van een curve verwijderen</target>
</phrase>
<phrase>
<source>Symétrique</source>
<target>Symmetrisch</target>
</phrase>
<phrase>
<source>Séparation de potentiel</source>
<target>Potentiaalscheiding</target>
</phrase>
<phrase>
<source>Taille</source>
<target>Grootte</target>
</phrase>
<phrase>
<source>Tension nominale</source>
<target>Nominale spanning</target>
</phrase>
<phrase>
<source>Tolérance pour cette couleur</source>
<target>Tolerantie voor deze kleur</target>
</phrase>
<phrase>
<source>Tous les fichiers (*)</source>
<target>Alle bestanden (*)</target>
</phrase>
<phrase>
<source>Transformer %1</source>
<target>%1 omvormen</target>
</phrase>
<phrase>
<source>Transparence non conservée</source>
<target>Transparantie niet behouden</target>
</phrase>
<phrase>
<source>un arc</source>
<target>een boog</target>
</phrase>
<phrase>
<source>un coin</source>
<target>een hoek</target>
</phrase>
<phrase>
<source>un point</source>
<target>een punt</target>
</phrase>
<phrase>
<source>une courbe</source>
<target>een curve</target>
</phrase>
<phrase>
<source>une extrémité</source>
<target>een uiteinde</target>
</phrase>
<phrase>
<source>une image</source>
<target>een afbeelding</target>
<definition>part of a sentence listing the content of a diagram</definition>
</phrase>
<phrase>
<source>Verrouiller la numérotation automatique</source>
<target>Automatische nummering vergrendelen</target>
</phrase>
<phrase>
<source>Verrouiller la position</source>
<target>Positie vergrendelen</target>
</phrase>
<phrase>
<source>Verrouillé : modifier la largeur ou la hauteur ajuste l&apos;autre pour conserver les proportions. Cliquer pour déverrouiller.</source>
<target>Vergrendeld: het wijzigen van de breedte of hoogte past de andere aan om de verhoudingen te behouden. Klik om te ontgrendelen.</target>
</phrase>
<phrase>
<source>Échec de l&apos;enregistrement</source>
<target>Opslaan mislukt</target>
</phrase>
<phrase>
<source>Édition des nœuds</source>
<target>Knooppunten bewerken</target>
</phrase>
<phrase>
<source>armoire</source>
<target>kast</target>
<definition>cabinet / enclosure</definition>
</phrase>
<phrase>
<source>bobine</source>
<target>spoel</target>
<definition>coil</definition>
</phrase>
<phrase>
<source>borne</source>
<target>klem</target>
<definition>terminal - a connection point on a symbol</definition>
</phrase>
<phrase>
<source>bornier</source>
<target>klemmenstrook</target>
<definition>terminal strip</definition>
</phrase>
<phrase>
<source>cable</source>
<target>kabel</target>
<definition>cable</definition>
</phrase>
<phrase>
<source>calibre</source>
<target>kaliber</target>
<definition>rating - the current rating of a device</definition>
</phrase>
<phrase>
<source>cartouche</source>
<target>titelblok</target>
<definition>title block - the framed information panel</definition>
</phrase>
<phrase>
<source>collection</source>
<target>collectie</target>
<definition>collection - the symbol library</definition>
</phrase>
<phrase>
<source>conducteur</source>
<target>geleider</target>
<definition>conductor - the drawn wire between terminals</definition>
</phrase>
<phrase>
<source>contact</source>
<target>contact</target>
<definition>contact</definition>
</phrase>
<phrase>
<source>contacteur</source>
<target>contacteur</target>
<definition>contactor</definition>
</phrase>
<phrase>
<source>courant</source>
<target>stroom</target>
<definition>current</definition>
</phrase>
<phrase>
<source>disjoncteur</source>
<target>automaat</target>
<definition>circuit breaker</definition>
</phrase>
<phrase>
<source>dossier</source>
<target>map</target>
<definition>folder</definition>
</phrase>
<phrase>
<source>element</source>
<target>element</target>
<definition>element - a symbol placed on a sheet</definition>
</phrase>
<phrase>
<source>esclave</source>
<target>hulpcontact</target>
<definition>slave - the contact block belonging to a master</definition>
</phrase>
<phrase>
<source>fusible</source>
<target>zekering</target>
<definition>fuse</definition>
</phrase>
<phrase>
<source>grille</source>
<target>raster</target>
<definition>grid</definition>
</phrase>
<phrase>
<source>interrupteur</source>
<target>schakelaar</target>
<definition>switch</definition>
</phrase>
<phrase>
<source>maitre</source>
<target>hoofd</target>
<definition>master - the element a cross-reference points from</definition>
</phrase>
<phrase>
<source>modele</source>
<target>sjabloon</target>
<definition>template</definition>
</phrase>
<phrase>
<source>moteur</source>
<target>motor</target>
<definition>motor</definition>
</phrase>
<phrase>
<source>neutre</source>
<target>nul</target>
<definition>neutral</definition>
</phrase>
<phrase>
<source>nomenclature</source>
<target>stuklijst</target>
<definition>parts list / bill of materials</definition>
</phrase>
<phrase>
<source>numerotation</source>
<target>nummering</target>
<definition>numbering - automatic wire and part numbering</definition>
</phrase>
<phrase>
<source>phase</source>
<target>fase</target>
<definition>phase</definition>
</phrase>
<phrase>
<source>potentiel</source>
<target>potentiaal</target>
<definition>potential - conductors electrically joined as one node</definition>
</phrase>
<phrase>
<source>projet</source>
<target>project</target>
<definition>project - the .qet file holding the sheets</definition>
</phrase>
<phrase>
<source>puissance</source>
<target>vermogen</target>
<definition>power</definition>
</phrase>
<phrase>
<source>relais</source>
<target>relais</target>
<definition>relay</definition>
</phrase>
<phrase>
<source>renvoi de folio</source>
<target>bladverwijzing</target>
<definition>sheet cross-reference</definition>
</phrase>
<phrase>
<source>repere</source>
<target>aanduiding</target>
<definition>designation - the identifying mark on a part</definition>
</phrase>
<phrase>
<source>report de folio</source>
<target>bladverwijzing</target>
<definition>sheet cross-reference (the arrow symbol)</definition>
</phrase>
<phrase>
<source>schema</source>
<target>schema</target>
<definition>diagram</definition>
</phrase>
<phrase>
<source>section</source>
<target>doorsnede</target>
<definition>cross-section - conductor size in mm2</definition>
</phrase>
<phrase>
<source>sommaire</source>
<target>inhoudstafel</target>
<definition>summary / table of contents</definition>
</phrase>
<phrase>
<source>tension</source>
<target>spanning</target>
<definition>voltage</definition>
</phrase>
<phrase>
<source>terre</source>
<target>aarde</target>
<definition>earth / ground</definition>
</phrase>
<phrase>
<source>texte</source>
<target>tekst</target>
<definition>text</definition>
</phrase>
<phrase>
<source>transformateur</source>
<target>transformator</target>
<definition>transformer</definition>
</phrase>
</QPH>
-218
View File
@@ -1,218 +0,0 @@
<!DOCTYPE QPH>
<QPH sourcelanguage="fr_FR" language="pl">
<phrase>
<source>armoire</source>
<target>Szafa</target>
<definition>cabinet / enclosure</definition>
</phrase>
<phrase>
<source>bobine</source>
<target>Cewka</target>
<definition>coil</definition>
</phrase>
<phrase>
<source>borne</source>
<target>Zacisk</target>
<definition>terminal - a connection point on a symbol</definition>
</phrase>
<phrase>
<source>bornier</source>
<target>Listwa zaciskowa</target>
<definition>terminal strip</definition>
</phrase>
<phrase>
<source>cable</source>
<target>Kabel</target>
<definition>cable</definition>
</phrase>
<phrase>
<source>calibre</source>
<target>Prąd znamionowy</target>
<definition>rating - the current rating of a device</definition>
</phrase>
<phrase>
<source>cartouche</source>
<target>Tabliczka rysunkowa</target>
<definition>title block - the framed information panel</definition>
</phrase>
<phrase>
<source>champ de texte</source>
<target>Pole tekstowe</target>
<definition>text field</definition>
</phrase>
<phrase>
<source>collection</source>
<target>Biblioteka</target>
<definition>collection - the symbol library</definition>
</phrase>
<phrase>
<source>conducteur</source>
<target>Przewód</target>
<definition>conductor - the drawn wire between terminals</definition>
</phrase>
<phrase>
<source>contact</source>
<target>Styk</target>
<definition>contact</definition>
</phrase>
<phrase>
<source>contacteur</source>
<target>Stycznik</target>
<definition>contactor</definition>
</phrase>
<phrase>
<source>courant</source>
<target>Prąd</target>
<definition>current</definition>
</phrase>
<phrase>
<source>disjoncteur</source>
<target>Wyłącznik nadprądowy</target>
<definition>circuit breaker</definition>
</phrase>
<phrase>
<source>dossier</source>
<target>Folder</target>
<definition>folder</definition>
</phrase>
<phrase>
<source>element</source>
<target>Element</target>
<definition>element - a symbol placed on a sheet</definition>
</phrase>
<phrase>
<source>esclave</source>
<target>Element podrzędny</target>
<definition>slave - the contact block belonging to a master</definition>
</phrase>
<phrase>
<source>fil</source>
<target>Przewód</target>
<definition>wire</definition>
</phrase>
<phrase>
<source>folio</source>
<target>Arkusz</target>
<definition>sheet - one drawing of a set</definition>
</phrase>
<phrase>
<source>fusible</source>
<target>Bezpiecznik</target>
<definition>fuse</definition>
</phrase>
<phrase>
<source>grille</source>
<target>Siatka</target>
<definition>grid</definition>
</phrase>
<phrase>
<source>interrupteur</source>
<target>Wyłącznik</target>
<definition>switch</definition>
</phrase>
<phrase>
<source>maitre</source>
<target>Element główny</target>
<definition>master - the element a cross-reference points from</definition>
</phrase>
<phrase>
<source>modele</source>
<target>Szablon</target>
<definition>template</definition>
</phrase>
<phrase>
<source>moteur</source>
<target>Silnik</target>
<definition>motor</definition>
</phrase>
<phrase>
<source>neutre</source>
<target>Neutralny</target>
<definition>neutral</definition>
</phrase>
<phrase>
<source>nomenclature</source>
<target>Zestawienie materiałów</target>
<definition>parts list / bill of materials</definition>
</phrase>
<phrase>
<source>numerotation</source>
<target>Numeracja</target>
<definition>numbering - automatic wire and part numbering</definition>
</phrase>
<phrase>
<source>phase</source>
<target>Faza</target>
<definition>phase</definition>
</phrase>
<phrase>
<source>potentiel</source>
<target>Potencjał</target>
<definition>potential - conductors electrically joined as one node</definition>
</phrase>
<phrase>
<source>projet</source>
<target>Projekt</target>
<definition>project - the .qet file holding the sheets</definition>
</phrase>
<phrase>
<source>puissance</source>
<target>Moc</target>
<definition>power</definition>
</phrase>
<phrase>
<source>relais</source>
<target>Przekaźnik</target>
<definition>relay</definition>
</phrase>
<phrase>
<source>renvoi de folio</source>
<target>Odnośnik do arkusza</target>
<definition>sheet cross-reference</definition>
</phrase>
<phrase>
<source>repere</source>
<target>Oznaczenie</target>
<definition>designation - the identifying mark on a part</definition>
</phrase>
<phrase>
<source>report de folio</source>
<target>Odnośnik do arkusza</target>
<definition>sheet cross-reference (the arrow symbol)</definition>
</phrase>
<phrase>
<source>schema</source>
<target>Schemat</target>
<definition>diagram</definition>
</phrase>
<phrase>
<source>section</source>
<target>Przekrój</target>
<definition>cross-section - conductor size in mm2</definition>
</phrase>
<phrase>
<source>sommaire</source>
<target>Spis treści</target>
<definition>summary / table of contents</definition>
</phrase>
<phrase>
<source>tension</source>
<target>Napięcie</target>
<definition>voltage</definition>
</phrase>
<phrase>
<source>terre</source>
<target>Uziemienie</target>
<definition>earth / ground</definition>
</phrase>
<phrase>
<source>texte</source>
<target>Tekst</target>
<definition>text</definition>
</phrase>
<phrase>
<source>transformateur</source>
<target>Transformator</target>
<definition>transformer</definition>
</phrase>
</QPH>
File diff suppressed because it is too large Load Diff
-465
View File
@@ -1,465 +0,0 @@
<!DOCTYPE QPH>
<QPH sourcelanguage="fr_FR" language="pt_BR">
<phrase>
<source> Contacts : NO : %1, NC : %2, inverseurs : %3, autres : %4
</source>
<target>Contatos: NA: %1, NF: %2, reversíveis: %3, outros: %4
</target>
</phrase>
<phrase>
<source> Contacts : NO : %1/%2, NC : %3/%4, inverseurs : %5/%6, autres : %7/%8
</source>
<target>Contatos: NA: %1/%2, NF: %3/%4, reversíveis: %5/%6, outros: %7/%8
</target>
</phrase>
<phrase>
<source>Aperçu</source>
<target>Pré-visualização</target>
</phrase>
<phrase>
<source>Arrondir les coins d&apos;%1</source>
<target>Arredondar os cantos de %1</target>
</phrase>
<phrase>
<source>Borne 1</source>
<target>Borne 1</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Borne 2</source>
<target>Borne 2</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Catégorie</source>
<target>Categoria</target>
</phrase>
<phrase>
<source>Ce format ne prend pas en charge la transparence : l&apos;image sera enregistrée telle qu&apos;elle était avant l&apos;application de la couleur transparente. Continuer ?</source>
<target>Este formato não suporta transparência: a imagem será salva como estava antes da aplicação da cor transparente. Continuar?</target>
</phrase>
<phrase>
<source>Champ de texte</source>
<target>Campo de texto</target>
</phrase>
<phrase>
<source>Composant 1</source>
<target>Componente 1</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Composant 2</source>
<target>Componente 2</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Convertir %1 en courbe de Bézier</source>
<target>Converter %1 em curva de Bézier</target>
</phrase>
<phrase>
<source>Convertir en courbe de Bézier</source>
<target>Converter em curva de Bézier</target>
</phrase>
<phrase>
<source>Courant nominal</source>
<target>Corrente nominal</target>
</phrase>
<phrase>
<source>Distance in pixels between the label and the slave cross reference</source>
<target>Distância em pixels entre o rótulo e a referência cruzada do escravo</target>
</phrase>
<phrase>
<source>Distance label - slave :</source>
<target>Distância do rótulo - escravo:</target>
</phrase>
<phrase>
<source>Enregistrer l&apos;image d&apos;origine sous...</source>
<target>Salvar a imagem original como...</target>
</phrase>
<phrase>
<source>Enregistrer l&apos;image sous...</source>
<target>Salvar a imagem como...</target>
</phrase>
<phrase>
<source>Erreur</source>
<target>Erro</target>
</phrase>
<phrase>
<source>Exclure de la nomenclature</source>
<target>Excluir da lista de materiais</target>
</phrase>
<phrase>
<source>Faire pivoter %1</source>
<target>Girar %1</target>
</phrase>
<phrase>
<source>Faire pivoter une image</source>
<target>Girar uma imagem</target>
</phrase>
<phrase>
<source>Fil</source>
<target>Fio</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Folio</source>
<target>Folha</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Hauteur</source>
<target>Altura</target>
</phrase>
<phrase>
<source>Image BMP (*.bmp)</source>
<target>Imagem BMP (*.bmp)</target>
</phrase>
<phrase>
<source>Image JPEG (*.jpg *.jpeg)</source>
<target>Imagem JPEG (*.jpg *.jpeg)</target>
</phrase>
<phrase>
<source>Image PNG (*.png)</source>
<target>Imagem PNG (*.png)</target>
</phrase>
<phrase>
<source>Image SVG (*.svg)</source>
<target>Imagem SVG (*.svg)</target>
</phrase>
<phrase>
<source>Images non incluses dans l&apos;export DXF</source>
<target>Imagens não incluídas na exportação DXF</target>
<definition>message box title</definition>
</phrase>
<phrase>
<source>Impossible d&apos;enregistrer l&apos;image à cet emplacement.</source>
<target>Não foi possível salvar a imagem neste local.</target>
</phrase>
<phrase>
<source>Impossible d&apos;enregistrer la nomenclature dans %1.
%2</source>
<target>Não foi possível salvar a lista de materiais em %1.
%2</target>
</phrase>
<phrase>
<source>Impossible de charger l&apos;image.</source>
<target>Não foi possível carregar a imagem.</target>
</phrase>
<phrase>
<source>Incliner %1</source>
<target>Inclinar %1</target>
</phrase>
<phrase>
<source>Incliner une image</source>
<target>Inclinar uma imagem</target>
</phrase>
<phrase>
<source>La limite fixée pour cet élément maître est atteinte (Limite: %1).
Voulez-vous tout de même lier ce contact esclave ?</source>
<target>O limite definido para este elemento mestre foi atingido (Limite: %1).
Deseja vincular este contato escravo mesmo assim?</target>
</phrase>
<phrase>
<source>Largeur</source>
<target>Largura</target>
</phrase>
<phrase>
<source>Le format DXF utilisé ici (AC1006) ne permet pas d&apos;inclure d&apos;image. Les images seront représentées uniquement par un rectangle de contour (position, taille, rotation et inclinaison conservées), sans le contenu de l&apos;image.</source>
<target>O formato DXF usado aqui (AC1006) não permite incluir imagens. As imagens serão representadas apenas por um retângulo de contorno (posição, tamanho, rotação e inclinação preservados), sem o conteúdo da imagem.</target>
<definition>message box content</definition>
</phrase>
<phrase>
<source>Liste de câblage</source>
<target>Lista de cabeamento</target>
<definition>window title</definition>
</phrase>
<phrase>
<source>Liste de câblage (base de données)</source>
<target>Lista de cabeamento (banco de dados)</target>
</phrase>
<phrase>
<source>margin: 5px; font-weight: bold;</source>
<target>margin: 5px; font-weight: bold;</target>
</phrase>
<phrase>
<source>Modifier l&apos;angle d&apos;un arc</source>
<target>Modificar o ângulo de um arco</target>
</phrase>
<phrase>
<source>Modifier la courbure d&apos;%1</source>
<target>Modificar a curvatura de %1</target>
</phrase>
<phrase>
<source>Modifier la forme d&apos;%1</source>
<target>Modificar a forma de %1</target>
</phrase>
<phrase>
<source>Modèle</source>
<target>Modelo</target>
</phrase>
<phrase>
<source>Nombre maximum de contacts esclaves définis : non défini
</source>
<target>Número máximo de contatos escravos definidos: não definido
</target>
</phrase>
<phrase>
<source>Notes</source>
<target>Observações</target>
</phrase>
<phrase>
<source>Redimensionner %1</source>
<target>Redimensionar %1</target>
</phrase>
<phrase>
<source>Redimensionner une image</source>
<target>Redimensionar uma imagem</target>
</phrase>
<phrase>
<source>Réinitialiser</source>
<target>Redefinir</target>
</phrase>
<phrase>
<source>Selectionner une image...</source>
<target>Selecionar uma imagem...</target>
</phrase>
<phrase>
<source>Séparation de potentiel</source>
<target>Separação de potencial</target>
</phrase>
<phrase>
<source>Taille</source>
<target>Tamanho</target>
</phrase>
<phrase>
<source>Tension nominale</source>
<target>Tensão nominal</target>
</phrase>
<phrase>
<source>Tous les fichiers (*)</source>
<target>Todos os arquivos (*)</target>
</phrase>
<phrase>
<source>Transparence non conservée</source>
<target>Transparência não preservada</target>
</phrase>
<phrase>
<source>une image</source>
<target>uma imagem</target>
<definition>part of a sentence listing the content of a diagram</definition>
</phrase>
<phrase>
<source>Verrouiller la numérotation automatique</source>
<target>Bloquear a numeração automática</target>
</phrase>
<phrase>
<source>Verrouiller la position</source>
<target>Bloquear a posição</target>
</phrase>
<phrase>
<source>Échec de l&apos;enregistrement</source>
<target>Falha ao salvar</target>
</phrase>
<phrase>
<source>armoire</source>
<target>armário</target>
<definition>cabinet / enclosure</definition>
</phrase>
<phrase>
<source>bobine</source>
<target>bobina</target>
<definition>coil</definition>
</phrase>
<phrase>
<source>borne</source>
<target>borne</target>
<definition>terminal - a connection point on a symbol</definition>
</phrase>
<phrase>
<source>bornier</source>
<target>borneira</target>
<definition>terminal strip</definition>
</phrase>
<phrase>
<source>cable</source>
<target>cabo</target>
<definition>cable</definition>
</phrase>
<phrase>
<source>calibre</source>
<target>calibre</target>
<definition>rating - the current rating of a device</definition>
</phrase>
<phrase>
<source>cartouche</source>
<target>bloco de título</target>
<definition>title block - the framed information panel</definition>
</phrase>
<phrase>
<source>collection</source>
<target>coleção</target>
<definition>collection - the symbol library</definition>
</phrase>
<phrase>
<source>conducteur</source>
<target>condutor</target>
<definition>conductor - the drawn wire between terminals</definition>
</phrase>
<phrase>
<source>contact</source>
<target>contato</target>
<definition>contact</definition>
</phrase>
<phrase>
<source>contacteur</source>
<target>contator</target>
<definition>contactor</definition>
</phrase>
<phrase>
<source>courant</source>
<target>corrente</target>
<definition>current</definition>
</phrase>
<phrase>
<source>disjoncteur</source>
<target>disjuntor</target>
<definition>circuit breaker</definition>
</phrase>
<phrase>
<source>dossier</source>
<target>pasta</target>
<definition>folder</definition>
</phrase>
<phrase>
<source>element</source>
<target>elemento</target>
<definition>element - a symbol placed on a sheet</definition>
</phrase>
<phrase>
<source>esclave</source>
<target>escravo</target>
<definition>slave - the contact block belonging to a master</definition>
</phrase>
<phrase>
<source>fusible</source>
<target>fusível</target>
<definition>fuse</definition>
</phrase>
<phrase>
<source>grille</source>
<target>grade</target>
<definition>grid</definition>
</phrase>
<phrase>
<source>interrupteur</source>
<target>interruptor</target>
<definition>switch</definition>
</phrase>
<phrase>
<source>maitre</source>
<target>mestre</target>
<definition>master - the element a cross-reference points from</definition>
</phrase>
<phrase>
<source>modele</source>
<target>modelo</target>
<definition>template</definition>
</phrase>
<phrase>
<source>moteur</source>
<target>motor</target>
<definition>motor</definition>
</phrase>
<phrase>
<source>neutre</source>
<target>neutro</target>
<definition>neutral</definition>
</phrase>
<phrase>
<source>nomenclature</source>
<target>lista de materiais</target>
<definition>parts list / bill of materials</definition>
</phrase>
<phrase>
<source>numerotation</source>
<target>numeração</target>
<definition>numbering - automatic wire and part numbering</definition>
</phrase>
<phrase>
<source>phase</source>
<target>fase</target>
<definition>phase</definition>
</phrase>
<phrase>
<source>potentiel</source>
<target>potencial</target>
<definition>potential - conductors electrically joined as one node</definition>
</phrase>
<phrase>
<source>projet</source>
<target>projeto</target>
<definition>project - the .qet file holding the sheets</definition>
</phrase>
<phrase>
<source>puissance</source>
<target>potência</target>
<definition>power</definition>
</phrase>
<phrase>
<source>relais</source>
<target>relé</target>
<definition>relay</definition>
</phrase>
<phrase>
<source>renvoi de folio</source>
<target>referência de folha</target>
<definition>sheet cross-reference</definition>
</phrase>
<phrase>
<source>repere</source>
<target>designação</target>
<definition>designation - the identifying mark on a part</definition>
</phrase>
<phrase>
<source>report de folio</source>
<target>remissão de folha</target>
<definition>sheet cross-reference (the arrow symbol)</definition>
</phrase>
<phrase>
<source>schema</source>
<target>diagrama</target>
<definition>diagram</definition>
</phrase>
<phrase>
<source>section</source>
<target>seção</target>
<definition>cross-section - conductor size in mm2</definition>
</phrase>
<phrase>
<source>sommaire</source>
<target>sumário</target>
<definition>summary / table of contents</definition>
</phrase>
<phrase>
<source>tension</source>
<target>tensão</target>
<definition>voltage</definition>
</phrase>
<phrase>
<source>terre</source>
<target>terra</target>
<definition>earth / ground</definition>
</phrase>
<phrase>
<source>texte</source>
<target>texto</target>
<definition>text</definition>
</phrase>
<phrase>
<source>transformateur</source>
<target>transformador</target>
<definition>transformer</definition>
</phrase>
</QPH>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-917
View File
@@ -1,917 +0,0 @@
<!DOCTYPE QPH>
<QPH sourcelanguage="fr_FR" language="zh">
<phrase>
<source> Contacts : NO : %1, NC : %2, inverseurs : %3, autres : %4
</source>
<target>触点:常开:%1,常闭:%2,转换:%3,其他:%4
</target>
</phrase>
<phrase>
<source> Contacts : NO : %1/%2, NC : %3/%4, inverseurs : %5/%6, autres : %7/%8
</source>
<target>触点:常开:%1/%2,常闭:%3/%4,转换:%5/%6,其他:%7/%8
</target>
</phrase>
<phrase>
<source> %</source>
<target>%</target>
</phrase>
<phrase>
<source> (Ctrl pendant le glissement = position libre, sans accrochage à la grille)</source>
<target>(拖动时按 Ctrl = 自由位置,不吸附到网格)</target>
</phrase>
<phrase>
<source> -- %1 : mode %2</source>
<target>-- %1:%2 模式</target>
</phrase>
<phrase>
<source> ; point rouge : glisser pour repositionner le centre de rotation</source>
<target>;红点:拖动以重新定位旋转中心</target>
</phrase>
<phrase>
<source> ; point turquoise : arc</source>
<target>;青色点:圆弧</target>
</phrase>
<phrase>
<source> ; un bord : inclinaison</source>
<target>;边:倾斜</target>
</phrase>
<phrase>
<source> °</source>
<target>°</target>
</phrase>
<phrase>
<source> — Cliquer : mode %1</source>
<target>— 单击:%1 模式</target>
</phrase>
<phrase>
<source>, Alt = créer des poignées</source>
<target>,Alt = 创建控制柄</target>
</phrase>
<phrase>
<source>, Alt = détacher en polyligne</source>
<target>,Alt = 分离为多段线</target>
</phrase>
<phrase>
<source>Ajoute une courbe de Bézier sur le folio actuel</source>
<target>在当前图纸上添加贝塞尔曲线</target>
</phrase>
<phrase>
<source>Ajouter un point à une courbe</source>
<target>向曲线添加点</target>
</phrase>
<phrase>
<source>Ajouter une courbe</source>
<target>添加曲线</target>
</phrase>
<phrase>
<source>Angle</source>
<target>角度</target>
</phrase>
<phrase>
<source>Anguleux</source>
<target>角点</target>
</phrase>
<phrase>
<source>Aperçu</source>
<target>预览</target>
</phrase>
<phrase>
<source>Arrondir les coins d&apos;%1</source>
<target>将%1的角变圆</target>
</phrase>
<phrase>
<source>Borne 1</source>
<target>端子1</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Borne 2</source>
<target>端子2</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Catégorie</source>
<target>类别</target>
</phrase>
<phrase>
<source>Ce format ne prend pas en charge la transparence : l&apos;image sera enregistrée telle qu&apos;elle était avant l&apos;application de la couleur transparente. Continuer ?</source>
<target>此格式不支持透明度:图像将按应用透明色之前的状态保存。是否继续?</target>
</phrase>
<phrase>
<source>Champ de texte</source>
<target>文本字段</target>
</phrase>
<phrase>
<source>Clic : positionner à la taille d&apos;origine. Cliquer-glisser : positionner et redimensionner. Clic droit : pivoter de 90°. Ctrl+molette : ajuster la taille.</source>
<target>单击:按原始尺寸放置。单击拖动:放置并调整大小。右键:旋转 90°。Ctrl+滚轮:调整大小。</target>
</phrase>
<phrase>
<source>Clic gauche : point suivant ; double-clic ou Entrée : terminer ; clic droit : annuler le dernier point</source>
<target>左键单击:下一个点;双击或 Enter:结束;右键单击:撤销上一个点</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le coin opposé (Maj = carré, Ctrl = depuis le centre + position libre, Ctrl+Maj = carré centré) ; clic droit : annuler</source>
<target>左键单击:放置对角(Shift = 正方形,Ctrl = 从中心 + 自由位置,Ctrl+Shift = 居中正方形);右键单击:取消</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le coin opposé (Maj = cercle, Ctrl = depuis le centre + position libre, Ctrl+Maj = cercle centré) ; clic droit : annuler</source>
<target>左键单击:放置对角(Shift = 圆形,Ctrl = 从中心 + 自由位置,Ctrl+Shift = 居中圆形);右键单击:取消</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le point de départ (Ctrl = position libre)</source>
<target>左键单击:放置起点(Ctrl = 自由位置)</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le point final (Ctrl = position libre) ; clic droit : annuler</source>
<target>左键单击:放置终点(Ctrl = 自由位置);右键单击:取消</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le premier coin (Ctrl = point central, position libre)</source>
<target>左键单击:放置第一个角(Ctrl = 中心点,自由位置)</target>
</phrase>
<phrase>
<source>Clic gauche : positionner le premier point (Ctrl = position libre)</source>
<target>左键单击:放置第一个点(Ctrl = 自由位置)</target>
</phrase>
<phrase>
<source>Clic: point anguleux. Cliquer-glisser: point courbe. Clic sur le premier point: fermer. Échap/Entrée: terminer. Clic droit: annuler le dernier point.</source>
<target>单击:角点。单击拖动:曲线点。单击第一个点:闭合。Esc/Enter:结束。右键:撤销上一个点。</target>
</phrase>
<phrase>
<source>Cliquer</source>
<target>单击</target>
</phrase>
<phrase>
<source>Cliquer : mode %1</source>
<target>单击:%1 模式</target>
</phrase>
<phrase>
<source>Cliquez pour choisir une couleur</source>
<target>点击选择颜色</target>
</phrase>
<phrase>
<source>Cliquez sur l&apos;image pour ajouter une couleur. Ajustez la tolérance de chaque couleur avec son curseur, ou cliquez sur × pour la retirer.</source>
<target>单击图像以添加颜色。使用滑块调整每种颜色的容差,或单击 × 将其移除。</target>
</phrase>
<phrase>
<source>Cliquez sur l&apos;image pour choisir une couleur</source>
<target>单击图像选择颜色</target>
</phrase>
<phrase>
<source>Coins arrondis</source>
<target>圆角</target>
</phrase>
<phrase>
<source>Composant 1</source>
<target>元件1</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Composant 2</source>
<target>元件2</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Convertir %1 en courbe de Bézier</source>
<target>将 %1 转换为贝塞尔曲线</target>
</phrase>
<phrase>
<source>Convertir %1 en polyligne</source>
<target>将 %1 转换为多段线</target>
</phrase>
<phrase>
<source>Convertir en courbe de Bézier</source>
<target>转换为贝塞尔曲线</target>
</phrase>
<phrase>
<source>Convertir en polyligne</source>
<target>转换为多段线</target>
</phrase>
<phrase>
<source>Couleur transparente</source>
<target>透明色</target>
</phrase>
<phrase>
<source>Couleur transparente...</source>
<target>透明色...</target>
</phrase>
<phrase>
<source>Courant nominal</source>
<target>额定电流</target>
</phrase>
<phrase>
<source>Deplacer le centre de rotation</source>
<target>移动旋转中心</target>
</phrase>
<phrase>
<source>Distance in pixels between the label and the slave cross reference</source>
<target>标签与从站交叉引用之间的像素距离</target>
</phrase>
<phrase>
<source>Distance label - slave :</source>
<target>标签距离 - 从站:</target>
</phrase>
<phrase>
<source>Définir une couleur transparente</source>
<target>设置透明色</target>
</phrase>
<phrase>
<source>Déformer une courbe</source>
<target>变形曲线</target>
</phrase>
<phrase>
<source>Déplacer le centre de rotation d&apos;une image</source>
<target>移动图像的旋转中心</target>
</phrase>
<phrase>
<source>Déverrouillé : largeur et hauteur peuvent être modifiées indépendamment. Cliquer pour verrouiller.</source>
<target>已解锁:宽度和高度可独立修改。单击锁定。</target>
</phrase>
<phrase>
<source>Enregistrer l&apos;image d&apos;origine sous...</source>
<target>原始图像另存为...</target>
</phrase>
<phrase>
<source>Enregistrer l&apos;image sous...</source>
<target>图像另存为...</target>
</phrase>
<phrase>
<source>Erreur</source>
<target>错误</target>
</phrase>
<phrase>
<source>Exclure de la nomenclature</source>
<target>从物料清单中排除</target>
</phrase>
<phrase>
<source>Faire pivoter %1</source>
<target>旋转 %1</target>
</phrase>
<phrase>
<source>Faire pivoter une image</source>
<target>旋转图像</target>
</phrase>
<phrase>
<source>Faites glisser les poignées, ou l&apos;intérieur du cadre, pour ajuster la zone à conserver.</source>
<target>拖动控制柄或框内区域,调整要保留的区域。</target>
</phrase>
<phrase>
<source>Fil</source>
<target>导线</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Folio</source>
<target>图页</target>
<definition>column title</definition>
</phrase>
<phrase>
<source>Forme fermée</source>
<target>闭合形状</target>
</phrase>
<phrase>
<source>Glisser %1 : rotation (Maj = 15°)</source>
<target>拖动 %1:旋转(Shift = 15°)</target>
</phrase>
<phrase>
<source>Glisser : ajuster l&apos;arc (Ctrl = position libre, Maj = 15°)</source>
<target>拖动:调整圆弧(Ctrl = 自由位置,Shift = 15°)</target>
</phrase>
<phrase>
<source>Glisser : arrondir les coins (Ctrl = position libre)</source>
<target>拖动:圆角(Ctrl = 自由位置)</target>
</phrase>
<phrase>
<source>Glisser : déformer la courbe (Ctrl = position libre, Alt = briser la tangente)</source>
<target>拖动:变形曲线(Ctrl = 自由位置,Alt = 断开切线)</target>
</phrase>
<phrase>
<source>Glisser : déplacer ce point</source>
<target>拖动:移动此点</target>
</phrase>
<phrase>
<source>Glisser : déplacer le centre de rotation</source>
<target>拖动:移动旋转中心</target>
</phrase>
<phrase>
<source>Glisser : déplacer le point (Ctrl = position libre</source>
<target>拖动:移动点(Ctrl = 自由位置</target>
</phrase>
<phrase>
<source>Glisser : inclinaison (Ctrl = position libre, Maj = 15°)</source>
<target>拖动:倾斜(Ctrl = 自由位置,Shift = 15°)</target>
</phrase>
<phrase>
<source>Glisser : incliner (Maj = par pas de 15°)</source>
<target>拖动:倾斜(Shift = 以 15° 为步长)</target>
</phrase>
<phrase>
<source>Glisser : pivoter (Maj = par pas de 15°)</source>
<target>拖动:旋转(Shift = 以 15° 为步长)</target>
</phrase>
<phrase>
<source>Glisser : redimensionner (Ctrl = depuis le centre + position libre, Maj = proportions</source>
<target>拖动:调整大小(Ctrl = 从中心 + 自由位置,Shift = 比例</target>
</phrase>
<phrase>
<source>Glisser : redimensionner (Maj = conserver les proportions, Ctrl = depuis le centre)</source>
<target>拖动:调整大小(Shift = 保持比例,Ctrl = 从中心)</target>
</phrase>
<phrase>
<source>Glisser : repositionner le centre de rotation (Ctrl = position libre)</source>
<target>拖动:重新定位旋转中心(Ctrl = 自由位置)</target>
</phrase>
<phrase>
<source>Glisser : rotation (Ctrl = position libre, Maj = 15°)</source>
<target>拖动:旋转(Ctrl = 自由位置,Shift = 15°)</target>
</phrase>
<phrase>
<source>Glisser le point violet : arrondir les coins</source>
<target>拖动紫色点:圆角</target>
</phrase>
<phrase>
<source>Glisser un coin : pivoter (Maj = par pas de 15°) ; glisser un bord : incliner (Maj = par pas de 15°) ; point rouge : déplacer le centre de rotation</source>
<target>拖动角:旋转(Shift = 以 15° 为步长);拖动边:倾斜(Shift = 以 15° 为步长);红点:移动旋转中心</target>
</phrase>
<phrase>
<source>Glisser un coin/bord : redimensionner (Ctrl = depuis le centre, Maj = conserver les proportions)</source>
<target>拖动角/边:调整大小(Ctrl = 从中心,Shift = 保持比例)</target>
</phrase>
<phrase>
<source>Glisser un coin/bord : redimensionner (Ctrl = depuis le centre, Maj = proportions, Alt = détacher en polyligne)</source>
<target>拖动角/边:调整大小(Ctrl = 从中心,Shift = 比例,Alt = 分离为多段线)</target>
</phrase>
<phrase>
<source>Glisser un point : le déplacer</source>
<target>拖动点:移动它</target>
</phrase>
<phrase>
<source>Glisser une extrémité : la déplacer</source>
<target>拖动端点:移动它</target>
</phrase>
<phrase>
<source>Glisser une poignée ou la courbe : déformer (Alt = briser la tangente) ; Alt+glisser un point anguleux : créer des poignées ; clic droit : menu du nœud le plus proche</source>
<target>拖动控制柄或曲线:变形(Alt = 断开切线);Alt+拖动角点:创建控制柄;右键单击:最近节点菜单</target>
</phrase>
<phrase>
<source>Géométrie</source>
<target>几何</target>
</phrase>
<phrase>
<source>Hauteur</source>
<target>高度</target>
</phrase>
<phrase>
<source>Image BMP (*.bmp)</source>
<target>BMP 图像 (*.bmp)</target>
</phrase>
<phrase>
<source>Image Files (*.png *.jpg *.jpeg *.bmp *.svg)</source>
<target>图像文件 (*.png *.jpg *.jpeg *.bmp *.svg)</target>
</phrase>
<phrase>
<source>Image JPEG (*.jpg *.jpeg)</source>
<target>JPEG 图像 (*.jpg *.jpeg)</target>
</phrase>
<phrase>
<source>Image PNG (*.png)</source>
<target>PNG 图像 (*.png)</target>
</phrase>
<phrase>
<source>Image source</source>
<target>源图像</target>
</phrase>
<phrase>
<source>Image SVG (*.svg)</source>
<target>SVG 图像 (*.svg)</target>
</phrase>
<phrase>
<source>Images non incluses dans l&apos;export DXF</source>
<target>图像未包含在 DXF 导出中</target>
<definition>message box title</definition>
</phrase>
<phrase>
<source>Impossible d&apos;enregistrer l&apos;image à cet emplacement.</source>
<target>无法将图像保存到该位置。</target>
</phrase>
<phrase>
<source>Impossible d&apos;enregistrer la nomenclature dans %1.
%2</source>
<target>无法将物料清单保存到 %1。
%2</target>
</phrase>
<phrase>
<source>Impossible de charger l&apos;image.</source>
<target>无法加载图像。</target>
</phrase>
<phrase>
<source>Inclinaison X</source>
<target>X 倾斜</target>
</phrase>
<phrase>
<source>Inclinaison Y</source>
<target>Y 倾斜</target>
</phrase>
<phrase>
<source>Incliner %1</source>
<target>倾斜 %1</target>
</phrase>
<phrase>
<source>Incliner une image</source>
<target>倾斜图像</target>
</phrase>
<phrase>
<source>La limite fixée pour cet élément maître est atteinte (Limite: %1).
Voulez-vous tout de même lier ce contact esclave ?</source>
<target>已达到此主元件的限制(限制:%1)。
是否仍要链接此从属触点?</target>
</phrase>
<phrase>
<source>Largeur</source>
<target>宽度</target>
</phrase>
<phrase>
<source>Le format DXF utilisé ici (AC1006) ne permet pas d&apos;inclure d&apos;image. Les images seront représentées uniquement par un rectangle de contour (position, taille, rotation et inclinaison conservées), sans le contenu de l&apos;image.</source>
<target>此处使用的 DXF 格式 (AC1006) 无法包含图像。图像将仅以轮廓矩形表示(保留位置、大小、旋转和倾斜),不包含图像内容。</target>
<definition>message box content</definition>
</phrase>
<phrase>
<source>Lisse</source>
<target>平滑</target>
</phrase>
<phrase>
<source>Liste de câblage</source>
<target>接线列表</target>
<definition>window title</definition>
</phrase>
<phrase>
<source>Liste de câblage (base de données)</source>
<target>接线列表(数据库)</target>
</phrase>
<phrase>
<source>Longueur</source>
<target>长度</target>
</phrase>
<phrase>
<source>margin: 5px; font-weight: bold;</source>
<target>margin: 5px; font-weight: bold;</target>
</phrase>
<phrase>
<source>Miroir horizontal</source>
<target>水平镜像</target>
</phrase>
<phrase>
<source>Miroir horizontal d&apos;une image</source>
<target>图像水平镜像</target>
</phrase>
<phrase>
<source>Miroir horizontal de %1</source>
<target>%1 的水平镜像</target>
</phrase>
<phrase>
<source>Miroir impossible : inclinaison trop extrême pour cette forme</source>
<target>无法镜像:此形状的倾斜过于极端</target>
</phrase>
<phrase>
<source>Miroir vertical</source>
<target>垂直镜像</target>
</phrase>
<phrase>
<source>Miroir vertical d&apos;une image</source>
<target>图像垂直镜像</target>
</phrase>
<phrase>
<source>Miroir vertical de %1</source>
<target>%1 的垂直镜像</target>
</phrase>
<phrase>
<source>Modifier l&apos;angle d&apos;un arc</source>
<target>修改弧的角度</target>
</phrase>
<phrase>
<source>Modifier l&apos;angle d&apos;une forme</source>
<target>修改形状角度</target>
</phrase>
<phrase>
<source>Modifier l&apos;angle d&apos;une image</source>
<target>修改图像角度</target>
</phrase>
<phrase>
<source>Modifier l&apos;inclinaison d&apos;une image</source>
<target>修改图像倾斜</target>
</phrase>
<phrase>
<source>Modifier la courbure d&apos;%1</source>
<target>修改%1的曲率</target>
</phrase>
<phrase>
<source>Modifier la forme d&apos;%1</source>
<target>修改%1的形状</target>
</phrase>
<phrase>
<source>Modifier la hauteur d&apos;une image</source>
<target>修改图像高度</target>
</phrase>
<phrase>
<source>Modifier la largeur d&apos;une image</source>
<target>修改图像宽度</target>
</phrase>
<phrase>
<source>Modifier la longueur d&apos;une ligne</source>
<target>修改线条长度</target>
</phrase>
<phrase>
<source>Modifier la taille d&apos;une forme</source>
<target>修改形状大小</target>
</phrase>
<phrase>
<source>Modifier le type d&apos;un nœud</source>
<target>修改节点类型</target>
</phrase>
<phrase>
<source>Modifier une image</source>
<target>修改图像</target>
</phrase>
<phrase>
<source>Modèle</source>
<target>型号</target>
</phrase>
<phrase>
<source>Nombre maximum de contacts esclaves définis : non défini
</source>
<target>已定义的从属触点最大数量:未定义
</target>
</phrase>
<phrase>
<source>Notes</source>
<target>备注</target>
</phrase>
<phrase>
<source>Nœud le plus proche</source>
<target>最近节点</target>
</phrase>
<phrase>
<source>pivoter/incliner</source>
<target>旋转/倾斜</target>
</phrase>
<phrase>
<source>Rayon X</source>
<target>X半径</target>
</phrase>
<phrase>
<source>Rayon Y</source>
<target>Y半径</target>
</phrase>
<phrase>
<source>redimensionner</source>
<target>调整大小</target>
</phrase>
<phrase>
<source>Redimensionner %1</source>
<target>调整 %1 大小</target>
</phrase>
<phrase>
<source>Redimensionner une image</source>
<target>调整图像大小</target>
</phrase>
<phrase>
<source>Remplacer l&apos;image...</source>
<target>替换图像...</target>
</phrase>
<phrase>
<source>Remplacer une image</source>
<target>替换图像</target>
</phrase>
<phrase>
<source>Restaurer les proportions</source>
<target>恢复比例</target>
</phrase>
<phrase>
<source>Restaurer les proportions d&apos;une image</source>
<target>恢复图像比例</target>
</phrase>
<phrase>
<source>Retirer cette couleur</source>
<target>移除此颜色</target>
</phrase>
<phrase>
<source>Revenir à l&apos;image complète, sans rognage</source>
<target>恢复为完整图像,不裁剪</target>
</phrase>
<phrase>
<source>rgb(%1, %2, %3)</source>
<target>rgb(%1, %2, %3)</target>
</phrase>
<phrase>
<source>Rogner l&apos;image</source>
<target>裁剪图像</target>
</phrase>
<phrase>
<source>Rogner une image</source>
<target>裁剪图像</target>
</phrase>
<phrase>
<source>Rogner...</source>
<target>裁剪...</target>
</phrase>
<phrase>
<source>Rotation/Inclinaison</source>
<target>旋转/倾斜</target>
</phrase>
<phrase>
<source>Réinitialiser</source>
<target>重置</target>
</phrase>
<phrase>
<source>Selectionner une image...</source>
<target>选择图像...</target>
</phrase>
<phrase>
<source>Supprimer le nœud le plus proche</source>
<target>删除最近节点</target>
</phrase>
<phrase>
<source>Supprimer un point d&apos;une courbe</source>
<target>从曲线删除点</target>
</phrase>
<phrase>
<source>Symétrique</source>
<target>对称</target>
</phrase>
<phrase>
<source>Séparation de potentiel</source>
<target>电位分隔</target>
</phrase>
<phrase>
<source>Taille</source>
<target>大小</target>
</phrase>
<phrase>
<source>Tension nominale</source>
<target>额定电压</target>
</phrase>
<phrase>
<source>Tolérance pour cette couleur</source>
<target>此颜色的容差</target>
</phrase>
<phrase>
<source>Tous les fichiers (*)</source>
<target>所有文件 (*)</target>
</phrase>
<phrase>
<source>Transformer %1</source>
<target>变换 %1</target>
</phrase>
<phrase>
<source>Transparence non conservée</source>
<target>不保留透明度</target>
</phrase>
<phrase>
<source>un arc</source>
<target>一段弧</target>
</phrase>
<phrase>
<source>un coin</source>
<target>一个角</target>
</phrase>
<phrase>
<source>un point</source>
<target>一个点</target>
</phrase>
<phrase>
<source>une courbe</source>
<target>一条曲线</target>
</phrase>
<phrase>
<source>une extrémité</source>
<target>一个端点</target>
</phrase>
<phrase>
<source>une image</source>
<target>一张图像</target>
<definition>part of a sentence listing the content of a diagram</definition>
</phrase>
<phrase>
<source>Verrouiller la numérotation automatique</source>
<target>锁定自动编号</target>
</phrase>
<phrase>
<source>Verrouiller la position</source>
<target>锁定位置</target>
</phrase>
<phrase>
<source>Verrouillé : modifier la largeur ou la hauteur ajuste l&apos;autre pour conserver les proportions. Cliquer pour déverrouiller.</source>
<target>已锁定:修改宽度或高度会调整另一个以保持比例。单击解锁。</target>
</phrase>
<phrase>
<source>Échec de l&apos;enregistrement</source>
<target>保存失败</target>
</phrase>
<phrase>
<source>Édition des nœuds</source>
<target>节点编辑</target>
</phrase>
<phrase>
<source>armoire</source>
<target>机柜</target>
<definition>cabinet / enclosure</definition>
</phrase>
<phrase>
<source>bobine</source>
<target>线圈</target>
<definition>coil</definition>
</phrase>
<phrase>
<source>borne</source>
<target>端子</target>
<definition>terminal - a connection point on a symbol</definition>
</phrase>
<phrase>
<source>bornier</source>
<target>端子排</target>
<definition>terminal strip</definition>
</phrase>
<phrase>
<source>cable</source>
<target>电缆</target>
<definition>cable</definition>
</phrase>
<phrase>
<source>calibre</source>
<target>额定值</target>
<definition>rating - the current rating of a device</definition>
</phrase>
<phrase>
<source>cartouche</source>
<target>标题栏</target>
<definition>title block - the framed information panel</definition>
</phrase>
<phrase>
<source>collection</source>
<target>符号库</target>
<definition>collection - the symbol library</definition>
</phrase>
<phrase>
<source>conducteur</source>
<target>导线</target>
<definition>conductor - the drawn wire between terminals</definition>
</phrase>
<phrase>
<source>contact</source>
<target>触点</target>
<definition>contact</definition>
</phrase>
<phrase>
<source>contacteur</source>
<target>接触器</target>
<definition>contactor</definition>
</phrase>
<phrase>
<source>courant</source>
<target>电流</target>
<definition>current</definition>
</phrase>
<phrase>
<source>disjoncteur</source>
<target>断路器</target>
<definition>circuit breaker</definition>
</phrase>
<phrase>
<source>dossier</source>
<target>文件夹</target>
<definition>folder</definition>
</phrase>
<phrase>
<source>element</source>
<target>元件</target>
<definition>element - a symbol placed on a sheet</definition>
</phrase>
<phrase>
<source>esclave</source>
<target>从</target>
<definition>slave - the contact block belonging to a master</definition>
</phrase>
<phrase>
<source>fusible</source>
<target>熔断器</target>
<definition>fuse</definition>
</phrase>
<phrase>
<source>grille</source>
<target>网格</target>
<definition>grid</definition>
</phrase>
<phrase>
<source>interrupteur</source>
<target>开关</target>
<definition>switch</definition>
</phrase>
<phrase>
<source>maitre</source>
<target>主</target>
<definition>master - the element a cross-reference points from</definition>
</phrase>
<phrase>
<source>modele</source>
<target>模板</target>
<definition>template</definition>
</phrase>
<phrase>
<source>moteur</source>
<target>电动机</target>
<definition>motor</definition>
</phrase>
<phrase>
<source>neutre</source>
<target>中性线</target>
<definition>neutral</definition>
</phrase>
<phrase>
<source>nomenclature</source>
<target>物料清单</target>
<definition>parts list / bill of materials</definition>
</phrase>
<phrase>
<source>numerotation</source>
<target>编号</target>
<definition>numbering - automatic wire and part numbering</definition>
</phrase>
<phrase>
<source>phase</source>
<target>相</target>
<definition>phase</definition>
</phrase>
<phrase>
<source>potentiel</source>
<target>电位</target>
<definition>potential - conductors electrically joined as one node</definition>
</phrase>
<phrase>
<source>projet</source>
<target>项目</target>
<definition>project - the .qet file holding the sheets</definition>
</phrase>
<phrase>
<source>puissance</source>
<target>功率</target>
<definition>power</definition>
</phrase>
<phrase>
<source>relais</source>
<target>继电器</target>
<definition>relay</definition>
</phrase>
<phrase>
<source>renvoi de folio</source>
<target>图纸交叉引用</target>
<definition>sheet cross-reference</definition>
</phrase>
<phrase>
<source>repere</source>
<target>标识</target>
<definition>designation - the identifying mark on a part</definition>
</phrase>
<phrase>
<source>report de folio</source>
<target>图纸交叉引用</target>
<definition>sheet cross-reference (the arrow symbol)</definition>
</phrase>
<phrase>
<source>schema</source>
<target>原理图</target>
<definition>diagram</definition>
</phrase>
<phrase>
<source>section</source>
<target>截面积</target>
<definition>cross-section - conductor size in mm2</definition>
</phrase>
<phrase>
<source>sommaire</source>
<target>目录</target>
<definition>summary / table of contents</definition>
</phrase>
<phrase>
<source>tension</source>
<target>电压</target>
<definition>voltage</definition>
</phrase>
<phrase>
<source>terre</source>
<target>接地</target>
<definition>earth / ground</definition>
</phrase>
<phrase>
<source>texte</source>
<target>文本</target>
<definition>text</definition>
</phrase>
<phrase>
<source>transformateur</source>
<target>变压器</target>
<definition>transformer</definition>
</phrase>
</QPH>
BIN
View File
Binary file not shown.
+2 -8
View File
@@ -103,9 +103,7 @@ cmake -S . -B "$BUILD_DIR" -G Ninja \
-DBUILD_WITH_KF=$BUILD_WITH_KF \ -DBUILD_WITH_KF=$BUILD_WITH_KF \
-DBUILD_KF=OFF \ -DBUILD_KF=OFF \
-DQET_EXPORT_PROJECT_DB=ON \ -DQET_EXPORT_PROJECT_DB=ON \
-DPACKAGE_TESTS=OFF \ -DPACKAGE_TESTS=OFF
-DQET_ENABLE_SPACEMOUSE=ON \
-DQET_SPACEMOUSE_BACKEND=hid
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "ERROR: cmake configure failed." echo "ERROR: cmake configure failed."
@@ -398,12 +396,10 @@ done
echo "-- Signing main executable..." echo "-- Signing main executable..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \ codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
--entitlements "${current_dir}/misc/qelectrotech.entitlements" \
"$BUNDLE/Contents/MacOS/$APPNAME" "$BUNDLE/Contents/MacOS/$APPNAME"
echo "-- Signing bundle..." echo "-- Signing bundle..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \ codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$BUNDLE"
--entitlements "${current_dir}/misc/qelectrotech.entitlements" "$BUNDLE"
echo echo
echo "Verifying bundle signature..." echo "Verifying bundle signature..."
@@ -504,10 +500,8 @@ find "$MOUNT_POINT/$BUNDLE/Contents/PlugIns" \( -name "*.dylib" -o -name "*.so"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib" codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done done
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \ codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
--entitlements "${current_dir}/misc/qelectrotech.entitlements" \
"$MOUNT_POINT/$BUNDLE/Contents/MacOS/$APPNAME" "$MOUNT_POINT/$BUNDLE/Contents/MacOS/$APPNAME"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \ codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
--entitlements "${current_dir}/misc/qelectrotech.entitlements" \
"$MOUNT_POINT/$BUNDLE" "$MOUNT_POINT/$BUNDLE"
echo "Verifying bundle signature inside DMG..." echo "Verifying bundle signature inside DMG..."
-12
View File
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- 3D mouse with 3DxWare installed: lets QElectroTech load
3Dconnexion's client library, which 3Dconnexion signs, at run time
(sources/spacemouse/connexionbackend.h). The hardened runtime
refuses it otherwise, and the 3D mouse does nothing. -->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
+23 -242
View File
@@ -30,20 +30,14 @@ here read the model.
| `qet_project_info` | title, format version, folios, element and conductor counts | | `qet_project_info` | title, format version, folios, element and conductor counts |
| `qet_elements` | placed elements: uuid, type, position, label, information bag | | `qet_elements` | placed elements: uuid, type, position, label, information bag |
| `qet_conductors` | conductors and their documentation fields; filter by attribute | | `qet_conductors` | conductors and their documentation fields; filter by attribute |
| `qet_diff` | **what an edit actually changed** — element moves, adds, removes, relabels; conductor changes; and folio fields, texts, shapes, images, symbol text fields and terminal strips | | `qet_diff` | **what an edit actually changed** — moves with deltas, adds, removes, relabels, conductor field changes |
| `qet_scan` | sweep a directory of projects, counting nodes carrying an attribute | | `qet_scan` | sweep a directory of projects, counting nodes carrying an attribute |
| `qet_element_info` | a `.elmt`: translated names, terminals, info fields, part counts | | `qet_element_info` | a `.elmt`: translated names, terminals, info fields, part counts |
| `qet_export` | run a headless export (pdf, png, svg, bom, cables, wires, wiring, nets, links, info) | | `qet_export` | run a headless export (pdf, png, svg, bom, cables, wires, wiring, nets, links, info) |
| `qet_edit` | **change a project** — place, move, rotate, label, wire, number, cross-reference, add text, shapes and images, restyle a symbol's text fields, delete; then diff the result |
| `qet_element_build` | **author a `.elmt`** — draw a new symbol, with terminals to wire it by |
| `qet_project_new` | **start from nothing** — an empty project with a title and folios |
| `qet_element_search` | **find a symbol** in a collection by name (any language), type or terminal count |
| `qet_check` | **design-rule checks** — duplicate labels, unlabelled masters, unnumbered conductors, empty folios |
| `qet_query` | **ask the project database** — read-only SQL over the views and tables |
`qet_export` and `qet_edit` launch QElectroTech. Everything else parses the Only `qet_export` launches QElectroTech. Everything else parses the file
file directly, which is faster, needs no display, and cannot be confused by directly, which is faster, needs no display, and cannot be confused by a
a dialog. dialog.
## Running it ## Running it
@@ -62,80 +56,12 @@ Register it with an MCP client, for example:
"mcpServers": { "mcpServers": {
"qet": { "qet": {
"command": "python3", "command": "python3",
"args": ["/path/to/qelectrotech/misc/qet-mcp/qet_mcp.py"], "args": ["/path/to/qelectrotech/misc/qet-mcp/qet_mcp.py"]
"env": {
"QET_MCP_WORKSPACE": "/home/you/drawings",
"QET_ENABLE_SCRIPTING": "1"
}
} }
} }
} }
``` ```
## Five tools need scripting switched on
A QElectroTech with JavaScript scripting switched off refuses `--run`, and
off is the default from
[#984](https://github.com/qelectrotech/qelectrotech-source-mirror/pull/984)
onwards. Five tools here drive it that way and stop working until it is
turned on:
| | |
|---|---|
| need `QET_ENABLE_SCRIPTING=1` | `qet_query`, `qet_continuity`, `qet_check`, `qet_project_new`, `qet_edit` |
| unaffected | everything else — they read the `.qet` directly, or, in `qet_export`'s case, use a plain CLI flag |
The variable goes in the environment this server is started in, which for an
MCP client is the `env` block above; the server passes its environment
straight through to QElectroTech. It does not set the variable itself, on
purpose — a switch a program turns on for itself is not a switch. Whoever
configured this server and pointed it at a QElectroTech binary made that
choice, and their interactive QElectroTech keeps whatever its own setting
says.
Without it, those five come back `"ok": false` with a `hint` naming the
variable. Older builds, from before the setting existed, need nothing.
## What the server is allowed to touch
Every path in a tool call is chosen by the model, so without a policy this
server would be a read/write primitive for anything the operating system
lets the process reach: read any project on the disk, export one somewhere
else, overwrite an unrelated file, embed an arbitrary local image or PDF.
So **data paths are confined to a workspace**:
| | |
|---|---|
| `QET_MCP_WORKSPACE` | the directories tool calls may read and write, separated by `:` (`;` on Windows) |
| unset | the directory the server was started in |
| `QET_MCP_ALLOW_ANY_PATH=1` | turns the check off entirely |
Set the workspace to the folder your drawings live in. A path outside it is
refused with an error naming what was allowed; symlinks are resolved first,
so a link planted inside the workspace is judged by where it points.
Two arguments are deliberately **not** confined: `binary` (the
`qelectrotech` executable) and `elements_dir` (the element collection).
Those are configuration, chosen once by whoever runs the server, and both
normally live in `/usr` or a build tree — outside any sensible workspace.
Confining them would reject the ordinary case while stopping nothing.
`QET_MCP_ALLOW_ANY_PATH=1` is equivalent to granting the client local
filesystem access with this process's privileges. It exists so that is a
deliberate choice rather than the default.
**Nothing is overwritten unasked.** `qet_export`, `qet_edit`,
`qet_project_new` and `qet_element_build` refuse an `output` that already
exists unless the call passes `"overwrite": true`. Replacing a file is the
one step this server cannot undo, so it is the one step it will not take on
its own.
The confinement is applied where tool arguments enter the server, not inside
each tool. Importing `qet_mcp` and calling `tool_export()` from your own
Python is not confined and is not meant to be — that is your code calling a
library, and you already chose the paths.
## Worked examples ## Worked examples
**What did that edit change?** **What did that edit change?**
@@ -153,74 +79,6 @@ library, and you already chose the paths.
Four elements moved by one uniform delta; nothing was relabelled. That is Four elements moved by one uniform delta; nothing was relabelled. That is
the answer a screenshot gave wrongly. the answer a screenshot gave wrongly.
**Draw something, and check it landed**
```json
{"name": "qet_edit", "arguments": {
"binary": "/path/to/qelectrotech",
"project": "in.qet", "output": "out.qet",
"elements_dir": "/path/to/qelectrotech/elements",
"operations": [
{"op": "add_folio", "id": "f"},
{"op": "set_folio_title", "folio": "$f", "title": "Starter"},
{"op": "add_element", "id": "k1", "folio": "$f", "path": "common://.../coil.elmt", "x": 100, "y": 100},
{"op": "add_element", "id": "k2", "folio": "$f", "path": "common://.../coil.elmt", "x": 320, "y": 100},
{"op": "add_conductor", "folio": "$f", "from": "$k1", "from_terminal": 0, "to": "$k2", "to_terminal": 0},
{"op": "set_conductor", "folio": "$f", "element": "$k1", "terminal": 0, "property": "num", "value": "W7"},
{"op": "set_label", "folio": "$f", "element": "$k1", "label": "KM1"}
]}}
```
An op that creates something takes an `"id"`; later ops name it as `"$id"`.
Terminals are addressed by index — top to bottom, then left to right, **not**
the order the `.elmt` lists them. `qet_element_info` and `qet_element_search`
both report that index order. The answer carries a per-operation result
*and* a `qet_diff`, because "addConductor → true" says the call was
accepted, not that the file came out right:
```json
"diff": {"elements": {"before": 11, "after": 13, "added": ["{0aa3…}", "{6f63…}"]},
"conductors": {"before": 47, "after": 48, "added": ["4:{0aa3…}/{2904…}--{6f63…}/{2904…}"],
"removed": []}}
```
**Draw a symbol that does not exist yet**
```json
{"name": "qet_element_build", "arguments": {
"output": "/path/to/collection/99_custom/my_resistor.elmt",
"names": {"en": "Test resistor", "fr": "Résistance de test"},
"parts": [
{"type": "rect", "x": -10, "y": -20, "width": 20, "height": 40},
{"type": "line", "x1": 0, "y1": -30, "x2": 0, "y2": -20},
{"type": "line", "x1": 0, "y1": 20, "x2": 0, "y2": 30},
{"type": "text", "x": 14, "y": -4, "text": "R"}
],
"terminals": [{"x": 0, "y": -30, "orientation": "n", "name": "1"},
{"x": 0, "y": 30, "orientation": "s", "name": "2"}]}}
```
Then place it with `qet_edit` like any catalogue element. Unlike a
project, a `.elmt` is not rewritten by QElectroTech on a round trip, so
generating one here is safe in a way that generating a `.qet` would not
be — there is no `toXml()` waiting to drop what this writer did not know
to emit.
**Ask a question the XML cannot answer**
```json
{"name": "qet_query", "arguments": {
"binary": "/path/to/qelectrotech", "project": "industrial.qet",
"sql": "SELECT label, COUNT(*) AS n FROM element_nomenclature_view WHERE label <> '' GROUP BY label HAVING n > 1 ORDER BY n DESC"}}
```
```json
"rows": [{"label": "V6", "n": 7}, {"label": "V5", "n": 6}, {"label": "V4", "n": 6}]
```
Duplicate element labels in a shipped example — a design-rule question,
answered by the database that already knew it.
**How much of a corpus uses a field?** **How much of a corpus uses a field?**
```json ```json
@@ -234,43 +92,14 @@ answered by the database that already knew it.
Across the shipped examples: 3190 conductors, not one with a cable value. Across the shipped examples: 3190 conductors, not one with a cable value.
## Testing
```bash
python3 test_qet_mcp.py # unit + protocol, no QElectroTech needed
QET_BINARY=/path/to/qelectrotech \
QET_ELEMENTS=/path/to/qelectrotech/elements \
QET_EXAMPLES=/path/to/qelectrotech/examples \
QET_ENABLE_SCRIPTING=1 \
python3 test_qet_mcp.py # everything
```
`QET_ENABLE_SCRIPTING=1` matters from #984 onwards: without it the
integration tests that drive QElectroTech through a script all fail, and
they fail as "the edit did nothing" rather than as "scripting is off", which
reads like a regression in the thing under test.
176 tests in three layers: unit (validation, script generation, the terminal
order rule, the diff, the part schema), the real stdio transport, and
integration against a built QElectroTech. Several exist because the
behaviour they pin was once wrong and looked right, and say so in their
docstrings. To check the suite itself rather than trust it, each of those
bugs was reintroduced in turn and the suite confirmed to fail: ten in the
Python, plus the hang guard on `addConductor` and the database refresh in
`ConductorCreator` in the C++.
## Notes and limits ## Notes and limits
- **The project database is reachable now, through `qet_query`.** It was - **The project database is not reachable from outside the application.**
not when this server was written, which is why every other structural `projectDataBase::newQuery()` and `isReadOnlySelect()` are C++-internal
tool here re-derives its answer from the XML. Prefer the views — and the JavaScript scripting API exposes no SQL binding, so structural
`element_nomenclature_view`, `project_summary_view`, `wiring_list_view` queries here are done over the XML. A `--query` CLI verb, or a scripting
— which exist to be queried; the underlying tables are how the cache is binding, would let this server expose the guarded read-only SQL surface
arranged today and a column may move. Call `qet_query` with no `sql` to instead, and would be a better foundation.
list both. Only `SELECT` and `WITH` are accepted, which is the rule
QElectroTech applies to its own custom-query box, not one invented here.
An empty result and a failed query are told apart: `row_count` 0 with no
`error` means nothing matched, and a typo'd column name says so.
- **`qet_export` isolates its launch.** SingleApplication keys its socket - **`qet_export` isolates its launch.** SingleApplication keys its socket
on `applicationFilePath()`, so a second launch of the same binary path on `applicationFilePath()`, so a second launch of the same binary path
forwards its request to an already-running instance and returns *that* forwards its request to an already-running instance and returns *that*
@@ -286,64 +115,16 @@ Python, plus the hang guard on `addConductor` and the database refresh in
its ends with `terminal1`/`terminal2`, and the project format has two its ends with `terminal1`/`terminal2`, and the project format has two
schemes: folio-scoped integer ids in older files, terminal-definition schemes: folio-scoped integer ids in older files, terminal-definition
uuids plus `element1`/`element2` in newer ones. The integer ids are uuids plus `element1`/`element2` in newer ones. The integer ids are
**renumbered on every save**, so keying on them — which this tool did at **renumbered on every save**, so keying on them made all 47 conductors of
first — made all 47 conductors of an untouched folio read as removed and an untouched `ArduinoLCD.qet` read as 29 removed and 29 re-added the
re-added the moment the other side had been through QElectroTech, which moment the other side had been through QElectroTech. Ends are now keyed
is exactly what `qet_edit` produces. They are now keyed by owning element by owning element uuid plus terminal, which is stable across a save:
uuid plus terminal, which is stable across a save: measured at 0 colliding measured at 0 colliding keys over 3190 conductors in the 24 shipped
keys over 3190 conductors in the 24 shipped examples, and 0 churn on a examples, and 0 churn on a re-saved but otherwise untouched project.
no-op edit. Where an element predates persisted uuids the end cannot be Where an element predates persisted uuids the end cannot be resolved and
resolved and keeps a `#`-marked unstable key; the diff then reports keeps a `#`-marked unstable key; the diff then reports `unstable_keys`
`unstable_keys` and says so rather than pretending to be comparable. and says so rather than pretending to be comparable.
- **Texts, shapes and images have no uuid**, so `qet_diff` cannot say "the same
text, edited": an edited text reads as the old one removed and a new one
added, both shown. Shapes and images are keyed by position, so a restyle
or rescale *is* reported as a change to that item, but a move reads as a
removal plus an addition. The folio `version` attribute is left out of the
comparison on purpose: QElectroTech rewrites it on every save, and
including it made every folio of any re-saved project look edited.
- **Elements** written before persisted uuids fall back to a positional key, - **Elements** written before persisted uuids fall back to a positional key,
which makes a move in such a file read as a remove plus an add. which makes a move in such a file read as a remove plus an add rather
- **`qet_edit` needs a build whose scripting API carries the drawing verbs.** than as a move.
Against an older one it reports exactly which methods are missing and - Read-only by design. Nothing here writes to a project.
changes nothing. `addElement` and the move/delete verbs shipped with the
scripting API; `addConductor`, `rotateElement`, `setElementLabel`,
`setElementInfo` and `setFolioTitle` are newer.
- **`elements_dir` is not optional for `common://` paths.** The sandboxed
run has its own empty HOME, so QElectroTech falls back to the compiled-in
collection path, which on a machine that never ran `make install` does not
exist. The only symptom is `addElement` reporting that a file plainly
present "does not resolve to an element". An absolute `.elmt` path works
without it.
- **`set_conductor` changes the whole potential, not one segment.** That is
what the application does — a wire number describes a potential — so name
a terminal carrying exactly one conductor and the change reaches every
conductor electrically joined to it. A terminal several conductors meet
at names none of them and is refused, so address a potential from one of
its leaves. Property names are the file's own, so `qet_conductors` reads
back exactly what was set.
- **`link_elements` takes a folio for each end**, because a master and its
slave are normally on different folios. Whether a pair may be linked is
decided by QElectroTech's own `isLinkable()`, so a script cannot make a
link the GUI would refuse.
- **An element must live inside a collection to be placeable.** This is
not about the path syntax: an absolute `.elmt` path works, but only if
the file sits under a directory QElectroTech knows as a collection.
Write it under the tree you pass as `elements_dir` and `qet_edit` can
place it, by absolute path or as `common://…`; write it anywhere else
and `add_element` reports only "does not resolve to an element".
- **`qet_element_build` computes the `.elmt` size header, and checks it.**
`width`/`height`/`hotspot_x`/`hotspot_y` relate to the drawing by a
containment constraint, not a formula — the declared box runs from
`(-hotspot_x, -hotspot_y)` to `(width - hotspot_x, height - hotspot_y)`
and the drawing must fit inside it. The shipped collection shows authors
picking their own margins (one element pads 2 units left and 3 right,
another 8 and 2), so there is no convention to copy, only an invariant
to satisfy. A drawing that escaped its box is the classic way a
hand-written element renders clipped in the collection panel while
looking fine in XML.
- **QElectroTech interrupts a script at 30 s** of its own accord, separately
from this tool's `timeout`. A very long operation list will hit that
first.
- **`qet_edit` never writes the input.** It saves to a separate file and
diffs the two, so the original is always the thing the diff is against.
Binary file not shown.
@@ -1,44 +0,0 @@
<definition version="0.100.0" type="element" link_type="master" width="40" height="60" hotspot_x="17" hotspot_y="32">
<uuid uuid="{6d3714f6-a0e1-71b3-e4bd-5a35d4cbbec1}"/>
<names>
<name lang="ar">ملف KA بمحتفظ مغناطيسي</name>
<name lang="ca">Bobina</name>
<name lang="cs">Bistabilní remanentní relé</name>
<name lang="de">Remanenzrelais</name>
<name lang="el">Πηνίο με μανδάλωση</name>
<name lang="en">Coil</name>
<name lang="es">Bobina KA de remanencia</name>
<name lang="fr">Bobine KA à rémanence</name>
<name lang="hu">Tekercs</name>
<name lang="it">Bobina</name>
<name lang="nl">spoel remanent</name>
<name lang="nl_BE">Spoel KA remanent</name>
<name lang="pl">Cewka przekaźnika remanencyjnego</name>
<name lang="pt_BR">Bobina</name>
<name lang="ru">Обмотка</name>
<name lang="zh">剩磁保持线圈</name>
</names>
<kindInformations>
<kindInformation name="type">plc</kindInformation>
</kindInformations>
<informations>Author: The QElectroTech team
License: see http://qelectrotech.org/wiki/doc/elements_license</informations>
<description>
<rect x="14" y="-8" width="6" height="16" rx="0" ry="0" style="line-style:normal;line-weight:normal;filling:none;color:black" antialias="false"/>
<line x1="0" y1="-20" x2="0" y2="-8" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" antialias="false"/>
<dynamic_text x="2" y="11.33" z="5" text_width="-1" Halignment="AlignLeft" Valignment="AlignTop" frame="false" rotation="0" keep_visual_rotation="false" text_from="UserText" uuid="{647e33ed-520b-4d80-bbc4-7b31177b8f26}" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular">
<text>A2</text>
</dynamic_text>
<dynamic_text x="2" y="-24.67" z="6" text_width="-1" Halignment="AlignLeft" Valignment="AlignTop" frame="false" rotation="0" keep_visual_rotation="false" text_from="UserText" uuid="{fb1f8de9-70c0-4801-8dc7-12e6bf88d8f5}" font="Liberation Sans,4,-1,5,25,0,0,0,0,0,Regular">
<text>A1</text>
</dynamic_text>
<line x1="0" y1="8" x2="0" y2="20" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" antialias="false"/>
<dynamic_text x="30" y="-9.17" z="8" text_width="-1" Halignment="AlignLeft" Valignment="AlignTop" frame="false" rotation="0" keep_visual_rotation="false" text_from="ElementInfo" uuid="{b7fccfc3-05f1-459c-9766-f49a481bd0ff}" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular">
<text></text>
<info_name>label</info_name>
</dynamic_text>
<polygon x1="-14" y1="-8" x2="14" y2="-8" x3="20" y3="8" x4="-14" y4="8" antialias="false" style="line-style:normal;line-weight:normal;filling:none;color:black"/>
<terminal uuid="{6a87c921-6f5d-4f5c-8673-228a6d13c5c8}" name="A2" x="0" y="20" orientation="s" type="Generic"/>
<terminal uuid="{2904e5fa-6bbe-4127-acdd-f92feadd6ece}" name="A1" x="0" y="-20" orientation="n" type="Generic"/>
</description>
</definition>
-16
View File
@@ -1,16 +0,0 @@
<definition version="0.100.0" type="element" link_type="slave" width="20" height="60" hotspot_x="9" hotspot_y="30">
<uuid uuid="{94d56f06-6814-5561-9148-2da3a9c4f900}"/>
<names>
<name lang="en">PLC Slave Test</name>
</names>
<kindInformations>
<kindInformation name="type">plc</kindInformation>
<kindInformation name="state">NO</kindInformation>
<kindInformation name="number">1</kindInformation>
</kindInformations>
<description>
<rect x="0" y="-8" width="6" height="16" rx="0" ry="0" style="line-style:normal;line-weight:normal;filling:none;color:black" antialias="false"/>
<terminal uuid="{6a87c921-6f5d-4f5c-8673-228a6d13c500}" name="1" x="0" y="20" orientation="s" type="Generic"/>
<terminal uuid="{2904e5fa-6bbe-4127-acdd-f92feadd6e00}" name="2" x="0" y="-20" orientation="n" type="Generic"/>
</description>
</definition>
+8 -2060
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-195
View File
@@ -1,195 +0,0 @@
#!/usr/bin/env python3
# Copyright 2006-2026 The QElectroTech Team
# This file is part of QElectroTech.
#
# QElectroTech is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# QElectroTech is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
"""Record raw USB reports from a 3Dconnexion 3D mouse, for QElectroTech.
QElectroTech's Windows/macOS 3D mouse support reads the device directly
over USB, so it has to decode each device model's raw reports itself.
This records what your device sends while you make a few guided
movements, and saves it to one file you can attach to the discussion.
Nothing is sent anywhere.
sudo python3 spacemouse-capture.py # finds the device itself
sudo python3 spacemouse-capture.py --list # just show what it finds
sudo is needed because /dev/hidraw* is usually readable by root only.
spacenavd can keep running. If the recording comes out empty, stop it
(`sudo systemctl stop spacenavd`) and try again.
Only the standard library is used, so it runs on any Linux with Python 3.
"""
import argparse
import datetime
import glob
import json
import os
import platform
import select
import sys
import time
VENDORS = {0x046D: 'Logitech (older 3Dconnexion)', 0x256F: '3Dconnexion'}
STEPS = [
('rest', 'Do not touch the device.', 3),
('right', 'Push the cap to the RIGHT and hold it, then let go.', 4),
('left', 'Push the cap to the LEFT and hold it, then let go.', 4),
('away', 'Push the cap AWAY from you and hold it, then let go.', 4),
('toward', 'Pull the cap TOWARDS you and hold it, then let go.', 4),
('down', 'Press the cap DOWN and hold it, then let go.', 4),
('up', 'Lift the cap UP and hold it, then let go.', 4),
('twist_cw', 'TWIST the cap CLOCKWISE (seen from above) and hold, then let go.', 4),
('twist_ccw', 'TWIST the cap ANTICLOCKWISE and hold, then let go.', 4),
('tilt_away', 'TILT the cap AWAY from you and hold, then let go.', 4),
('tilt_right', 'TILT the cap to the RIGHT and hold, then let go.', 4),
('buttons', 'Press each button once, slowly, one at a time, in any order.', 15),
]
def find_devices():
"""Return [{hidraw, name, vendor, product, sysfs}] for 3Dconnexion devices."""
found = []
for sysdir in sorted(glob.glob('/sys/class/hidraw/hidraw*')):
try:
with open(os.path.join(sysdir, 'device', 'uevent')) as f:
uevent = dict(line.strip().split('=', 1) for line in f if '=' in line)
except OSError:
continue
# HID_ID=0003:0000256F:0000C635 (bus:vendor:product)
try:
_bus, vendor, product = (int(x, 16) for x in uevent.get('HID_ID', '').split(':'))
except ValueError:
continue
if vendor not in VENDORS:
continue
found.append({
'hidraw': '/dev/' + os.path.basename(sysdir),
'name': uevent.get('HID_NAME', '?'),
'vendor': '%04x' % vendor,
'product': '%04x' % product,
'sysfs': sysdir,
})
return found
def read_descriptor(sysdir):
try:
with open(os.path.join(sysdir, 'device', 'report_descriptor'), 'rb') as f:
return f.read().hex()
except OSError as e:
return 'unreadable: %s' % e
def record(fd, seconds):
"""Read every report arriving within `seconds`; return [[ms, hex], ...]."""
reports = []
start = time.monotonic()
while True:
left = seconds - (time.monotonic() - start)
if left <= 0:
return reports
ready, _, _ = select.select([fd], [], [], left)
if not ready:
continue
try:
data = os.read(fd, 64)
except BlockingIOError:
continue
if not data: # only a test FIFO with no writer does this
time.sleep(0.01)
continue
reports.append([round((time.monotonic() - start) * 1000, 1), data.hex()])
def main():
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
ap.add_argument('--list', action='store_true', help='only list matching devices')
ap.add_argument('--device', help='hidraw path, if more than one device is found')
ap.add_argument('--descriptor', help=argparse.SUPPRESS) # testing without a device
ap.add_argument('--yes', action='store_true', help=argparse.SUPPRESS) # no Enter prompts
ap.add_argument('-o', '--output', help='output file (default: spacemouse-capture-<product>.json)')
args = ap.parse_args()
devices = find_devices()
if args.list:
for d in devices:
print('%(hidraw)s %(vendor)s:%(product)s %(name)s' % d)
if not devices:
print('No 3Dconnexion device found under /sys/class/hidraw.')
return 0 if devices else 1
if args.device:
dev = next((d for d in devices if d['hidraw'] == args.device),
{'hidraw': args.device, 'name': '?', 'vendor': '?', 'product': '?', 'sysfs': None})
elif len(devices) == 1:
dev = devices[0]
elif not devices:
sys.exit('No 3Dconnexion device found. Is it plugged in? (try --list)')
else:
sys.exit('Several devices found, pick one with --device:\n' +
'\n'.join(' %(hidraw)s %(name)s' % d for d in devices))
if args.descriptor:
with open(args.descriptor, 'rb') as f:
descriptor = f.read().hex()
elif dev['sysfs']:
descriptor = read_descriptor(dev['sysfs'])
else:
descriptor = 'unknown'
try:
fd = os.open(dev['hidraw'], os.O_RDONLY | os.O_NONBLOCK)
except PermissionError:
sys.exit('Permission denied on %s -- run with sudo.' % dev['hidraw'])
print('Recording from %s (%s, %s:%s).' % (dev['hidraw'], dev['name'], dev['vendor'], dev['product']))
print('For each step, press Enter, do the movement, and wait for the next prompt.\n')
result = {
'tool': 'spacemouse-capture.py 1',
'date': datetime.datetime.now(datetime.timezone.utc).isoformat(timespec='seconds'),
'system': platform.platform(),
'device': {k: dev[k] for k in ('name', 'vendor', 'product')},
'report_descriptor': descriptor,
'steps': [],
}
try:
for i, (key, text, seconds) in enumerate(STEPS, 1):
print('[%d/%d] %s' % (i, len(STEPS), text))
if not args.yes:
input(' Press Enter to start (%d s)... ' % seconds)
reports = record(fd, seconds)
print(' %d reports recorded.\n' % len(reports))
result['steps'].append({'step': key, 'instruction': text, 'reports': reports})
except KeyboardInterrupt:
print('\nStopped early -- saving what was recorded so far.')
finally:
os.close(fd)
out = args.output or 'spacemouse-capture-%s.json' % dev['product']
with open(out, 'w') as f:
json.dump(result, f, indent=1)
total = sum(len(s['reports']) for s in result['steps'])
print('Saved %s (%d reports in total).' % (out, total))
if total == 0:
print('Nothing was recorded. Try stopping spacenavd first: sudo systemctl stop spacenavd')
else:
print('Please attach this file to discussion #599. Thank you!')
return 0
if __name__ == '__main__':
sys.exit(main())
@@ -49,14 +49,6 @@ ElementsCollectionModel::ElementsCollectionModel(QObject *parent) :
*/ */
ElementsCollectionModel::~ElementsCollectionModel() ElementsCollectionModel::~ElementsCollectionModel()
{ {
// Without cancel(), the wait below runs the whole queued
// QtConcurrent::map() to completion, so closing this dialog on a
// large collection blocks until every remaining item has been
// processed -- a visible hang on the button pressed precisely to
// stop the work. cancel() drops the not-yet-started items so the
// wait that follows (still needed, so an in-flight item can't
// dereference this object after it's gone) is short.
m_future.cancel();
m_future.waitForFinished(); m_future.waitForFinished();
} }
@@ -439,7 +431,7 @@ void ElementsCollectionModel::addLocation(const ElementsLocation& location)
collection_name); collection_name);
} }
} }
// Macros and Custom Collection are handled here // ANPASSUNG: Makros und Custom Collection werden hier behandelt!
else if (location.isCustomCollection() || location.isMacrosCollection()) { else if (location.isCustomCollection() || location.isMacrosCollection()) {
QList <ElementCollectionItem *> child_list; QList <ElementCollectionItem *> child_list;
@@ -503,10 +495,6 @@ void ElementsCollectionModel::addProject(QETProject *project, bool set_data)
connect(project->embeddedElementCollection(), connect(project->embeddedElementCollection(),
&XmlElementCollection::directoryRemoved, &XmlElementCollection::directoryRemoved,
this, &ElementsCollectionModel::itemRemovedFromCollection); this, &ElementsCollectionModel::itemRemovedFromCollection);
connect(project, &QETProject::projectTitleChanged,
this, &ElementsCollectionModel::projectNameChanged);
connect(project, &QETProject::projectFilePathChanged,
this, &ElementsCollectionModel::projectNameChanged);
} }
/** /**
@@ -538,10 +526,6 @@ void ElementsCollectionModel::removeProject(QETProject *project)
&XmlElementCollection::directoryRemoved, &XmlElementCollection::directoryRemoved,
this, this,
&ElementsCollectionModel::itemRemovedFromCollection); &ElementsCollectionModel::itemRemovedFromCollection);
disconnect(project, &QETProject::projectTitleChanged,
this, &ElementsCollectionModel::projectNameChanged);
disconnect(project, &QETProject::projectFilePathChanged,
this, &ElementsCollectionModel::projectNameChanged);
} }
} }
@@ -663,7 +647,7 @@ QModelIndex ElementsCollectionModel::indexFromLocation(
if (eci->type() == FileElementCollectionItem::Type) { if (eci->type() == FileElementCollectionItem::Type) {
if (FileElementCollectionItem *feci = static_cast<FileElementCollectionItem *>(eci)) { if (FileElementCollectionItem *feci = static_cast<FileElementCollectionItem *>(eci)) {
// Macro check added so the model finds the path in the tree // ANPASSUNG: Makro-Prüfung hinzugefügt, damit das Modell den Pfad im Baum findet!
if ( (location.isCommonCollection() && feci->isCommonCollection()) || if ( (location.isCommonCollection() && feci->isCommonCollection()) ||
(location.isCompanyCollection() && feci->isCompanyCollection()) || (location.isCompanyCollection() && feci->isCompanyCollection()) ||
(location.isMacrosCollection() && feci->isMacrosCollection()) || (location.isMacrosCollection() && feci->isMacrosCollection()) ||
@@ -782,15 +766,3 @@ void ElementsCollectionModel::updateItem(const QString& path)
eci->setUpData(); eci->setUpData();
} }
} }
/**
@brief ElementsCollectionModel::projectNameChanged
Update the displayed name of the collection of project,
when its title or its file path changed.
@param project
*/
void ElementsCollectionModel::projectNameChanged(QETProject *project)
{
if (XmlProjectElementCollectionItem *xpeci = m_project_hash.value(project))
xpeci->updateProjectName();
}
@@ -73,7 +73,6 @@ class ElementsCollectionModel : public QStandardItemModel
void elementIntegratedToCollection (const QString& path); void elementIntegratedToCollection (const QString& path);
void itemRemovedFromCollection (const QString& path); void itemRemovedFromCollection (const QString& path);
void updateItem (const QString& path); void updateItem (const QString& path);
void projectNameChanged (QETProject *project);
private: private:
QList <QETProject *> m_project_list; QList <QETProject *> m_project_list;
@@ -653,17 +653,10 @@ QDomElement ElementsLocation::xml() const
if (!m_project) if (!m_project)
{ {
QFile file (m_file_system_path); QFile file (m_file_system_path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return QDomElement();
QDomDocument docu; QDomDocument docu;
if (docu.setContent(&file)) if (docu.setContent(&file))
{
file.close();
return docu.documentElement(); return docu.documentElement();
} }
file.close();
}
else else
{ {
QString str = m_collection_path; QString str = m_collection_path;
@@ -20,7 +20,7 @@
#include "../NameList/nameslist.h" #include "../NameList/nameslist.h"
#include "../diagramcontext.h" #include "../diagramcontext.h"
#include "pugixml.hpp" #include "pugixml/src/pugixml.hpp"
#include <QIcon> #include <QIcon>
#include <QString> #include <QString>
@@ -162,7 +162,7 @@ QString FileElementCollectionItem::localName()
else if (m_path == QETApp::customElementsDirN()) else if (m_path == QETApp::customElementsDirN())
setText(QObject::tr("Collection utilisateur")); setText(QObject::tr("Collection utilisateur"));
else if (m_path == macrosPath) else if (m_path == macrosPath)
setText(QObject::tr("Macros")); setText(QObject::tr("Makros"));
else else
setText(QObject::tr("Collection inconnue")); setText(QObject::tr("Collection inconnue"));
} }
@@ -21,8 +21,6 @@
#include "../qetproject.h" #include "../qetproject.h"
#include "xmlelementcollection.h" #include "xmlelementcollection.h"
#include <QFileInfo>
/** /**
@brief XmlProjectElementCollectionItem::XmlProjectElementCollectionItem @brief XmlProjectElementCollectionItem::XmlProjectElementCollectionItem
Constructor Constructor
@@ -60,7 +58,10 @@ QString XmlProjectElementCollectionItem::localName()
return text(); return text();
if (isCollectionRoot()) { if (isCollectionRoot()) {
updateProjectName(); if (m_project->title().isEmpty())
setText(QObject::tr("Projet sans titre"));
else
setText(m_project->title());
} }
else { else {
ElementsLocation location (embeddedPath(), m_project); ElementsLocation location (embeddedPath(), m_project);
@@ -70,25 +71,6 @@ QString XmlProjectElementCollectionItem::localName()
return text(); return text();
} }
/**
@brief XmlProjectElementCollectionItem::updateProjectName
Set the displayed name of the collection root from the project :
its title, or its file name when it has no title, like the project panel.
Does nothing if this item is not the root of the collection.
*/
void XmlProjectElementCollectionItem::updateProjectName()
{
if (!isCollectionRoot() || !m_project)
return;
if (!m_project->title().isEmpty())
setText(m_project->title());
else if (!m_project->filePath().isEmpty())
setText(QFileInfo(m_project->filePath()).completeBaseName());
else
setText(QObject::tr("Projet sans titre"));
}
/** /**
@brief XmlProjectElementCollectionItem::name @brief XmlProjectElementCollectionItem::name
@return The collection name of this item @return The collection name of this item
@@ -45,7 +45,6 @@ class XmlProjectElementCollectionItem : public ElementCollectionItem
bool isCollectionRoot() const override; bool isCollectionRoot() const override;
void addChildAtPath(const QString &collection_name) override; void addChildAtPath(const QString &collection_name) override;
QETProject * project() const; QETProject * project() const;
void updateProjectName();
void setProject (QETProject *project, void setProject (QETProject *project,
bool set_data = true, bool set_data = true,
+1 -1
View File
@@ -17,7 +17,7 @@
*/ */
#ifndef NAMES_LIST_H #ifndef NAMES_LIST_H
#define NAMES_LIST_H #define NAMES_LIST_H
#include "pugixml.hpp" #include "pugixml/src/pugixml.hpp"
#include <QtXml> #include <QtXml>
/** /**
@@ -171,20 +171,20 @@ MoveTerminalCommand::MoveTerminalCommand(QSharedPointer<PhysicalTerminal> termin
QString text; QString text;
if (t_label.isEmpty()) { if (t_label.isEmpty()) {
if (strip_name.isEmpty() && new_strip_name.isEmpty()) if (strip_name.isEmpty() && new_strip_name.isEmpty())
text = QObject::tr("Déplacer une borne d'un groupe de bornes vers un autre groupe de bornes"); text = QObject::tr("Déplacer une borne d'un groupe de bornes vers un groupe de bornes");
else if (strip_name.isEmpty()) else if (strip_name.isEmpty())
text = QObject::tr("Déplacer une borne d'un groupe de bornes vers le groupe de bornes %1").arg(new_strip_name); text = QObject::tr("Déplacer une borne d'un groupe de bornes vers le groupe de bornes %1").arg(new_strip_name);
else if (new_strip_name.isEmpty()) else if (new_strip_name.isEmpty())
text = QObject::tr("Déplacer une borne du groupe de bornes %1 vers un autre groupe de bornes").arg(strip_name); text = QObject::tr("Déplacer une borne du groupe de bornes %1 vers un groupe de bornes").arg(strip_name);
else else
text = QObject::tr("Déplacer une borne du groupe de bornes %1 vers le groupe de bornes %2").arg(strip_name, new_strip_name); text = QObject::tr("Déplacer une borne du groupe de bornes %1 vers le groupe de bornes %2").arg(strip_name, new_strip_name);
} else { } else {
if (strip_name.isEmpty() && new_strip_name.isEmpty()) if (strip_name.isEmpty() && new_strip_name.isEmpty())
text = QObject::tr("Déplacer la borne %1 d'un groupe de bornes vers un autre groupe de bornes").arg(t_label); text = QObject::tr("Déplacer la borne %1 d'un groupe de bornes vers un groupe de bornes").arg(t_label);
else if (strip_name.isEmpty()) else if (strip_name.isEmpty())
text = QObject::tr("Déplacer la borne %1 d'un groupe de bornes vers le groupe de bornes %2").arg(t_label, new_strip_name); text = QObject::tr("Déplacer la borne %1 d'un groupe de bornes vers le groupe de bornes %2").arg(t_label, new_strip_name);
else if (new_strip_name.isEmpty()) else if (new_strip_name.isEmpty())
text = QObject::tr("Déplacer la borne %1 du groupe de bornes %2 vers un autre groupe de bornes").arg(t_label, strip_name); text = QObject::tr("Déplacer la borne %1 du groupe de bornes %2 vers un groupe de bornes").arg(t_label, strip_name);
else else
text = QObject::tr("Déplacer la borne %1 du groupe de bornes %2 vers le groupe de bornes %3").arg(t_label, strip_name, new_strip_name); text = QObject::tr("Déplacer la borne %1 du groupe de bornes %2 vers le groupe de bornes %3").arg(t_label, strip_name, new_strip_name);
} }
@@ -205,11 +205,11 @@ MoveTerminalCommand::MoveTerminalCommand(QVector<QSharedPointer<PhysicalTerminal
QString text; QString text;
if (strip_name.isEmpty() && new_strip_name.isEmpty()) if (strip_name.isEmpty() && new_strip_name.isEmpty())
text = QObject::tr("Déplacer %n borne(s) d'un groupe de bornes vers un autre groupe de bornes", "", count); text = QObject::tr("Déplacer %n borne(s) d'un groupe de bornes vers un groupe de bornes", "", count);
else if (strip_name.isEmpty()) else if (strip_name.isEmpty())
text = QObject::tr("Déplacer %n borne(s) d'un groupe de bornes vers le groupe de bornes %1", "", count).arg(new_strip_name); text = QObject::tr("Déplacer %n borne(s) d'un groupe de bornes vers le groupe de bornes %1", "", count).arg(new_strip_name);
else if (new_strip_name.isEmpty()) else if (new_strip_name.isEmpty())
text = QObject::tr("Déplacer %n borne(s) du groupe de bornes %1 vers un autre groupe de bornes", "", count).arg(strip_name); text = QObject::tr("Déplacer %n borne(s) du groupe de bornes %1 vers un groupe de bornes", "", count).arg(strip_name);
else else
text = QObject::tr("Déplacer %n borne(s) du groupe de bornes %1 vers le groupe de bornes %2", "", count).arg(strip_name, new_strip_name); text = QObject::tr("Déplacer %n borne(s) du groupe de bornes %1 vers le groupe de bornes %2", "", count).arg(strip_name, new_strip_name);
setText(text); setText(text);
@@ -46,11 +46,10 @@ FreeTerminalEditor::FreeTerminalEditor(QETProject *project, QWidget *parent) :
connect(m_project, &QObject::destroyed, this, &FreeTerminalEditor::reload); connect(m_project, &QObject::destroyed, this, &FreeTerminalEditor::reload);
} }
//Disable the move button while cells are edited (yellow), re-evaluate on every change //Disabled the move if the table is currently edited (yellow cell)
connect(m_model, &FreeTerminalModel::dataChanged, this, &FreeTerminalEditor::selectionChanged); connect(m_model, &FreeTerminalModel::dataChanged, this, [=] {
this->setDisabledMove();
connect(ui->m_table_view->selectionModel(), &QItemSelectionModel::selectionChanged, });
this, &FreeTerminalEditor::selectionChanged);
connect(ui->m_table_view, &QAbstractItemView::doubleClicked, this, [=](const QModelIndex &index) connect(ui->m_table_view, &QAbstractItemView::doubleClicked, this, [=](const QModelIndex &index)
{ {
@@ -103,7 +102,7 @@ void FreeTerminalEditor::reload()
QString str(strip->installation() + " " + strip->location() + " " + strip->name()); QString str(strip->installation() + " " + strip->location() + " " + strip->name());
ui->m_move_in_cb->addItem(str, strip->uuid()); ui->m_move_in_cb->addItem(str, strip->uuid());
} }
selectionChanged(); setDisabledMove(false);
} }
} }
@@ -274,19 +273,10 @@ void FreeTerminalEditor::on_m_move_pb_clicked()
reload(); reload();
} }
void FreeTerminalEditor::selectionChanged()
{
const bool has_selection = !ui->m_table_view->selectionModel()->selectedIndexes().isEmpty();
const bool has_pending = !m_model->modifiedModelRealTerminalData().isEmpty();
setDisabledMove(!has_selection || has_pending);
}
void FreeTerminalEditor::setDisabledMove(bool b) void FreeTerminalEditor::setDisabledMove(bool b)
{ {
ui->m_move_label->setDisabled(b); ui->m_move_label->setDisabled(b);
ui->m_move_in_cb->setDisabled(b); ui->m_move_in_cb->setDisabled(b);
ui->m_move_pb->setDisabled(b); ui->m_move_pb->setDisabled(b);
ui->m_move_pb->setToolTip(b ? tr("Appliquez ou annulez les modifications en cours avant de déplacer")
: tr("Déplacer les bornes sélectionnées vers le bornier choisi"));
} }
@@ -182,7 +182,7 @@ bool FreeTerminalModel::setData(const QModelIndex &index, const QVariant &value,
modified_ = true; modified_ = true;
modified_cell = FUNCTION_CELL; modified_cell = FUNCTION_CELL;
} }
else if (column_ == LED_CELL && mrtd.led_ != value.toBool()) else if (column_ == LED_CELL)
{ {
mrtd.led_ = value.toBool(); mrtd.led_ = value.toBool();
modified_ = true; modified_ = true;
@@ -1,78 +0,0 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "renumberelementscommand.h"
#include "../qetproject.h"
#include "../qetgraphicsitem/element.h"
RenumberElementsCommand::RenumberElementsCommand(
QETProject *project,
QVector<ElementChange> changes,
QHash<QString, NumerotationContext> old_ctx,
QHash<QString, NumerotationContext> new_ctx,
const QString &text)
: QUndoCommand(text)
, project_(project)
, changes_(std::move(changes))
, old_ctx_(std::move(old_ctx))
, new_ctx_(std::move(new_ctx))
{
}
void RenumberElementsCommand::undo()
{
apply(false);
}
void RenumberElementsCommand::redo()
{
// QUndoStack calls redo() once immediately after push(); keep standard behaviour.
if (first_redo_) {
first_redo_ = false;
}
apply(true);
}
void RenumberElementsCommand::apply(bool use_new)
{
if (!project_) return;
// Restore project contexts
const auto &ctx = use_new ? new_ctx_ : old_ctx_;
for (auto it = ctx.constBegin(); it != ctx.constEnd(); ++it) {
project_->addElementAutoNum(it.key(), it.value());
}
// Apply per-element changes
for (const ElementChange &c : changes_) {
if (!c.element) continue;
const bool frozen = use_new ? c.new_frozen : c.old_frozen;
const auto &infos = use_new ? c.new_infos : c.old_infos;
const auto &seq = use_new ? c.new_seq : c.old_seq;
// Temporarily unfreeze so that label/infos update correctly.
const bool was_frozen = c.element->isFreezeLabel();
if (was_frozen) c.element->freezeLabel(false);
c.element->rSequenceStruct() = seq;
c.element->setElementInformations(infos);
c.element->freezeLabel(frozen);
}
}
-70
View File
@@ -1,70 +0,0 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RENUMBERELEMENTSCOMMAND_H
#define RENUMBERELEMENTSCOMMAND_H
#include <QUndoCommand>
#include <QHash>
#include <QVector>
#include "../diagramcontext.h"
#include "assignvariables.h" // defines autonum::sequentialNumbers
class Element;
class QETProject;
/**
* @brief Undoable renumbering of element labels and sequence structs.
*/
class RenumberElementsCommand final : public QUndoCommand
{
public:
struct ElementChange
{
Element *element = nullptr;
DiagramContext old_infos;
DiagramContext new_infos;
autonum::sequentialNumbers old_seq;
autonum::sequentialNumbers new_seq;
bool old_frozen = false;
bool new_frozen = false;
};
RenumberElementsCommand(
QETProject *project,
QVector<ElementChange> changes,
QHash<QString, NumerotationContext> old_ctx,
QHash<QString, NumerotationContext> new_ctx,
const QString &text);
void undo() override;
void redo() override;
private:
void apply(bool use_new);
private:
QETProject *project_ = nullptr;
QVector<ElementChange> changes_;
QHash<QString, NumerotationContext> old_ctx_;
QHash<QString, NumerotationContext> new_ctx_;
bool first_redo_ = true;
};
#endif // RENUMBERELEMENTSCOMMAND_H
@@ -23,7 +23,6 @@
#include "formulaautonumberingw.h" #include "formulaautonumberingw.h"
#include "numparteditorw.h" #include "numparteditorw.h"
#include "qdebug.h" #include "qdebug.h"
#include "renumberelementsdialog.h"
#include "ui_autonumberingmanagementw.h" #include "ui_autonumberingmanagementw.h"
#include "ui_formulaautonumberingw.h" #include "ui_formulaautonumberingw.h"
@@ -49,8 +48,6 @@ AutoNumberingManagementW::AutoNumberingManagementW(QETProject *project,
ui->m_selected_folios_le->setDisabled(true); ui->m_selected_folios_le->setDisabled(true);
ui->m_selected_folios_le->setReadOnly(true); ui->m_selected_folios_le->setReadOnly(true);
ui->m_apply_project_rb->setChecked(true); ui->m_apply_project_rb->setChecked(true);
// Enabled only when project is in "Under Development" status and not read-only.
ui->m_renumber_elements_pb->setEnabled(ui->m_status_cb->currentIndex() == 0 && project_ && !project_->isReadOnly());
setProjectContext(); setProjectContext();
} }
@@ -90,7 +87,6 @@ void AutoNumberingManagementW::on_m_status_cb_currentIndexChanged(int index)
ui->m_both_conductor_rb->setChecked(true); ui->m_both_conductor_rb->setChecked(true);
ui->m_both_element_rb->setChecked(true); ui->m_both_element_rb->setChecked(true);
ui->m_both_folio_rb->setChecked(true); ui->m_both_folio_rb->setChecked(true);
ui->m_renumber_elements_pb->setEnabled(true);
} }
//Installing //Installing
else if (index == 1) { else if (index == 1) {
@@ -100,31 +96,15 @@ void AutoNumberingManagementW::on_m_status_cb_currentIndexChanged(int index)
ui->m_new_conductor_rb->setChecked(true); ui->m_new_conductor_rb->setChecked(true);
ui->m_new_element_rb->setChecked(true); ui->m_new_element_rb->setChecked(true);
ui->m_new_folio_rb->setChecked(true); ui->m_new_folio_rb->setChecked(true);
ui->m_renumber_elements_pb->setEnabled(true);
} }
//Built //Built
else if (index == 2) { else if (index == 2) {
ui->m_disable_conductor_rb->setChecked(true); ui->m_disable_conductor_rb->setChecked(true);
ui->m_disable_element_rb->setChecked(true); ui->m_disable_element_rb->setChecked(true);
ui->m_disable_folio_rb->setChecked(true); ui->m_disable_folio_rb->setChecked(true);
ui->m_renumber_elements_pb->setEnabled(false);
} }
} }
void AutoNumberingManagementW::on_m_renumber_elements_pb_clicked()
{
if (!project_ || project_->isReadOnly()) return;
// Only allowed during "Under Development"
// if (ui->m_status_cb->currentIndex() != 1) return;
QStringList titles = project_->elementAutoNum().keys();
titles.sort(Qt::CaseInsensitive);
RenumberElementsDialog dlg(titles, this);
if (dlg.exec() != QDialog::Accepted) return;
project_->renumberElementsBySchemeTitle(dlg.selectedSchemeTitle());
}
/** /**
@brief AutoNumberingManagementW::on_m_apply_folios_rb_clicked @brief AutoNumberingManagementW::on_m_apply_folios_rb_clicked
Set From Folios Combobox Set From Folios Combobox
@@ -50,7 +50,6 @@ class AutoNumberingManagementW : public QWidget
void on_m_from_folios_cb_currentIndexChanged(int); void on_m_from_folios_cb_currentIndexChanged(int);
void on_m_to_folios_cb_currentIndexChanged(int); void on_m_to_folios_cb_currentIndexChanged(int);
void on_m_status_cb_currentIndexChanged(int); void on_m_status_cb_currentIndexChanged(int);
void on_m_renumber_elements_pb_clicked();
void on_m_apply_folios_rb_clicked(); void on_m_apply_folios_rb_clicked();
void on_m_apply_project_rb_clicked(); void on_m_apply_project_rb_clicked();
void on_buttonBox_clicked(QAbstractButton *); void on_buttonBox_clicked(QAbstractButton *);
@@ -305,13 +305,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="m_renumber_elements_pb">
<property name="text">
<string>Renumber element(s)…</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer_2"> <spacer name="horizontalSpacer_2">
<property name="orientation"> <property name="orientation">
@@ -1,75 +0,0 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "renumberelementsdialog.h"
#include <QComboBox>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QRadioButton>
#include <QVBoxLayout>
RenumberElementsDialog::RenumberElementsDialog(const QStringList &scheme_titles, QWidget *parent)
: QDialog(parent)
, m_scheme_titles(scheme_titles)
{
setWindowTitle(tr("Renumber element(s)"));
setModal(true);
auto *vl = new QVBoxLayout(this);
auto *scope = new QGroupBox(tr("Scope"), this);
auto *scope_l = new QVBoxLayout(scope);
m_all_rb = new QRadioButton(tr("All schemes"), scope);
m_one_rb = new QRadioButton(tr("One scheme"), scope);
scope_l->addWidget(m_all_rb);
scope_l->addWidget(m_one_rb);
vl->addWidget(scope);
auto *form = new QFormLayout();
m_combo = new QComboBox(this);
m_combo->addItems(m_scheme_titles);
form->addRow(new QLabel(tr("Scheme:"), this), m_combo);
vl->addLayout(form);
m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
vl->addWidget(m_buttons);
m_all_rb->setChecked(true);
updateUi();
connect(m_all_rb, &QRadioButton::toggled, this, &RenumberElementsDialog::updateUi);
connect(m_one_rb, &QRadioButton::toggled, this, &RenumberElementsDialog::updateUi);
}
QString RenumberElementsDialog::selectedSchemeTitle() const
{
if (m_all_rb && m_all_rb->isChecked()) return QString();
return m_combo ? m_combo->currentText() : QString();
}
void RenumberElementsDialog::updateUi()
{
const bool one = m_one_rb && m_one_rb->isChecked();
if (m_combo) m_combo->setEnabled(one);
}
@@ -1,59 +0,0 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RENUMBERELEMENTSDIALOG_H
#define RENUMBERELEMENTSDIALOG_H
#include <QDialog>
#include <QStringList>
class QComboBox;
class QRadioButton;
class QDialogButtonBox;
/**
* @brief Simple dialog to pick renumbering scope for elements.
*
* The user can choose between:
* - all element autonumbering schemes
* - one selected scheme title
*/
class RenumberElementsDialog final : public QDialog
{
Q_OBJECT
public:
explicit RenumberElementsDialog(const QStringList &scheme_titles, QWidget *parent = nullptr);
/**
* @return Empty string if "all" is selected, otherwise the chosen scheme title.
*/
QString selectedSchemeTitle() const;
private slots:
void updateUi();
private:
QStringList m_scheme_titles;
QRadioButton *m_all_rb = nullptr;
QRadioButton *m_one_rb = nullptr;
QComboBox *m_combo = nullptr;
QDialogButtonBox *m_buttons = nullptr;
};
#endif // RENUMBERELEMENTSDIALOG_H
+2 -12
View File
@@ -896,20 +896,10 @@ void BorderTitleBlock::updateDiagramContextForTitleBlock(
// An empty page-level value means the variable was auto-added to the // An empty page-level value means the variable was auto-added to the
// folio's Custom tab (#495) but never actually set by the user, so it // folio's Custom tab (#495) but never actually set by the user, so it
// must not shadow a real project-level value of the same name (#531). // must not shadow a real project-level value of the same name (#531).
//
// That guard has to stop short of removing the key outright, though
// (#973). TitleBlockTemplate::interpreteVariables() only replaces a
// "%name"/"%{name}" placeholder when "name" is a key in this context at
// all -- an unset variable that never makes it in is left as its own
// literal placeholder text in the rendered title block, not blank.
// So an empty page-level value is skipped only when a real project-level
// one is already there to show through; otherwise it still goes in
// empty, which is what makes the placeholder resolve to nothing.
DiagramContext context = initial_context; DiagramContext context = initial_context;
foreach (QString key, additional_fields_.keys()) { foreach (QString key, additional_fields_.keys()) {
const QVariant value = additional_fields_[key]; if (!additional_fields_[key].toString().isEmpty())
if (!value.toString().isEmpty() || !context.contains(key)) context.addValue(key, additional_fields_[key]);
context.addValue(key, value);
} }
// ... overridden by the historical and/or dynamically generated fields // ... overridden by the historical and/or dynamically generated fields
-1
View File
@@ -227,7 +227,6 @@ int exportPdf(QETProject &project, const QString &output,
// Rewrite the URI link annotations into native internal GoTo actions, so // Rewrite the URI link annotations into native internal GoTo actions, so
// the cross-references jump inside the document in any PDF viewer. // the cross-references jump inside the document in any PDF viewer.
PdfLinks::convertUriToGoTo(output); PdfLinks::convertUriToGoTo(output);
PdfLinks::removeUnusedPdfxNamespace(output);
out << "Exported " << diagrams.size() << " page(s) -> " << output << "\n"; out << "Exported " << diagrams.size() << " page(s) -> " << output << "\n";
return 0; return 0;
+22 -70
View File
@@ -17,8 +17,6 @@
*/ */
#include "projectdatabase.h" #include "projectdatabase.h"
#include "sqlreadonly.h"
#include "../diagram.h" #include "../diagram.h"
#include "../diagramposition.h" #include "../diagramposition.h"
#include "../elementprovider.h" #include "../elementprovider.h"
@@ -30,15 +28,13 @@
#include "../qetproject.h" #include "../qetproject.h"
#include <QLocale> #include <QLocale>
#include <QFile>
#include <QRegularExpression> #include <QRegularExpression>
#include <QSqlDriver>
#include <QSqlError> #include <QSqlError>
#include <QSqlDriver>
#include <sqlite3.h> #include <sqlite3.h>
/** /**
@brief projectDataBase::projectDataBase @brief projectDataBase::projectDataBase
Default constructor Default constructor
@@ -212,12 +208,6 @@ bool projectDataBase::isReadOnlySelect(const QString &query, QString *error)
*/ */
QSqlQuery projectDataBase::newQuery(const QString &query, QString *error) { QSqlQuery projectDataBase::newQuery(const QString &query, QString *error) {
QString reason; QString reason;
// First gate: which kind of statement is acceptable here at all. A
// textual check is the right tool for that and the wrong tool for
// anything else -- see isReadOnlySelect()'s own comment. It is what
// keeps ATTACH, BEGIN and PRAGMA out, none of which SQLite itself
// considers writes.
if (!isReadOnlySelect(query, &reason)) { if (!isReadOnlySelect(query, &reason)) {
qWarning().noquote() << "projectDataBase::newQuery: rejected query:" << reason << "--" << query; qWarning().noquote() << "projectDataBase::newQuery: rejected query:" << reason << "--" << query;
if (error) { if (error) {
@@ -225,24 +215,6 @@ QSqlQuery projectDataBase::newQuery(const QString &query, QString *error) {
} }
return QSqlQuery(m_data_base); return QSqlQuery(m_data_base);
} }
// Second gate, and the one that actually enforces read-only: SQLite is
// asked about the statement it compiled, instead of the text being read
// for clues. The first gate cannot see through a CTE prefix --
// "WITH x AS (SELECT 1) DELETE FROM element" starts with WITH, contains
// no semicolon, and deletes every row. That matters beyond the
// custom-query box, because this path is reachable from a file: a
// <graphics_table>'s saved <query> is read straight out of the .qet by
// ProjectDBModel::fromXml() and executed by fillValue(), so opening or
// exporting a project someone else produced would have been enough.
if (!QETSql::isSingleReadOnlyStatement(sqliteHandle(&m_data_base), 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); return QSqlQuery(query, m_data_base);
} }
@@ -319,23 +291,10 @@ void projectDataBase::addElement(Element *element)
void projectDataBase::removeElement(Element *element) void projectDataBase::removeElement(Element *element)
{ {
m_content_changed = true; m_content_changed = true;
bool changed = false;
m_remove_element_query.bindValue(":uuid", element->uuid().toString()); m_remove_element_query.bindValue(":uuid", element->uuid().toString());
if (m_remove_element_query.exec()) { if(!m_remove_element_query.exec()) {
changed = true;
} else {
qDebug() << "projectDataBase::removeElement remove error : " << m_remove_element_query.lastError(); qDebug() << "projectDataBase::removeElement remove error : " << m_remove_element_query.lastError();
}
m_remove_element_info_query.bindValue(":uuid", element->uuid().toString());
if (m_remove_element_info_query.exec()) {
changed = true;
} else { } else {
qDebug() << "projectDataBase::removeElement remove element_info error : " << m_remove_element_info_query.lastError();
}
if (changed) {
emit dataBaseUpdated(); emit dataBaseUpdated();
} }
} }
@@ -1172,21 +1131,6 @@ void projectDataBase::prepareQuery()
m_remove_element_query = QSqlQuery(m_data_base); m_remove_element_query = QSqlQuery(m_data_base);
m_remove_element_query.prepare(remove_element); m_remove_element_query.prepare(remove_element);
//REMOVE ELEMENT INFO
//element_info has no ON DELETE CASCADE (foreign keys aren't
//enforced by this connection), so removeElement() must clear it
//explicitly. Without this, the row is orphaned under the removed
//element's uuid, and re-adding an element with that same uuid
//later -- undo of this same removal, or a redo replaying it --
//hits element_info's PRIMARY KEY constraint on element_uuid: the
//element re-add succeeds, but its element_info insert silently
//fails and is lost. removeDiagram()'s cascade already clears this
//table when a whole folio goes, but that does not run for a
//single element removed on its own.
QString remove_element_info("DELETE FROM element_info WHERE element_uuid=:uuid");
m_remove_element_info_query = QSqlQuery(m_data_base);
m_remove_element_info_query.prepare(remove_element_info);
//UPDATE ELEMENT INFO //UPDATE ELEMENT INFO
QString update_str("UPDATE element_info SET "); QString update_str("UPDATE element_info SET ");
for (auto string : QETInformation::elementInfoKeys()) { for (auto string : QETInformation::elementInfoKeys()) {
@@ -1301,6 +1245,7 @@ void projectDataBase::bindDiagramInfoValues(QSqlQuery &query, Diagram *diagram)
} }
} }
#ifdef QET_EXPORT_PROJECT_DB
/** /**
@brief projectDataBase::sqliteHandle @brief projectDataBase::sqliteHandle
@param db @param db
@@ -1318,7 +1263,6 @@ sqlite3 *projectDataBase::sqliteHandle(QSqlDatabase *db)
return handle; return handle;
} }
#ifdef QET_EXPORT_PROJECT_DB
/** /**
* @brief projectDataBase::exportDb * @brief projectDataBase::exportDb
@@ -1354,19 +1298,27 @@ void projectDataBase::exportDb(projectDataBase *db,
return; return;
} }
// VACUUM INTO requires the destination not to exist. QFileDialog may ask QString connection_name("export_project_db_" % db->project()->uuid().toString());
// about overwriting, but it does not remove the existing file for us.
if (QFile::exists(path_) && !QFile::remove(path_)) { if (true) //Enter in a scope only to nicely use QSqlDatabase::removeDatabase just after the end of the scope
qWarning() << "Unable to replace project database export:" << path_; {
auto file_db = QSqlDatabase::addDatabase("QSQLITE", connection_name);
file_db.setDatabaseName(path_);
if (!file_db.open()) {
return; return;
} }
// VACUUM INTO creates a standalone copy of the current database without auto memory_db_handle = sqliteHandle(&db->m_data_base);
// requiring access to the SQLite driver's native connection handle. auto file_db_handle = sqliteHandle(&file_db);
const auto escaped_path = path_.replace("'", "''");
QSqlQuery query(db->m_data_base); auto sqlite_backup = sqlite3_backup_init(file_db_handle, "main", memory_db_handle, "main");
if (!query.exec("VACUUM INTO '" % escaped_path % "'")) { if (sqlite_backup)
qWarning() << "Unable to export project database:" << query.lastError().text(); {
sqlite3_backup_step(sqlite_backup, -1);
sqlite3_backup_finish(sqlite_backup);
} }
file_db.close();
}
QSqlDatabase::removeDatabase(connection_name);
} }
#endif #endif
+2 -28
View File
@@ -29,7 +29,7 @@ class QETProject;
class Diagram; class Diagram;
class Conductor; class Conductor;
class Terminal; class Terminal;
struct sqlite3; class sqlite3;
/** /**
@brief The projectDataBase class @brief The projectDataBase class
@@ -61,26 +61,6 @@ class projectDataBase : public QObject
QETProject *project() const; QETProject *project() const;
QSqlQuery newQuery(const QString &query = QString(), QString *error = nullptr); QSqlQuery newQuery(const QString &query = QString(), QString *error = nullptr);
static bool isReadOnlySelect(const QString &query, QString *error = nullptr); static bool isReadOnlySelect(const QString &query, QString *error = nullptr);
/**
The most rows any caller reads out of one query result.
A SELECT is not bounded by how much data the project holds:
SQLite produces rows lazily, so a query that never stops
producing them makes the loop that reads them never stop
either. A recursive CTE does exactly that in one line, and
a <graphics_table>'s <query> is stored in the .qet and run
on load -- so the text can arrive from a file rather than
from the person at the keyboard, and opening that file is
the whole attack.
100000 is far above any real result: the largest table in
the shipped examples is 396 rows. It is a backstop, not a
page size -- a caller that hits it has almost certainly
been handed something it should not run to completion, and
says so rather than truncating quietly.
*/
static constexpr int MaxResultRows = 100000;
QSqlDatabase database() const {return m_data_base;} QSqlDatabase database() const {return m_data_base;}
int excludedConductorCount() const; int excludedConductorCount() const;
@@ -139,7 +119,6 @@ class projectDataBase : public QObject
QSqlQuery m_insert_elements_query, QSqlQuery m_insert_elements_query,
m_insert_element_info_query, m_insert_element_info_query,
m_remove_element_query, m_remove_element_query,
m_remove_element_info_query,
m_update_element_query, m_update_element_query,
m_insert_diagram_query, m_insert_diagram_query,
m_remove_diagram_query, m_remove_diagram_query,
@@ -156,14 +135,9 @@ class projectDataBase : public QObject
m_cascade_remove_conductor_query, m_cascade_remove_conductor_query,
m_cascade_remove_element_query; m_cascade_remove_element_query;
public:
// Deliberately outside the QET_EXPORT_PROJECT_DB guard below:
// newQuery() needs the raw connection to ask SQLite whether a
// query only reads, and that check runs in every build.
static sqlite3 *sqliteHandle(QSqlDatabase *db);
#ifdef QET_EXPORT_PROJECT_DB #ifdef QET_EXPORT_PROJECT_DB
public: public:
static sqlite3 *sqliteHandle(QSqlDatabase *db);
static void exportDb(projectDataBase *db, static void exportDb(projectDataBase *db,
QWidget *parent = nullptr, QWidget *parent = nullptr,
const QString &caption = QString(), const QString &caption = QString(),
-134
View File
@@ -1,134 +0,0 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sqlreadonly.h"
#include <QCoreApplication>
#include <sqlite3.h>
namespace QETSql {
/**
@brief QETSql::isSingleReadOnlyStatement
Ask SQLite itself whether @p query is exactly one statement, and whether
that statement only reads.
Why SQLite is asked rather than the text inspected: a check on the
query's first keyword cannot see what the statement actually does.
SQLite has allowed a CTE prefix in front of a data-modifying statement
since 3.8.3, so
@code
WITH x AS (SELECT 1) DELETE FROM element
@endcode
begins with WITH, contains no semicolon, and deletes every row.
sqlite3_stmt_readonly() reports on the statement SQLite compiled, not
on how it was spelled, so the same query is correctly refused here
while an ordinary WITH ... SELECT still passes.
The statement is compiled and immediately finalised; sqlite3_prepare_v2()
does not run it, so nothing is executed to reach this verdict.
This is a read-only test, NOT a statement-type allowlist: SQLite
considers ATTACH, BEGIN and several PRAGMAs read-only too, because none
of them change the contents of the database. Callers that need to
restrict which *kind* of statement is acceptable must say so separately
-- projectDataBase::newQuery() keeps isReadOnlySelect() in front of this
for exactly that reason.
@param handle the connection the query would run on. A null handle is
refused rather than waved through: without it there is nothing to ask,
and guessing from the text is the weakness this exists to replace.
@param query the raw SQL text
@param error set to a human-readable reason when this returns false
@return true if @p query is a single, read-only statement
*/
bool isSingleReadOnlyStatement(sqlite3 *handle, const QString &query, QString *error)
{
if (error) {
error->clear();
}
if (!handle) {
if (error) {
*error = QCoreApplication::translate("QETSql",
"Impossible de vérifier la requête : "
"aucune connexion SQLite disponible.");
}
return false;
}
const QByteArray utf8 = query.toUtf8();
sqlite3_stmt *statement = nullptr;
const char *tail = nullptr;
if (sqlite3_prepare_v2(handle, utf8.constData(), utf8.size(),
&statement, &tail) != SQLITE_OK)
{
if (error) {
*error = QCoreApplication::translate("QETSql",
"Requête SQL invalide : %1")
.arg(QString::fromUtf8(sqlite3_errmsg(handle)));
}
sqlite3_finalize(statement);
return false;
}
// Whitespace or a bare comment compiles successfully to no statement
// at all, and sqlite3_stmt_readonly() must not be handed that.
if (!statement) {
if (error) {
*error = QCoreApplication::translate("QETSql",
"La requête ne contient aucune instruction.");
}
return false;
}
const bool read_only = sqlite3_stmt_readonly(statement) != 0;
sqlite3_finalize(statement);
if (!read_only) {
if (error) {
*error = QCoreApplication::translate("QETSql",
"Seules les requêtes en lecture seule sont autorisées : "
"cette requête modifierait la base de données.");
}
return false;
}
// tail points just past the first statement, semicolon included.
// Anything left once semicolons and spacing are stripped is a second
// statement -- caught structurally here, where "SELECT ';'" is a
// perfectly ordinary query rather than a suspicious string.
if (tail) {
QString rest = QString::fromUtf8(tail);
rest.remove(QLatin1Char(';'));
if (!rest.trimmed().isEmpty()) {
if (error) {
*error = QCoreApplication::translate("QETSql",
"Une seule requête est autorisée.");
}
return false;
}
}
return true;
}
} // namespace QETSql
-43
View File
@@ -1,43 +0,0 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SQLREADONLY_H
#define SQLREADONLY_H
#include <QString>
struct sqlite3;
/**
Deciding whether a piece of SQL only reads.
Deliberately its own translation unit, depending on nothing but QString
and SQLite: it is the enforcement point for every query QElectroTech
runs against a project database, including queries that arrive from
outside the application (a .qet file's saved report/table query), so it
is worth being able to test it in isolation -- see
tests/qttest/tst_sqlreadonly.cpp, which links this file and nothing
else of QElectroTech.
*/
namespace QETSql {
bool isSingleReadOnlyStatement(sqlite3 *handle,
const QString &query,
QString *error = nullptr);
}
#endif // SQLREADONLY_H
+30 -52
View File
@@ -42,9 +42,7 @@
#include "qetinformation.h" #include "qetinformation.h"
#include "qetproject.h" #include "qetproject.h"
#include "diagramsortkeys.h" #include "diagramsortkeys.h"
#include <QTextStream>
#include <algorithm> #include <algorithm>
#include <climits>
#include <cassert> #include <cassert>
#include <math.h> #include <math.h>
@@ -101,43 +99,6 @@ namespace {
QString b = terminalSortKey(cond->terminal2); QString b = terminalSortKey(cond->terminal2);
return (a <= b) ? (a + QLatin1Char('>') + b) : (b + QLatin1Char('>') + a); return (a <= b) ? (a + QLatin1Char('>') + b) : (b + QLatin1Char('>') + a);
} }
/// Serialize @p items and append them to a new @p tag block of @p root,
/// in stacking order (@p stack_rank). items() cannot be trusted for
/// this: with NoIndex, the first removeItem() on the scene sorts Qt's
/// item list by pointer address, so from then on items() hands them over
/// in a per-run order (bugtracker #343). Stacking order is what reloading
/// the file rebuilds, so the drawing is unchanged and a resave is stable.
template <typename T>
void appendInStackingOrder(QDomDocument &document, QDomElement &root,
const QString &tag, const QVector<T *> &items,
const QHash<const QGraphicsItem *, int> &stack_rank)
{
if (items.isEmpty())
return;
struct Entry { int rank; QString xml_text; QDomElement xml; };
QVector<Entry> sorted;
for (T *item : items) {
Entry entry{stack_rank.value(item, INT_MAX), QString(),
item->toXml(document)};
// Only an item the stacking query missed needs a tiebreak.
if (entry.rank == INT_MAX) {
QTextStream stream(&entry.xml_text);
entry.xml.save(stream, 0);
}
sorted.append(entry);
}
std::stable_sort(sorted.begin(), sorted.end(),
[](const Entry &a, const Entry &b) {
return a.rank != b.rank ? a.rank < b.rank
: a.xml_text < b.xml_text;
});
auto block = document.createElement(tag);
for (const auto &entry : sorted)
block.appendChild(entry.xml);
root.appendChild(block);
}
} }
int Diagram::xGrid = 10; int Diagram::xGrid = 10;
@@ -1262,20 +1223,37 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
dom_root.appendChild(dom_conductors); dom_root.appendChild(dom_conductors);
} }
// A rect query, unlike items(), returns true stacking order (z, then if (!list_texts.isEmpty()) {
// insertion order) even with NoIndex. auto dom_texts = document.createElement(QStringLiteral("inputs"));
QHash<const QGraphicsItem *, int> stack_rank; for (auto dti : list_texts) {
{ dom_texts.appendChild(dti->toXml(document));
const QList<QGraphicsItem *> stacked = items( }
QRectF(-1e9, -1e9, 2e9, 2e9), Qt::IntersectsItemBoundingRect, dom_root.appendChild(dom_texts);
Qt::AscendingOrder); }
for (int i = 0 ; i < stacked.size() ; ++i)
stack_rank.insert(stacked.at(i), i); if (!list_images.isEmpty()) {
auto dom_images = document.createElement(QStringLiteral("images"));
for (auto dii : list_images) {
dom_images.appendChild(dii->toXml(document));
}
dom_root.appendChild(dom_images);
}
if (!list_shapes.isEmpty()) {
auto dom_shapes = document.createElement(QStringLiteral("shapes"));
for (auto dii : list_shapes) {
dom_shapes.appendChild(dii -> toXml(document));
}
dom_root.appendChild(dom_shapes);
}
if (table_vector.size()) {
auto tables = document.createElement(QStringLiteral("tables"));
for (auto table : table_vector) {
tables.appendChild(table->toXml(document));
}
dom_root.appendChild(tables);
} }
appendInStackingOrder(document, dom_root, QStringLiteral("inputs"), list_texts, stack_rank);
appendInStackingOrder(document, dom_root, QStringLiteral("images"), list_images, stack_rank);
appendInStackingOrder(document, dom_root, QStringLiteral("shapes"), list_shapes, stack_rank);
appendInStackingOrder(document, dom_root, QStringLiteral("tables"), table_vector, stack_rank);
if (!strip_vector.isEmpty()) { if (!strip_vector.isEmpty()) {
dom_root.appendChild(TerminalStripItemXml::toXml(strip_vector, document)); dom_root.appendChild(TerminalStripItemXml::toXml(strip_vector, document));
+1 -17
View File
@@ -77,23 +77,6 @@ void PasteDiagramCommand::redo()
{ {
first_redo = false; first_redo = false;
//Resolve a linked master/slave pair pasted together (bugtracker
//#607) before anything below renews their uuids: at this exact
//moment a pasted element's tmp_uuids_link still holds its
//source's original partner uuid, which still equals the
//not-yet-renewed uuid of that partner's own pasted copy if it
//was carried along in the same batch. Scoped to this batch only
//(not a project-wide search), so a pair pasted together links to
//each other and not to an original element left elsewhere that
//happens to still carry that same soon-to-be-replaced uuid. If
//only one half of a linked group was pasted, its link entry
//simply finds no match here and is dropped -- same "leave it
//unlinked" outcome as always.
const QList <Element *> elmts_list = content.m_elements;
for (Element *e : elmts_list) {
e->initLink(elmts_list);
}
//make new uuid for every pasted conductor, because old uuid are //make new uuid for every pasted conductor, because old uuid are
//the uuid of the copied conductor //the uuid of the copied conductor
const QList <Conductor *> all_pasted_conductors = content.conductors(); const QList <Conductor *> all_pasted_conductors = content.conductors();
@@ -102,6 +85,7 @@ void PasteDiagramCommand::redo()
} }
//this is the first paste, we do some actions for the new element //this is the first paste, we do some actions for the new element
const QList <Element *> elmts_list = content.m_elements;
for (Element *e : elmts_list) for (Element *e : elmts_list)
{ {
//make new uuid, because old uuid are the uuid of the copied element //make new uuid, because old uuid are the uuid of the copied element
+3 -11
View File
@@ -151,8 +151,8 @@ bool DiagramContext::operator!=(const DiagramContext &dc) const
void DiagramContext::toXml(QDomElement &e, const QString &tag_name) const void DiagramContext::toXml(QDomElement &e, const QString &tag_name) const
{ {
foreach (QString key, keys()) { foreach (QString key, keys()) {
const QString raw = m_content[key].toString(); if ((tag_name == "elementInformation") &&
if ((tag_name == "elementInformation") && raw.trimmed().isEmpty()) { (m_content[key].toString().trimmed().isEmpty())) {
continue; continue;
} }
QDomElement property = e.ownerDocument().createElement(tag_name); QDomElement property = e.ownerDocument().createElement(tag_name);
@@ -161,15 +161,7 @@ void DiagramContext::toXml(QDomElement &e, const QString &tag_name) const
property.removeAttribute("name"); property.removeAttribute("name");
property.setAttribute("show", m_content_show[key]); property.setAttribute("show", m_content_show[key]);
property.setAttribute("name", key); property.setAttribute("name", key);
// Trim stray leading/trailing whitespace around real content, but QDomText value = e.ownerDocument().createTextNode(m_content[key].toString().trimmed());
// not a value that IS whitespace: unconditionally trimming an
// all-whitespace string collapses it to "", which is silently
// indistinguishable from a value that was never set. A title-block
// custom variable set to a single space -- a workaround for #973,
// where an unset variable renders as its own literal placeholder --
// would otherwise vanish on the very next save.
const QString stored = raw.trimmed().isEmpty() ? raw : raw.trimmed();
QDomText value = e.ownerDocument().createTextNode(stored);
property.appendChild(value); property.appendChild(value);
e.appendChild(property); e.appendChild(property);
} }
+1 -1
View File
@@ -17,7 +17,7 @@
*/ */
#ifndef DIAGRAM_CONTEXT_H #ifndef DIAGRAM_CONTEXT_H
#define DIAGRAM_CONTEXT_H #define DIAGRAM_CONTEXT_H
#include "pugixml.hpp" #include "pugixml/src/pugixml.hpp"
#include <QDomElement> #include <QDomElement>
#include <QHash> #include <QHash>
+6 -69
View File
@@ -117,16 +117,7 @@ void DiagramEventAddImage::mousePressEvent(QGraphicsSceneMouseEvent *event)
} }
else if (m_image && !m_pressed && event->button() == Qt::RightButton) else if (m_image && !m_pressed && event->button() == Qt::RightButton)
{ {
// rotationAngle()/setRotationAngle(), not QGraphicsItem's own m_image->setRotation(m_image->rotation() + 90);
// rotation()/setRotation(): DiagramImageItem's whole handle/undo/
// XML-save machinery reads exclusively from its own m_transform
// (see diagramimageitem.cpp's toXml() comment) and never looks at
// QGraphicsItem's built-in convenience property at all -- using
// it here left a rotation that displayed correctly in this tool
// but silently vanished the moment the item was saved and
// reloaded, and additionally desynced the rotate-handle's pivot
// math once the image was later selected for editing.
m_image->setRotationAngle(m_image->rotationAngle() + 90);
event->setAccepted(true); event->setAccepted(true);
} }
} }
@@ -176,22 +167,6 @@ void DiagramEventAddImage::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
} }
else else
{ {
// setPivot(QPointF(0, 0)), not left at its default
// boundingRect().center(): DiagramImageItem's own m_transform
// is pivot-centered, so with the default center pivot the
// setPos(qMin(...)) below would no longer land on the image's
// actual top-left corner the moment scale != 1 (the corner
// only coincides with pos() when the pivot is the local
// origin). Anchoring here to (0, 0) -- exactly the same
// temporary-anchor trick DiagramImageItem::handlerMousePressEvent()
// already uses for its own Resize handles -- makes pos()
// keep meaning "scene position of the top-left corner"
// regardless of scale, so the qMin(...) line below still
// needs no change at all. setPivot() itself is a no-op past
// the first call (same pivot value), and compensates pos()
// automatically so nothing visibly jumps at the switch.
m_image->setPivot(QPointF(0, 0));
const QSizeF naturalSize = m_image->boundingRect().size(); const QSizeF naturalSize = m_image->boundingRect().size();
if (naturalSize.width() > 0 && naturalSize.height() > 0) if (naturalSize.width() > 0 && naturalSize.height() > 0)
{ {
@@ -204,12 +179,7 @@ void DiagramEventAddImage::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
// free-form one -- breaking aspect ratio on purpose is // free-form one -- breaking aspect ratio on purpose is
// its own, separate, larger piece of work. // its own, separate, larger piece of work.
const qreal newScale = qBound(0.01, qMax(scaleX, scaleY), 50.0); const qreal newScale = qBound(0.01, qMax(scaleX, scaleY), 50.0);
// scaleFactorX()/scaleFactorY(), not QGraphicsItem's own m_image->setScale(newScale);
// scale(): see the mousePressEvent right-click rotate
// comment above for why -- identical reasoning, identical
// fix.
m_image->setScaleFactorX(newScale);
m_image->setScaleFactorY(newScale);
} }
m_image->setPos(qMin(m_press_pos.x(), pos.x()), qMin(m_press_pos.y(), pos.y())); m_image->setPos(qMin(m_press_pos.x(), pos.x()), qMin(m_press_pos.y(), pos.y()));
} }
@@ -236,17 +206,6 @@ void DiagramEventAddImage::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{ {
if (m_image && m_pressed && event->button() == Qt::LeftButton) if (m_image && m_pressed && event->button() == Qt::LeftButton)
{ {
// Undo whatever temporary origin-anchoring mouseMoveEvent's
// resize-drag branch did (a no-op if it never engaged): every
// other DiagramImageItem code path -- handles, undo, XML save --
// expects an image's pivot to be its bounding-rect center unless
// the user deliberately customized it, exactly like a completed
// handle-resize already resets it via resetPivotToBoundingRectCenter().
// Doing this before reading pos() below is what makes the pushed
// command capture the final, center-pivot position rather than
// the drag's temporary corner-anchored one.
m_image->setPivot(m_image->boundingRect().center());
m_diagram->undoStack().push(new AddGraphicsObjectCommand(m_image, m_diagram, m_image->pos())); m_diagram->undoStack().push(new AddGraphicsObjectCommand(m_image, m_diagram, m_image->pos()));
for (QGraphicsView *view : m_diagram->views()) { for (QGraphicsView *view : m_diagram->views()) {
@@ -287,32 +246,10 @@ void DiagramEventAddImage::wheelEvent(QGraphicsSceneWheelEvent *event)
return; return;
} }
// scaleFactorX()/scaleFactorY(), not QGraphicsItem's own scale(): see qreal scaling = m_image->scale();
// the right-click rotate comment in mousePressEvent for why. Wheel- event->delta() > 1? scaling += 0.01 : scaling -= 0.01;
// scaling only ever runs while !m_pressed (guarded above), i.e. before if (scaling>0.01 && scaling <= 2) {
// any drag-resize has anchored the pivot to the origin (see m_image->setScale(scaling);
// mouseMoveEvent), so the pivot here is still the default
// boundingRect().center() and this scales the image in place around
// its own middle, exactly like before.
//
// Step each axis from its own current value rather than reading X and
// writing it back to both: scaleFactorX and scaleFactorY cannot
// actually differ at this point today (every other mutator in this
// class -- the drag-resize branch above, and this same wheelEvent --
// only ever sets them to the same value, and mouseReleaseEvent commits
// and ends this tool on any left-button release, so a handle-based
// non-uniform resize can never happen first and leave this instance
// still alive). Stepping both from their own value rather than
// collapsing Y to X costs nothing today and removes the trap if that
// invariant ever stops holding.
qreal scalingX = m_image->scaleFactorX();
qreal scalingY = m_image->scaleFactorY();
const qreal step = event->delta() > 1 ? 0.01 : -0.01;
scalingX += step;
scalingY += step;
if (scalingX > 0.01 && scalingX <= 2 && scalingY > 0.01 && scalingY <= 2) {
m_image->setScaleFactorX(scalingX);
m_image->setScaleFactorY(scalingY);
} }
event->setAccepted(true); event->setAccepted(true);
+28 -56
View File
@@ -22,7 +22,6 @@
#include "../qetapp.h" #include "../qetapp.h"
#include "../qetdiagrameditor.h" #include "../qetdiagrameditor.h"
#include "../qetgraphicsitem/conductor.h" #include "../qetgraphicsitem/conductor.h"
#include "../qetproject.h"
#include <QSettings> #include <QSettings>
@@ -52,50 +51,28 @@
QDomDocument document_xml; QDomDocument document_xml;
if (!document_xml.setContent(clipboard_text)) return; if (!document_xml.setContent(clipboard_text)) return;
//Batch the database work the same way project loading does
//(QETProject::readProjectXml): without this, every addItem()
//below emits dataBaseUpdated(), which makes each connected
//table model re-run its full SQL query -- ~77 queries for a
//typical paste, i.e. the multi-second stall on Ctrl+V.
auto *db = m_diagram->project() ? m_diagram->project()->dataBase() : nullptr;
if (db) {
db->blockSignals(true);
db->setUpdateBlocked(true);
}
//Load items at their original XML coordinates. //Load items at their original XML coordinates.
m_diagram->fromXml(document_xml, QPointF(), false, &m_content); m_diagram->fromXml(document_xml, QPointF(), false, &m_content);
if (db) {
db->blockSignals(false);
db->setUpdateBlocked(false);
db->updateDB();
}
if (!m_content.count()) return; if (!m_content.count()) return;
const QList<QGraphicsItem *> movable = m_content.items(MovableItems); const QList<QGraphicsItem *> movable = m_content.items(MovableItems);
if (movable.isEmpty()) return; if (movable.isEmpty()) return;
//Compute the top-left of all items' actual on-screen bounding //Compute the top-left of all items' positions (not bounding
//boxes (not their raw pos()) and snap to grid: this is the point //rects) and snap to grid: this is the point that gets placed
//that gets placed under the cursor, and the baseline moveTo() //under the cursor, and the baseline moveTo() measures from.
//measures from. mapToScene(boundingRect()) matters here, not QPointF top_left;
//pos() alone: pos() is the scene location of an item's local bool first = true;
//origin, but for anything with a pivot-centered transform (a
//scaled or rotated image, in particular) that origin can sit far
//from where the item is actually drawn -- pivot + scale*(0 -
//pivot) is nowhere near (0, 0) once scale is well under 1. Using
//pos() here silently pasted content at the right *delta* from a
//point that wasn't actually where the content visually was,
//producing a constant, scale-dependent offset between the cursor
//and the pasted picture. Diagram::fromXml()'s own position
//parameter already gets this right the same way, for the same
//reason.
QRectF items_rect;
for (auto *item : movable) { for (auto *item : movable) {
items_rect = items_rect.united(item->mapToScene(item->boundingRect()).boundingRect()); const QPointF p = item->pos();
if (first) {
top_left = p;
first = false;
} else {
if (p.x() < top_left.x()) top_left.setX(p.x());
if (p.y() < top_left.y()) top_left.setY(p.y());
}
} }
const QPointF top_left = items_rect.topLeft();
QSettings settings; QSettings settings;
const int xGrid = settings.value(QStringLiteral("diagrameditor/Xgrid"), const int xGrid = settings.value(QStringLiteral("diagrameditor/Xgrid"),
Diagram::xGrid).toInt(); Diagram::xGrid).toInt();
@@ -106,40 +83,35 @@
qRound(p.x() / xGrid) * xGrid, qRound(p.x() / xGrid) * xGrid,
qRound(p.y() / yGrid) * yGrid); qRound(p.y() / yGrid) * yGrid);
}; };
const QPointF grid_origin = snapGrid(start_pos); const QPointF grid_origin = snapGrid(top_left);
//Land the pasted content under the cursor immediately, rather than //Move the group to the cursor, rather than the cursor to the
//leaving it at the copied source's own coordinates: fromXml() above //group. Both put the copy under the pointer, but warping the
//loads items at their original position purely because it doesn't //pointer also drags it back to the original's position, so the
//know the target yet, not because that is where a paste should end //copy appears exactly on top of what was copied until the mouse
//up. The previous approach instead left items there and warped the //is moved -- which is the thing pasting under the cursor was
//OS cursor to match -- QCursor::setPos() is silently ignored by //meant to avoid (issue #913). Taking the pointer away from
//many window managers and compositors (Wayland in particular), so //where the user put it is also its own surprise.
//on any of those the warp simply never happened and the paste was m_group_origin = snapGrid(start_pos);
//left wherever it had originally been copied from, which could be const QPointF offset = m_group_origin - grid_origin;
//anywhere on the folio -- exactly the "far from the cursor" bug.
const QPointF initial_delta = grid_origin - snapGrid(top_left);
for (auto *item : movable) {
item->setPos(item->pos() + initial_delta);
}
//Store each item's now-placed position. moveTo() applies a //Store each item's position after the move. moveTo() applies a
//grid-snapped delta from the baseline to these, so items //grid-snapped delta from the baseline to these, so items
//preserve their layout and move in whole grid steps. //preserve their layout and move in whole grid steps.
for (auto *item : movable) { for (auto *item : movable) {
item->setPos(item->pos() + offset);
m_relative_pos.insert(item, item->pos()); m_relative_pos.insert(item, item->pos());
} }
m_group_origin = grid_origin;
//The conductors were laid out against the original terminal //The conductors were laid out against the old terminal
//positions, so re-route them before anything is drawn. //positions, so re-route them before anything is drawn.
const QList<Conductor *> conductors = m_content.conductors(DiagramContent::AnyConductor); const QList<Conductor *> conductors = m_content.conductors(DiagramContent::AnyConductor);
for (auto *conductor : conductors) { for (auto *conductor : conductors) {
conductor->updatePath(); conductor->updatePath();
} }
//The baseline is the group's grid-snapped origin, so moveTo() //The baseline is known now, so moveTo() does not have to
//does not have to capture one from the first mouse movement. //capture one from the first mouse movement.
m_initial_cursor = m_group_origin; m_initial_cursor = m_group_origin;
m_baseline_captured = true; m_baseline_captured = true;
+6 -30
View File
@@ -84,10 +84,7 @@ void DiagramEventAddPdf::mousePressEvent(QGraphicsSceneMouseEvent *event)
} }
else if (m_image && event->button() == Qt::RightButton) else if (m_image && event->button() == Qt::RightButton)
{ {
// rotationAngle()/setRotationAngle(), not QGraphicsItem's own m_image->setRotation(m_image->rotation() + 90);
// rotation()/setRotation(): see DiagramEventAddImage's identical
// fix (mousePressEvent) for why -- same class, same reasoning.
m_image->setRotationAngle(m_image->rotationAngle() + 90);
event->setAccepted(true); event->setAccepted(true);
} }
} }
@@ -135,35 +132,14 @@ void DiagramEventAddPdf::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
*/ */
void DiagramEventAddPdf::wheelEvent(QGraphicsSceneWheelEvent *event) void DiagramEventAddPdf::wheelEvent(QGraphicsSceneWheelEvent *event)
{ {
// event->modifiers() & Qt::ControlModifier, not != Qt::CTRL: the same if (!m_is_added || !m_image || event->modifiers() != Qt::CTRL) {
// exact-equality bug already found and fixed elsewhere this session --
// Ctrl held together with any other modifier would silently fail to
// register as Ctrl at all.
if (!m_is_added || !m_image || !(event->modifiers() & Qt::ControlModifier)) {
return; return;
} }
// scaleFactorX()/scaleFactorY(), not QGraphicsItem's own scale(): see qreal scaling = m_image->scale();
// DiagramEventAddImage's identical fix for why. No drag-to-resize event->delta() > 1 ? scaling += 0.01 : scaling -= 0.01;
// exists here, and the pivot is never touched elsewhere in this if (scaling > 0.01 && scaling <= 2) {
// class, so it stays at its default boundingRect().center() and this m_image->setScale(scaling);
// scales the page in place around its own middle, exactly like
// before.
//
// Step each axis from its own current value rather than reading X and
// writing it back to both: scaleFactorX and scaleFactorY cannot
// actually differ here today (nothing in this class ever sets them to
// different values, and there is no drag-resize at all), but stepping
// both independently costs nothing and removes the trap if that ever
// changes.
qreal scalingX = m_image->scaleFactorX();
qreal scalingY = m_image->scaleFactorY();
const qreal step = event->delta() > 1 ? 0.01 : -0.01;
scalingX += step;
scalingY += step;
if (scalingX > 0.01 && scalingX <= 2 && scalingY > 0.01 && scalingY <= 2) {
m_image->setScaleFactorX(scalingX);
m_image->setScaleFactorY(scalingY);
} }
event->setAccepted(true); event->setAccepted(true);
+21 -25
View File
@@ -87,9 +87,9 @@ DiagramEventAddShape::~DiagramEventAddShape()
Applies a drag/click position to the in-progress shape, honouring two Applies a drag/click position to the in-progress shape, honouring two
modifiers that mirror how the very same shape can already be edited modifiers that mirror how the very same shape can already be edited
afterward, once placed: afterward, once placed:
- Ctrl, for Rectangle/Ellipse only: the first click's point acts as - Ctrl, for Rectangle/Ellipse only: the first click becomes the
the shape's *center* rather than a corner, growing symmetrically shape's *center* rather than a corner, growing symmetrically as
as the cursor moves away from it -- the same meaning Ctrl already the cursor moves away from it -- the same meaning Ctrl already
has on a Resize handle (anchor at center). Deliberately not has on a Resize handle (anchor at center). Deliberately not
offered for Line: unlike the Rectangle/Ellipse case, there's no offered for Line: unlike the Rectangle/Ellipse case, there's no
established convention for "a line grows symmetrically from its established convention for "a line grows symmetrically from its
@@ -100,18 +100,12 @@ DiagramEventAddShape::~DiagramEventAddShape()
dragged dimensions is currently larger and mirroring that onto dragged dimensions is currently larger and mirroring that onto
the other, preserving the direction the user is actually the other, preserving the direction the user is actually
dragging in. dragging in.
Both can combine (Ctrl+Shift: a centered square/circle). Whether Ctrl Both can combine (Ctrl+Shift: a centered square/circle). Whether or
currently anchors from the center is re-decided on every call, from not Ctrl is currently held, the non-anchored branch always rebuilds
the live modifiers passed in here -- not frozen at whatever was held from m_anchor_point rather than nudging the existing rect/line --
on the first click. Every comparable tool (Illustrator, Photoshop, otherwise, if Ctrl had been held earlier in the same drag (moving the
Figma, Inkscape...) lets you press or release the center-origin shape's own first point to a mirrored position), releasing it would
modifier at any point mid-drag, with the shape immediately jumping to leave that point stuck there instead of actually restoring it.
match; that jump is the expected feedback for changing which point is
anchored, not a glitch. Freezing the choice at the first click instead
meant holding Ctrl anywhere other than the initial mouse-down did
nothing visible -- which, tried the more natural way (drag first,
then reach for Ctrl once you decide you want it centered), read as
"Ctrl doesn't work" rather than as a deliberate one-shot decision.
*/ */
void DiagramEventAddShape::applyPosition(const QPointF &pos, Qt::KeyboardModifiers mods) void DiagramEventAddShape::applyPosition(const QPointF &pos, Qt::KeyboardModifiers mods)
{ {
@@ -131,13 +125,14 @@ void DiagramEventAddShape::applyPosition(const QPointF &pos, Qt::KeyboardModifie
return; return;
} }
m_center_anchored = (mods & Qt::ControlModifier) // m_center_anchored is decided once, in mousePressEvent, not
&& (m_shape_type == QetShapeItem::Rectangle || m_shape_type == QetShapeItem::Ellipse); // re-checked here on every call -- re-checking it live meant
if (m_center_anchored) // releasing Ctrl mid-drag (something you'd naturally do the moment
showCenterMarker(m_anchor_point); // your hand gets tired holding it, long before you're done resizing)
else // silently snapped the shape back to corner-anchored, discarding
hideCenterMarker(); // what felt like an already-made decision. Deciding it once at the
// first click matches "I held Ctrl when I clicked, so this shape is
// centered" -- a single, predictable rule instead of a live toggle.
QPointF target = pos; QPointF target = pos;
if ((mods & Qt::ShiftModifier) if ((mods & Qt::ShiftModifier)
@@ -223,9 +218,10 @@ void DiagramEventAddShape::mousePressEvent(QGraphicsSceneMouseEvent *event)
{ {
m_shape_item = new QetShapeItem(pos, pos, m_shape_type); m_shape_item = new QetShapeItem(pos, pos, m_shape_type);
m_anchor_point = pos; m_anchor_point = pos;
// Initial feedback only -- applyPosition() re-decides this // Decided once, here, rather than re-checked on every mouse
// live on every subsequent move, from whatever Ctrl state is // move for the rest of the drag -- see applyPosition()'s doc
// held at the time. // comment for why continuous re-checking made releasing Ctrl
// mid-drag feel like a bug rather than a deliberate choice.
m_center_anchored = (event->modifiers() & Qt::ControlModifier) m_center_anchored = (event->modifiers() & Qt::ControlModifier)
&& (m_shape_type == QetShapeItem::Rectangle || m_shape_type == QetShapeItem::Ellipse); && (m_shape_type == QetShapeItem::Rectangle || m_shape_type == QetShapeItem::Ellipse);
if (m_center_anchored) if (m_center_anchored)
+1 -1
View File
@@ -59,7 +59,7 @@ class DiagramEventAddShape : public DiagramEventInterface
QGraphicsLineItem *m_help_horiz, *m_help_verti; QGraphicsLineItem *m_help_horiz, *m_help_verti;
QPointF m_anchor_point; // the shape's first-click point -- meaningful once m_shape_item exists QPointF m_anchor_point; // the shape's first-click point -- meaningful once m_shape_item exists
QGraphicsEllipseItem *m_center_marker = nullptr; // shown only while Ctrl-anchoring is actually in effect, so it doubles as confirmation that it is QGraphicsEllipseItem *m_center_marker = nullptr; // shown only while Ctrl-anchoring is actually in effect, so it doubles as confirmation that it is
bool m_center_anchored = false; // re-decided live on every applyPosition() call, from current Ctrl state bool m_center_anchored = false; // decided once, at the first click -- see applyPosition()'s doc comment for why
QPointF m_last_mouse_scene_pos; // raw, unsnapped -- lets a modifier-only change re-snap correctly when reapplied QPointF m_last_mouse_scene_pos; // raw, unsnapped -- lets a modifier-only change re-snap correctly when reapplied
}; };
+1 -78
View File
@@ -445,82 +445,6 @@ void DiagramView::pasteHere()
paste(mapToScene(m_paste_here_pos)); paste(mapToScene(m_paste_here_pos));
} }
/**
@brief DiagramView::duplicate
Copy the current selection and place the copy at @p stepOffset grid
steps from it, landing immediately rather than following the cursor
like Ctrl+V does (bugtracker #991). @p stepOffset comes from
DuplicateOffsetDialog: (1, 0) is one grid step right, (0, -1) is one
grid step up, and so on -- QET's own scene axes, X right and Y down.
No interactive placement step on purpose: the point of a duplicate
shortcut is unattended, repeatable stamping (configure the offset
once, then tap Ctrl+D to lay out a row), which following the cursor
would interrupt on every press. QET already reselects whatever a
paste just added (see PasteDiagramCommand::redo()), so the next
Ctrl+D naturally continues from the copy just placed, not the
original -- a press-and-hold row falls out of that for free, with no
special-casing needed here for "keep going from the last one".
The offset is applied by hand rather than by asking paste()/
Diagram::fromXml() to place the copy at a target position. Both of
those feed the position through Diagram::snapToGrid(), which reads
QApplication::keyboardModifiers() and rounds to the nearest PIXEL
instead of the grid whenever Ctrl is held -- and Ctrl is always held
here, this action's own shortcut being Ctrl+D. Measured the hard way
before settling on this: routing the offset through paste() first
produced copies off-grid on both axes, by an amount that tracked the
selection's own bounding-box geometry rather than being a fixed
error. fromXml() is instead called with no position at all, which
leaves every item at its source coordinates (landing the copy
exactly on top of the originals -- (0, 0) is not a position, this is
"keep the source coordinates"), and the offset is added directly
with setPos(). A plain addition cannot be off by a rounding rule
that never runs.
Conductors are not in the translated set: fromXml() itself does not
reposition them either -- they are loaded from XML after elements
are already in their final place and take their geometry from their
terminals, which have already moved with the elements that own
them. Likewise dynamic element texts are not translated separately:
they are children of their element and move with it under Qt's
normal parent-child transform.
*/
void DiagramView::duplicate(const QPoint &stepOffset)
{
if (!isInteractive() || m_diagram->isReadOnly()) return;
const QList<QGraphicsItem *> selection = m_diagram->selectedItems();
if (selection.isEmpty()) return;
QSettings settings;
const int x_grid = settings.value(QStringLiteral("diagrameditor/Xgrid"),
Diagram::xGrid).toInt();
const int y_grid = settings.value(QStringLiteral("diagrameditor/Ygrid"),
Diagram::yGrid).toInt();
const QPointF offset(stepOffset.x() * x_grid, stepOffset.y() * y_grid);
// Mirrors copy(), but does not touch the system clipboard: Ctrl+D
// should not clobber whatever the user last copied with Ctrl+C.
QDomDocument document = m_diagram->toXml(false, true);
DiagramContent pasted;
// No position argument -- see the function comment above for why
// the offset is not passed here.
m_diagram->fromXml(document, QPointF(), false, &pasted);
if (!pasted.count()) return;
const int movable = DiagramContent::Elements | DiagramContent::TextFields
| DiagramContent::Images | DiagramContent::Shapes
| DiagramContent::Tables | DiagramContent::TerminalStrip;
for (QGraphicsItem *item : pasted.items(movable))
item->setPos(item->pos() + offset);
m_diagram->clearSelection();
m_diagram->undoStack().push(new PasteDiagramCommand(m_diagram, pasted));
adjustSceneRect();
}
/** /**
Manage the events press click : Manage the events press click :
* click to add an independent text field * click to add an independent text field
@@ -584,7 +508,6 @@ void DiagramView::mousePressEvent(QMouseEvent *e)
*/ */
void DiagramView::mouseMoveEvent(QMouseEvent *e) void DiagramView::mouseMoveEvent(QMouseEvent *e)
{ {
m_last_mouse_pos = e->pos();
setToolTip(tr("X: %1 Y: %2").arg(e->pos().x()).arg(e->pos().y())); setToolTip(tr("X: %1 Y: %2").arg(e->pos().x()).arg(e->pos().y()));
if (m_event_interface && m_event_interface->mouseMoveEvent(e)) return; if (m_event_interface && m_event_interface->mouseMoveEvent(e)) return;
@@ -1478,7 +1401,7 @@ void DiagramView::createTemplateFromSelection()
collection_node.appendChild(collection_elmt); collection_node.appendChild(collection_elmt);
} else { } else {
qDebug() << "Warning: could not load XML definition for" << old_type; qDebug() << "Warnung: Konnte XML-Definition für" << old_type << "nicht laden.";
} }
} }
} }
-8
View File
@@ -55,7 +55,6 @@ class DiagramView : public PaletteGraphicsView
QAction *m_multi_paste = nullptr; QAction *m_multi_paste = nullptr;
QAction *m_create_template = nullptr; QAction *m_create_template = nullptr;
QPoint m_paste_here_pos; QPoint m_paste_here_pos;
QPoint m_last_mouse_pos = QPoint(-1, -1);
QPointF m_drag_last_pos; QPointF m_drag_last_pos;
bool m_fresh_focus_in, bool m_fresh_focus_in,
m_first_activation = true; m_first_activation = true;
@@ -72,12 +71,6 @@ class DiagramView : public PaletteGraphicsView
void editSelection(); void editSelection();
void setEventInterface (DVEventInterface *event_interface); void setEventInterface (DVEventInterface *event_interface);
QList<QAction *> contextMenuActions() const; QList<QAction *> contextMenuActions() const;
/// Last mouse position seen by mouseMoveEvent(), in viewport
/// coordinates -- (-1, -1) if the mouse hasn't moved over this
/// view yet. Filled from ordinary Qt mouse events, not a global
/// cursor query (QCursor::pos()/setPos() are silently ignored by
/// several window managers and compositors, Wayland included).
QPoint lastMousePos() const { return m_last_mouse_pos; }
protected: protected:
void mouseDoubleClickEvent(QMouseEvent *) override; void mouseDoubleClickEvent(QMouseEvent *) override;
@@ -144,7 +137,6 @@ class DiagramView : public PaletteGraphicsView
void copy(); void copy();
void paste(const QPointF & = QPointF(), QClipboard::Mode = QClipboard::Clipboard); void paste(const QPointF & = QPointF(), QClipboard::Mode = QClipboard::Clipboard);
void pasteHere(); void pasteHere();
void duplicate(const QPoint &stepOffset);
void adjustSceneRect(); void adjustSceneRect();
void updateWindowTitle(); void updateWindowTitle();
void resetConductors(); void resetConductors();
-12
View File
@@ -117,18 +117,6 @@ void ElementView::scaleClamped(qreal factor)
scale(factor, factor); scale(factor, factor);
} }
/**
@brief ElementView::zoom
Zoom by an arbitrary factor, for a continuous input such as a 3D mouse.
Same clamping as the wheel zoom.
@param zoom_factor : > 1 zooms in, < 1 zooms out
*/
void ElementView::zoom(qreal zoom_factor)
{
adjustSceneRect();
scaleClamped(zoom_factor);
}
/** /**
Agrandit le schema (+33% = inverse des -25 % de zoomMoins()) Agrandit le schema (+33% = inverse des -25 % de zoomMoins())
*/ */
-1
View File
@@ -41,7 +41,6 @@ class ElementView : public QGraphicsView {
ElementScene *scene() const; ElementScene *scene() const;
void setScene(ElementScene *); void setScene(ElementScene *);
QRectF viewedSceneRect() const; QRectF viewedSceneRect() const;
void zoom(qreal zoom_factor);
protected: protected:
void mousePressEvent(QMouseEvent *) override; void mousePressEvent(QMouseEvent *) override;
@@ -17,7 +17,6 @@
*/ */
#include "elementpropertieseditorwidget.h" #include "elementpropertieseditorwidget.h"
#include "../../qet.h"
#include "../../qetapp.h" #include "../../qetapp.h"
#include "../../qetinformation.h" #include "../../qetinformation.h"
#include "ui_elementpropertieseditorwidget.h" #include "ui_elementpropertieseditorwidget.h"
@@ -186,13 +185,13 @@ void ElementPropertiesEditorWidget::upDateInterface()
const DiagramContext &info = m_data.m_informations; const DiagramContext &info = m_data.m_informations;
ui->m_auto_num_locked_cb->setChecked( ui->m_auto_num_locked_cb->setChecked(
QET::infoFlagIsTrue(info.value(QStringLiteral("auto_num_locked")).toString())); info.value(QStringLiteral("auto_num_locked")).toString() == QLatin1String("true"));
ui->m_potential_isolating_cb->setChecked( ui->m_potential_isolating_cb->setChecked(
QET::infoFlagIsTrue(info.value(QStringLiteral("potential_isolating")).toString())); info.value(QStringLiteral("potential_isolating")).toString() == QLatin1String("true"));
} }
ui->m_exclude_from_bom_cb->setChecked( ui->m_exclude_from_bom_cb->setChecked(
QET::infoFlagIsTrue(m_data.m_informations.value(QStringLiteral("exclude_from_bom")).toString())); m_data.m_informations.value(QStringLiteral("exclude_from_bom")).toString() == QLatin1String("true"));
on_m_base_type_cb_currentIndexChanged(ui->m_base_type_cb->currentIndex()); on_m_base_type_cb_currentIndexChanged(ui->m_base_type_cb->currentIndex());
} }
@@ -589,29 +588,6 @@ void ElementPropertiesEditorWidget::populateSlaveGroupsTable()
contact_ct->setValue(group.contactCount); contact_ct->setValue(group.contactCount);
ui->m_slave_groups_table->setCellWidget(i, 2, contact_ct); ui->m_slave_groups_table->setCellWidget(i, 2, contact_ct);
// When the contact count changes, keep the terminal count in step
// with it, otherwise the two drift apart (both are edited
// independently): the stored terminals-per-contact ratio is kept,
// or the contact type default (2, 3 for a switch) is used when the
// stored values don't divide evenly (inconsistent legacy data).
const int old_contacts = group.contactCount;
const int old_terminals = group.terminalCount;
connect(contact_ct, QOverload<int>::of(&QSpinBox::valueChanged),
this, [this, i, old_contacts, old_terminals](int val) {
if (i < m_data.m_slave_contact_groups.size()) {
readSlaveGroupsFromTable();
auto &group = m_data.m_slave_contact_groups[i];
int per_pole = old_terminals / qMax(1, old_contacts);
if (per_pole < 1 || old_terminals % qMax(1, old_contacts) != 0)
per_pole = group.type == ElementData::SW ? 3 : 2;
else if (group.type == ElementData::SW && per_pole < 3)
per_pole = 3; //a switch needs common, NC and NO
group.contactCount = val;
group.terminalCount = val * per_pole;
populateSlaveGroupsTable();
}
});
// Terminal count // Terminal count
auto *terminal_ct = new QSpinBox(ui->m_slave_groups_table); auto *terminal_ct = new QSpinBox(ui->m_slave_groups_table);
terminal_ct->setMinimum(1); terminal_ct->setMinimum(1);
-20
View File
@@ -25,8 +25,6 @@
#include "../../qetapp.h" #include "../../qetapp.h"
#include "../../qetmainwindow.h" #include "../../qetmainwindow.h"
#include "../../recentfiles.h" #include "../../recentfiles.h"
#include "../../elementscollectioncache.h"
#include "../../factory/elementpicturefactory.h"
#include "../graphicspart/customelementpart.h" #include "../graphicspart/customelementpart.h"
#include "../elementitemeditor.h" #include "../elementitemeditor.h"
#include "../styleeditor.h" #include "../styleeditor.h"
@@ -350,24 +348,6 @@ bool QETElementEditor::toLocation(const ElementsLocation &location)
tr("Impossible d'enregistrer l'élément", "message box content")); tr("Impossible d'enregistrer l'élément", "message box content"));
return(false); return(false);
} }
//setXml() just wrote the new drawing to disk, but the preview shown
//in the elements panel comes from two caches keyed by path+uuid that
//nothing here has told about the change: ElementPictureFactory's
//in-memory picture cache and ElementsCollectionCache's on-disk
//SQLite cache (see ElementsLocation::icon()). locationWasSaved()
//(elementscollectionwidget.cpp) re-reads the icon right after this
//call returns, but both caches still hand back the pre-edit pixmap,
//so the panel keeps showing the stale preview until the whole
//collection is reloaded. Drop and rebuild them here.
ElementPictureFactory::instance()->dropCache(location);
if (ElementsCollectionCache *cache = QETApp::collectionCache()) {
if (cache->fetchData(location)) {
cache->cacheName(location.toString(), location.uuid());
cache->cachePixmap(location.toString(), location.uuid());
}
}
return(true); return(true);
} }
+3 -24
View File
@@ -674,27 +674,6 @@ void ElementsPanelWidget::duplicateDiagram()
bool erase_labels = settings.value( bool erase_labels = settings.value(
"diagramcommands/erase-label-on-copy", true).toBool(); "diagramcommands/erase-label-on-copy", true).toBool();
// Resolve a linked pair duplicated together against each other
// (bugtracker #607) before the loop below renews their uuids or
// clears their pending links: at this exact moment a copy's
// tmp_uuids_link still holds its source's original partner
// uuid, which still equals the not-yet-renewed uuid of that
// partner's own copy if both were duplicated together. Scoped
// to this diagram's own copies, not a project-wide search, so
// this never links back to the source elements the copies were
// made from -- if only one half of a linked pair is here, its
// link entry simply finds no match and is dropped, same as
// clearPendingLinks() used to do unconditionally for every copy.
QList<Element *> new_elements;
for (QGraphicsItem *item : new_diagram->items()) {
if (Element *elmt = dynamic_cast<Element *>(item)) {
new_elements << elmt;
}
}
for (Element *elmt : new_elements) {
elmt->initLink(new_elements);
}
for (QGraphicsItem *item : new_diagram->items()) { for (QGraphicsItem *item : new_diagram->items()) {
if (Element *elmt = dynamic_cast<Element *>(item)) { if (Element *elmt = dynamic_cast<Element *>(item)) {
// The XML round-trip kept the source elements' uuids. Give the // The XML round-trip kept the source elements' uuids. Give the
@@ -716,9 +695,9 @@ void ElementsPanelWidget::duplicateDiagram()
new_diagram->restoreText(elmt); new_diagram->restoreText(elmt);
} }
// initLink() above already cleared tmp_uuids_link for // Clear pending links so copies don't link back to
// every copy, matched or not -- nothing left here that // the source elements via stale UUIDs.
// could link back to a stale source uuid. elmt->clearPendingLinks();
// Clean up copied element data: // Clean up copied element data:
// 1. Slaves always lose label/formula/comment/location // 1. Slaves always lose label/formula/comment/location
+1 -2
View File
@@ -19,7 +19,6 @@
#include "QPropertyUndoCommand/qpropertyundocommand.h" #include "QPropertyUndoCommand/qpropertyundocommand.h"
#include "diagram.h" #include "diagram.h"
#include "qetapp.h"
#include "qetgraphicsitem/dynamicelementtextitem.h" #include "qetgraphicsitem/dynamicelementtextitem.h"
#include "qetgraphicsitem/elementtextitemgroup.h" #include "qetgraphicsitem/elementtextitemgroup.h"
@@ -147,5 +146,5 @@ QString ElementTextsMover::undoText() const
if (parts.isEmpty()) if (parts.isEmpty())
return QString(); // should never occur return QString(); // should never occur
return QObject::tr("Déplacer %1").arg(QLocale(QETApp::interfaceLanguage()).createSeparatedList(parts)); return QObject::tr("Déplacer %1").arg(QLocale().createSeparatedList(parts));
} }
+1 -7
View File
@@ -32,14 +32,8 @@ class QetGraphicsTableFactory
static void createAndAddNomenclature(Diagram *diagram); static void createAndAddNomenclature(Diagram *diagram);
static void createAndAddSummary(Diagram *diagram); static void createAndAddSummary(Diagram *diagram);
// Public so a caller that has already built and configured an
// AddTableDialog itself (never shown or exec'd -- the two
// methods above always exec() one, which the scripting API
// cannot use headlessly) can create a table from it directly.
// create() only reads settings already on the dialog; nothing
// about it depends on the dialog having been shown.
static void create(Diagram *diagram, AddTableDialog *dialog);
private: private:
static void create(Diagram *diagram, AddTableDialog *dialog);
static QetGraphicsTableItem *newTable( static QetGraphicsTableItem *newTable(
Diagram *diagram, Diagram *diagram,
AddTableDialog *dialog, AddTableDialog *dialog,
-30
View File
@@ -72,15 +72,6 @@ bool AddTableDialog::adjustTableToFolio() const
return ui->m_adjust_table_size_cb->isChecked(); return ui->m_adjust_table_size_cb->isChecked();
} }
/**
@brief AddTableDialog::setAdjustTableToFolio
@param set
*/
void AddTableDialog::setAdjustTableToFolio(bool set)
{
ui->m_adjust_table_size_cb->setChecked(set);
}
/** /**
@brief AddTableDialog::addNewTableToNewDiagram @brief AddTableDialog::addNewTableToNewDiagram
@return @return
@@ -90,15 +81,6 @@ bool AddTableDialog::addNewTableToNewDiagram() const
return ui->m_add_table_and_folio->isChecked(); return ui->m_add_table_and_folio->isChecked();
} }
/**
@brief AddTableDialog::setAddNewTableToNewDiagram
@param set
*/
void AddTableDialog::setAddNewTableToNewDiagram(bool set)
{
ui->m_add_table_and_folio->setChecked(set);
}
/** /**
@brief AddTableDialog::tableName @brief AddTableDialog::tableName
@return @return
@@ -108,18 +90,6 @@ QString AddTableDialog::tableName() const
return ui->m_table_name_le->text(); return ui->m_table_name_le->text();
} }
/**
@brief AddTableDialog::setTableName
Set the name field directly, so a caller that builds this dialog to
read from (never shows or execs it -- the scripting API's addTable())
does not need a name typed by a user who was never there to type one.
@param name
*/
void AddTableDialog::setTableName(const QString &name)
{
ui->m_table_name_le->setText(name);
}
/** /**
@brief AddTableDialog::headerMargins @brief AddTableDialog::headerMargins
@return @return
-3
View File
@@ -44,12 +44,9 @@ class AddTableDialog : public QDialog
void setQueryWidget(QWidget *widget); void setQueryWidget(QWidget *widget);
bool adjustTableToFolio() const; bool adjustTableToFolio() const;
void setAdjustTableToFolio(bool set);
bool addNewTableToNewDiagram() const; bool addNewTableToNewDiagram() const;
void setAddNewTableToNewDiagram(bool set);
QString tableName() const; QString tableName() const;
void setTableName(const QString &name);
QMargins headerMargins() const; QMargins headerMargins() const;
Qt::Alignment headerAlignment() const; Qt::Alignment headerAlignment() const;
-32
View File
@@ -22,8 +22,6 @@
#include "logging/eventloopwatchdog.h" #include "logging/eventloopwatchdog.h"
#include "logging/qetlogger.h" #include "logging/qetlogger.h"
#include "machine_info.h" #include "machine_info.h"
#include "diagram.h"
#include "palettegraphicsview.h"
#include "qet.h" #include "qet.h"
#include "qetapp.h" #include "qetapp.h"
#include "qetmessagebox.h" #include "qetmessagebox.h"
@@ -33,7 +31,6 @@
#include <QApplication> #include <QApplication>
#include <QDomImplementation> #include <QDomImplementation>
#include <QFont>
#include <QStyleFactory> #include <QStyleFactory>
#include <QtConcurrentRun> #include <QtConcurrentRun>
@@ -108,19 +105,6 @@ int main(int argc, char **argv)
// from Qt 6.12 on; opt in explicitly for older Qt 5/6. // from Qt 6.12 on; opt in explicitly for older Qt 5/6.
QDomImplementation::setInvalidDataPolicy( QDomImplementation::setInvalidDataPolicy(
QDomImplementation::ReturnNullNode); QDomImplementation::ReturnNullNode);
#ifdef Q_OS_WIN
// "MS Shell Dlg 2" is not a font but a Windows alias, and many projects
// and settings saved on Windows carry it. Qt 5's GDI font backend let
// Windows resolve it to Tahoma; Qt 6's DirectWrite backend does not know
// the alias and falls back to Arial, so those texts come out heavier on
// screen and in exported PDFs (bugtracker #340). Resolve both aliases
// the way Windows does. Done before any application object exists so
// that the headless export and scripting runs below get it too.
QFont::insertSubstitution("MS Shell Dlg 2", "Tahoma");
QFont::insertSubstitution("MS Shell Dlg", "Microsoft Sans Serif");
#endif
//Creation and execution of the application //Creation and execution of the application
//HighDPI //HighDPI
qputenv("QT_ENABLE_HIGHDPI_SCALING", "1"); qputenv("QT_ENABLE_HIGHDPI_SCALING", "1");
@@ -160,22 +144,6 @@ int main(int argc, char **argv)
#endif #endif
} }
// Re-apply the sheet background last picked in the diagram editor, so
// every project opened from here on -- existing or new, whichever one
// it is -- draws that background instead of the built-in default that
// would otherwise force the user to pick it again after each start.
//
// Done here rather than in main()'s first lines on purpose: the
// headless export and scripting runs above return before reaching
// this point and must keep rendering on plain white. It also has to
// happen before QETApp is constructed below, since that constructor
// already loads the projects given on the command line.
{
const QetSettings::SheetBackground sheet_background = QetSettings::sheetBackground();
PaletteGraphicsView::setCustomBackgroundColor(sheet_background.custom);
Diagram::background_color = sheet_background.color;
}
// Resolve the logger's state (log directory, session filename, open // Resolve the logger's state (log directory, session filename, open
// file handle) explicitly here, immediately before installing the // file handle) explicitly here, immediately before installing the
// handler -- not implicitly on whichever thread happens to log // handler -- not implicitly on whichever thread happens to log
-23
View File
@@ -388,29 +388,6 @@ void convertUriToGoTo(const QString &pdfPath)
out.close(); out.close();
} }
void removeUnusedPdfxNamespace(const QString &pdfPath)
{
QFile f(pdfPath);
if (!f.open(QIODevice::ReadOnly)) return;
QByteArray data = f.readAll();
f.close();
// Qt only fills the pdfxid namespace in when it writes PDF/X-4.
if (data.contains("pdfxid:GTS_PDFXVersion")) return;
static const QByteArray decl =
" xmlns:pdfxid=\"http://www.npes.org/pdfx/ns/id/\"";
const int pos = data.indexOf(decl);
if (pos == -1) return;
// Same length, so the stream /Length and the xref offsets stay valid.
data.replace(pos, decl.size(), QByteArray(decl.size(), ' '));
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) return;
f.write(data);
f.close();
}
void convertComponentInfoAnnotations(const QString &pdfPath, void convertComponentInfoAnnotations(const QString &pdfPath,
const QList<ComponentInfo> &annotations) const QList<ComponentInfo> &annotations)
{ {
-9
View File
@@ -74,15 +74,6 @@ namespace PdfLinks {
*/ */
void convertUriToGoTo(const QString &pdfPath); void convertUriToGoTo(const QString &pdfPath);
/**
Post-process a Qt-generated PDF file: blank out the PDF/X namespace
declaration Qt 6 writes into the XMP metadata of every PDF. Adobe
Acrobat draws small text too bold when it is present (bugtracker #340).
Replaced in place with spaces, so no offset changes. No-op for a real
PDF/X file or when the declaration is absent.
*/
void removeUnusedPdfxNamespace(const QString &pdfPath);
struct ComponentInfo { struct ComponentInfo {
QString contents; QString contents;
}; };
-1
View File
@@ -874,7 +874,6 @@ void ProjectPrintWindow::print()
// Convert URI link annotations into native internal GoTo/FitR // Convert URI link annotations into native internal GoTo/FitR
// actions so cross-references jump inside the document. // actions so cross-references jump inside the document.
PdfLinks::convertUriToGoTo(pdfFile); PdfLinks::convertUriToGoTo(pdfFile);
PdfLinks::removeUnusedPdfxNamespace(pdfFile);
this->close(); this->close();
}); });

Some files were not shown because too many files have changed in this diff Show More