mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-06-18 11:52:36 +02:00
Merge branch 'master' into master
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"id": "org.qelectrotech.QElectroTech",
|
||||
"base-version": "5.15-23.08",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "5.15-23.08",
|
||||
"runtime-version": "5.15-25.08",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"command": "qelectrotech",
|
||||
"rename-desktop-file": "org.qelectrotech.qelectrotech.desktop",
|
||||
@@ -18,51 +17,17 @@
|
||||
"--socket=cups",
|
||||
"--filesystem=host"
|
||||
],
|
||||
"cleanup": [
|
||||
"/include",
|
||||
"/man",
|
||||
"/share/doc",
|
||||
"/share/man",
|
||||
"*.la",
|
||||
"*.a"
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "tkinter",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [
|
||||
"pip3 install --prefix=${FLATPAK_DEST} ."
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/iwalton3/tkinter-standalone",
|
||||
"commit": "23c793bad2429f4a81eee9f50e2d07ae845b7785"
|
||||
}
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "tcl",
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://sourceforge.net/projects/tcl/files/Tcl/8.6.11/tcl8.6.11-src.tar.gz",
|
||||
"sha256": "8c0486668586672c5693d7d95817cb05a18c5ecca2f40e2836b9578064088258"
|
||||
}
|
||||
],
|
||||
"subdir": "unix",
|
||||
"post-install": [
|
||||
"chmod +w ${FLATPAK_DEST}/lib/libtcl8.6.so"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tk",
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://sourceforge.net/projects/tcl/files/Tcl/8.6.11/tk8.6.11-src.tar.gz",
|
||||
"sha256": "5228a8187a7f70fa0791ef0f975270f068ba9557f57456f51eb02d9d4ea31282"
|
||||
}
|
||||
],
|
||||
"subdir": "unix",
|
||||
"post-install": [
|
||||
"chmod +w ${FLATPAK_DEST}/lib/libtk8.6.so"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"tkinter.json",
|
||||
"pypi-dependencies.json",
|
||||
{
|
||||
"name": "qelectrotech",
|
||||
"buildsystem": "qmake",
|
||||
@@ -76,37 +41,11 @@
|
||||
},
|
||||
{
|
||||
"type": "patch",
|
||||
"path": "patches/0001-build-Fix-the-installation-paths.patch"
|
||||
"paths": [
|
||||
"patches/fix-the-installation-paths.patch"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "python3-PySimpleGUI",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [
|
||||
"pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} PySimpleGUI"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "file",
|
||||
"url": "https://files.pythonhosted.org/packages/d0/c3/c1ce811a1e48d5e0f2df0b393ff189fae4842ec840bb6e4db79c8da55e74/PySimpleGUI-4.41.2.tar.gz",
|
||||
"sha256": "cf42d9f61f28c8e790a9c031ce900a9cee5fd2f950da2f055ed36bbc487dcf11"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "python3-qet-tb-generator",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [
|
||||
"pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} qet-tb-generator"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "file",
|
||||
"url": "https://files.pythonhosted.org/packages/70/aa/ebde0dddfbde799a4e8cf0564e52f95089105a7f562739ee1d16ff5a495a/qet_tb_generator-1.3.1.tar.gz",
|
||||
"sha256": "52c9836387d54bc30ea29272068ec156fc65c3905e0cb863afd9418abc3c0907"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
From 5cb80674cec7363ed00bab5248b3674ca5241c2f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Sabri=20=C3=9Cnal?= <yakushabb@gmail.com>
|
||||
Date: Fri, 20 Feb 2026 22:56:52 +0300
|
||||
Subject: [PATCH] Fix appdata paper cuts
|
||||
|
||||
---
|
||||
misc/qelectrotech.appdata.xml | 26 ++++++++++++++++++++------
|
||||
1 file changed, 20 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/misc/qelectrotech.appdata.xml b/misc/qelectrotech.appdata.xml
|
||||
index dd06ab7..eb02119 100644
|
||||
--- a/misc/qelectrotech.appdata.xml
|
||||
+++ b/misc/qelectrotech.appdata.xml
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright 2006-2023 The QElectroTech Team -->
|
||||
-<application>
|
||||
- <id type="desktop">qelectrotech.desktop</id>
|
||||
+<component type="desktop-application">
|
||||
+ <id>org.qelectrotech.QElectroTech</id>
|
||||
+ <launchable type="desktop-id">qelectrotech.desktop</launchable>
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<project_license>GPL-2.0-or-later</project_license>
|
||||
<name>QElectroTech</name>
|
||||
@@ -83,9 +84,22 @@
|
||||
QET は要素と回路図に XML 形式を利用し、回路図エディタ、要素エディタ、表題欄エディタを含みます。
|
||||
</p>
|
||||
</description>
|
||||
- <url type="homepage">http://qelectrotech.org</url>
|
||||
+ <url type="homepage">https://qelectrotech.org</url>
|
||||
+ <url type="bugtracker">https://qelectrotech.org/bugtracker</url>
|
||||
+ <url type="vcs-browser">https://github.com/qelectrotech/qelectrotech-source-mirror</url>
|
||||
+ <developer id="org.qelectrotech">
|
||||
+ <name>QElectroTech</name>
|
||||
+ </developer>
|
||||
<screenshots>
|
||||
- <screenshot type="default">http://download.tuxfamily.org/qet/screens/qelectrotech5.png</screenshot>
|
||||
+ <screenshot type="default">
|
||||
+ <image>https://qelectrotech.org/screenshots/qet_overview04.png</image>
|
||||
+ </screenshot>
|
||||
+ <screenshot>
|
||||
+ <image>https://qelectrotech.org/screenshots/qet_overview06.png</image>
|
||||
+ </screenshot>
|
||||
+ <screenshot>
|
||||
+ <image>https://qelectrotech.org/screenshots/qet_overview09.png</image>
|
||||
+ </screenshot>
|
||||
</screenshots>
|
||||
- <updatecontact>qet@lists.tuxfamily.org</updatecontact>
|
||||
-</application>
|
||||
+ <update_contact>qet@lists.tuxfamily.org</update_contact>
|
||||
+</component>
|
||||
--
|
||||
2.53.0
|
||||
|
||||
+3
-16
@@ -1,16 +1,6 @@
|
||||
From 14f0685ddcf3a7d64bb85a3e9a9ac97c369bb508 Mon Sep 17 00:00:00 2001
|
||||
From: Laurent Trinques <scorpio@qelectrotech.org>
|
||||
Date: Sat, 26 Sep 2020 22:52:52 +0200
|
||||
Subject: [PATCH] build: Fix the installation paths
|
||||
|
||||
---
|
||||
qelectrotech.pro | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/qelectrotech.pro b/qelectrotech.pro
|
||||
index 7baddbb08..5dcda04b6 100644
|
||||
--- a/qelectrotech.pro
|
||||
+++ b/qelectrotech.pro
|
||||
diff -ruN a/qelectrotech.pro b/qelectrotech.pro
|
||||
--- a/qelectrotech.pro 2023-04-20 11:47:07.695847458 +0200
|
||||
+++ b/qelectrotech.pro 2023-04-20 11:51:14.843611898 +0200
|
||||
@@ -5,18 +5,18 @@
|
||||
# Chemins utilises pour la compilation et l'installation de QET
|
||||
unix {
|
||||
@@ -35,6 +25,3 @@ index 7baddbb08..5dcda04b6 100644
|
||||
QET_APPDATA_PATH = 'share/appdata'
|
||||
}
|
||||
win32 {
|
||||
--
|
||||
2.35.1
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
commit 3bbb09a0598fc976d2bf8dac932b27740086c1bd
|
||||
Author: Hubert Figuière <hub@figuiere.net>
|
||||
Date: Sun Dec 21 17:49:43 2025 -0500
|
||||
|
||||
Port to Python 3.13
|
||||
|
||||
Signed-off-by: Hubert Figuière <hub@figuiere.net>
|
||||
|
||||
diff --git a/_tkinter.c b/_tkinter.c
|
||||
index e537707..dfc5789 100644
|
||||
--- a/_tkinter.c
|
||||
+++ b/_tkinter.c
|
||||
@@ -21,7 +21,6 @@ Copyright (C) 1994 Steen Lumholt.
|
||||
|
||||
*/
|
||||
|
||||
-#define PY_SSIZE_T_CLEAN
|
||||
#ifndef Py_BUILD_CORE_BUILTIN
|
||||
# define Py_BUILD_CORE_MODULE 1
|
||||
#endif
|
||||
@@ -32,6 +31,9 @@ Copyright (C) 1994 Steen Lumholt.
|
||||
# include "pycore_fileutils.h" // _Py_stat()
|
||||
#endif
|
||||
|
||||
+#include "pycore_long.h" // _PyLong_IsNegative()
|
||||
+#include "pycore_sysmodule.h" // _PySys_GetOptionalAttrString()
|
||||
+
|
||||
#ifdef MS_WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
@@ -135,7 +137,7 @@ _get_tcl_lib_path()
|
||||
struct stat stat_buf;
|
||||
int stat_return_value;
|
||||
|
||||
- prefix = PyUnicode_FromWideChar(Py_GetPrefix(), -1);
|
||||
+ (void) _PySys_GetOptionalAttrString("base_prefix", &prefix);
|
||||
if (prefix == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -143,9 +145,11 @@ _get_tcl_lib_path()
|
||||
/* Check expected location for an installed Python first */
|
||||
tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION);
|
||||
if (tcl_library_path == NULL) {
|
||||
+ Py_DECREF(prefix);
|
||||
return NULL;
|
||||
}
|
||||
tcl_library_path = PyUnicode_Concat(prefix, tcl_library_path);
|
||||
+ Py_DECREF(prefix);
|
||||
if (tcl_library_path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -959,7 +963,8 @@ AsObj(PyObject *value)
|
||||
(unsigned char *)(void *)&wideValue,
|
||||
sizeof(wideValue),
|
||||
PY_LITTLE_ENDIAN,
|
||||
- /* signed */ 1) == 0) {
|
||||
+ /* signed */ 1,
|
||||
+ /* with_exceptions */ 1) == 0) {
|
||||
return Tcl_NewWideIntObj(wideValue);
|
||||
}
|
||||
PyErr_Clear();
|
||||
@@ -1988,7 +1993,7 @@ _tkinter_tkapp_getboolean(TkappObject *self, PyObject *arg)
|
||||
int v;
|
||||
|
||||
if (PyLong_Check(arg)) { /* int or bool */
|
||||
- return PyBool_FromLong(Py_SIZE(arg) != 0);
|
||||
+ return PyBool_FromLong(!_PyLong_IsZero((PyLongObject *)arg));
|
||||
}
|
||||
|
||||
if (PyTclObject_Check(arg)) {
|
||||
diff --git a/clinic/_tkinter.c.h b/clinic/_tkinter.c.h
|
||||
index 9103565..f43510d 100644
|
||||
--- a/clinic/_tkinter.c.h
|
||||
+++ b/clinic/_tkinter.c.h
|
||||
@@ -2,6 +2,8 @@
|
||||
preserve
|
||||
[clinic start generated code]*/
|
||||
|
||||
+#include "pycore_modsupport.h" // _PyArg_BadArgument()
|
||||
+
|
||||
PyDoc_STRVAR(_tkinter_tkapp_eval__doc__,
|
||||
"eval($self, script, /)\n"
|
||||
"--\n"
|
||||
@@ -426,7 +428,7 @@ _tkinter_tkapp_createfilehandler(TkappObject *self, PyObject *const *args, Py_ss
|
||||
goto exit;
|
||||
}
|
||||
file = args[0];
|
||||
- mask = _PyLong_AsInt(args[1]);
|
||||
+ mask = PyLong_AsInt(args[1]);
|
||||
if (mask == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -490,7 +492,7 @@ _tkinter_tkapp_createtimerhandler(TkappObject *self, PyObject *const *args, Py_s
|
||||
if (!_PyArg_CheckPositional("createtimerhandler", nargs, 2, 2)) {
|
||||
goto exit;
|
||||
}
|
||||
- milliseconds = _PyLong_AsInt(args[0]);
|
||||
+ milliseconds = PyLong_AsInt(args[0]);
|
||||
if (milliseconds == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -524,7 +526,7 @@ _tkinter_tkapp_mainloop(TkappObject *self, PyObject *const *args, Py_ssize_t nar
|
||||
if (nargs < 1) {
|
||||
goto skip_optional;
|
||||
}
|
||||
- threshold = _PyLong_AsInt(args[0]);
|
||||
+ threshold = PyLong_AsInt(args[0]);
|
||||
if (threshold == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -558,7 +560,7 @@ _tkinter_tkapp_dooneevent(TkappObject *self, PyObject *const *args, Py_ssize_t n
|
||||
if (nargs < 1) {
|
||||
goto skip_optional;
|
||||
}
|
||||
- flags = _PyLong_AsInt(args[0]);
|
||||
+ flags = PyLong_AsInt(args[0]);
|
||||
if (flags == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -741,29 +743,29 @@ _tkinter_create(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
if (nargs < 4) {
|
||||
goto skip_optional;
|
||||
}
|
||||
- interactive = _PyLong_AsInt(args[3]);
|
||||
- if (interactive == -1 && PyErr_Occurred()) {
|
||||
+ interactive = PyObject_IsTrue(args[3]);
|
||||
+ if (interactive < 0) {
|
||||
goto exit;
|
||||
}
|
||||
if (nargs < 5) {
|
||||
goto skip_optional;
|
||||
}
|
||||
- wantobjects = _PyLong_AsInt(args[4]);
|
||||
- if (wantobjects == -1 && PyErr_Occurred()) {
|
||||
+ wantobjects = PyObject_IsTrue(args[4]);
|
||||
+ if (wantobjects < 0) {
|
||||
goto exit;
|
||||
}
|
||||
if (nargs < 6) {
|
||||
goto skip_optional;
|
||||
}
|
||||
- wantTk = _PyLong_AsInt(args[5]);
|
||||
- if (wantTk == -1 && PyErr_Occurred()) {
|
||||
+ wantTk = PyObject_IsTrue(args[5]);
|
||||
+ if (wantTk < 0) {
|
||||
goto exit;
|
||||
}
|
||||
if (nargs < 7) {
|
||||
goto skip_optional;
|
||||
}
|
||||
- sync = _PyLong_AsInt(args[6]);
|
||||
- if (sync == -1 && PyErr_Occurred()) {
|
||||
+ sync = PyObject_IsTrue(args[6]);
|
||||
+ if (sync < 0) {
|
||||
goto exit;
|
||||
}
|
||||
if (nargs < 8) {
|
||||
@@ -814,7 +816,7 @@ _tkinter_setbusywaitinterval(PyObject *module, PyObject *arg)
|
||||
PyObject *return_value = NULL;
|
||||
int new_val;
|
||||
|
||||
- new_val = _PyLong_AsInt(arg);
|
||||
+ new_val = PyLong_AsInt(arg);
|
||||
if (new_val == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
diff --git a/setup.py b/setup.py
|
||||
index f379305..cf7b6ad 100644
|
||||
--- a/setup.py
|
||||
+++ b/setup.py
|
||||
@@ -3,11 +3,11 @@ from distutils.core import setup, Extension
|
||||
module1 = Extension('_tkinter',
|
||||
libraries=['tcl8.6', 'tk8.6'],
|
||||
sources=['_tkinter.c'],
|
||||
- include_dirs=['/app/include/'])
|
||||
+ include_dirs=['/app/include/', '/usr/include/python3.13/internal/'])
|
||||
|
||||
setup(
|
||||
name='tkinter-standalone',
|
||||
- version='3.11',
|
||||
+ version='3.13',
|
||||
description='Tkinter packaged as an external package for flatpak.',
|
||||
ext_modules=[module1],
|
||||
packages=["tkinter"]
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "pypi-dependencies",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [],
|
||||
"modules": [
|
||||
{
|
||||
"name": "python3-PySimpleGUI",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [
|
||||
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"PySimpleGUI\" --no-build-isolation"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "file",
|
||||
"url": "https://files.pythonhosted.org/packages/4d/d9/3de4b7ca71a7779e4f4a160088621b072a29d9b814a7fa9b5411571f4849/pysimplegui-5.0.8.3-py3-none-any.whl",
|
||||
"sha256": "67e35ad6dd76e9369051261114f4711308e87815a0488f7fa28b37c29a546f8b"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "python3-qet-tb-generator",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [
|
||||
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"qet-tb-generator\" --no-build-isolation"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "file",
|
||||
"url": "https://files.pythonhosted.org/packages/4d/d9/3de4b7ca71a7779e4f4a160088621b072a29d9b814a7fa9b5411571f4849/pysimplegui-5.0.8.3-py3-none-any.whl",
|
||||
"sha256": "67e35ad6dd76e9369051261114f4711308e87815a0488f7fa28b37c29a546f8b"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"url": "https://files.pythonhosted.org/packages/63/d6/81de49a3ccec259583241fec4d79c668eff4acf9eb4d0226db36e1399f2d/qet_tb_generator-1.3.1-py3-none-any.whl",
|
||||
"sha256": "80fb4af229edfd5774e61f96fa387ff394d5060abd0ca45c3c74d29de1ce9b53"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"//note": "The `tkinter` module is missing from the Freedesktop Sdk's Python installation",
|
||||
"name": "tkinter",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [
|
||||
"pip3 install --prefix=${FLATPAK_DEST} --no-build-isolation ."
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/iwalton3/tkinter-standalone",
|
||||
"commit": "88aa05075d90d393a29a484bce676e237d311082"
|
||||
},
|
||||
{
|
||||
"type": "patch",
|
||||
"path": "patches/tkinter-build.patch"
|
||||
}
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "tcl",
|
||||
"buildsystem": "autotools",
|
||||
"subdir": "unix",
|
||||
"post-install": [
|
||||
"chmod 755 /app/lib/libtcl*.so"
|
||||
],
|
||||
"cleanup": [
|
||||
"/bin"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://prdownloads.sourceforge.net/tcl/tcl8.6.17-src.tar.gz",
|
||||
"sha256": "a3903371efcce8a405c5c245d029e9f6850258a60fa3761c4d58995610949b31"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tk",
|
||||
"buildsystem": "autotools",
|
||||
"subdir": "unix",
|
||||
"post-install": [
|
||||
"chmod 755 /app/lib/libtk*.so"
|
||||
],
|
||||
"cleanup": [
|
||||
"/bin",
|
||||
"/lib/tk*/demos"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://prdownloads.sourceforge.net/tcl/tk8.6.17-src.tar.gz",
|
||||
"sha256": "e4982df6f969c08bf9dd858a6891059b4a3f50dc6c87c10abadbbe2fc4838946"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -88,6 +88,7 @@ set(QET_RES_FILES
|
||||
${QET_DIR}/sources/ui/dynamicelementtextitemeditor.ui
|
||||
${QET_DIR}/sources/ui/elementinfopartwidget.ui
|
||||
${QET_DIR}/sources/ui/elementinfowidget.ui
|
||||
${QET_DIR}/sources/ui/terminalnumberingdialog.ui
|
||||
${QET_DIR}/sources/ui/formulaassistantdialog.ui
|
||||
${QET_DIR}/sources/ui/imagepropertieswidget.ui
|
||||
${QET_DIR}/sources/ui/importelementdialog.ui
|
||||
@@ -112,6 +113,8 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/conductorautonumerotation.cpp
|
||||
${QET_DIR}/sources/conductorautonumerotation.h
|
||||
${QET_DIR}/sources/conductornumexport.cpp
|
||||
${QET_DIR}/sources/wiringlistexport.h
|
||||
${QET_DIR}/sources/wiringlistexport.cpp
|
||||
${QET_DIR}/sources/conductornumexport.h
|
||||
${QET_DIR}/sources/conductorprofile.cpp
|
||||
${QET_DIR}/sources/conductorprofile.h
|
||||
@@ -630,6 +633,8 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/ui/elementinfopartwidget.h
|
||||
${QET_DIR}/sources/ui/elementinfowidget.cpp
|
||||
${QET_DIR}/sources/ui/elementinfowidget.h
|
||||
${QET_DIR}/sources/ui/terminalnumberingdialog.cpp
|
||||
${QET_DIR}/sources/ui/terminalnumberingdialog.h
|
||||
${QET_DIR}/sources/ui/elementpropertieswidget.cpp
|
||||
${QET_DIR}/sources/ui/elementpropertieswidget.h
|
||||
${QET_DIR}/sources/ui/formulaassistantdialog.cpp
|
||||
|
||||
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -9723,7 +9723,7 @@ Bitte laden Sie diese über den Link herunter und entpacken Sie sie in den Insta
|
||||
<message>
|
||||
<location filename="../sources/ui/terminalnumberingdialog.cpp" line="140"/>
|
||||
<source>Automatic terminal numbering</source>
|
||||
<translation>Automatische Anschlussnummerierung</translation>
|
||||
<translation>Automatische Klemmennummerierung</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
+364
-186
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+368
-188
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
+364
-186
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ Icon=qelectrotech
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=application/x-qet-project;application/x-qet-element;application/x-qet-titleblock;
|
||||
Categories=Graphics;Qt;VectorGraphics;Science;Electricity;Engineering;
|
||||
Categories=Graphics;
|
||||
Keywords=Graphics;Science;Electricity;Engineering;
|
||||
Comment=Edit electrical diagrams.
|
||||
Comment[ar]=تحرير مخططات كهربائية
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright 2006-2026 The QElectroTech Team -->
|
||||
<application>
|
||||
<id type="desktop">qelectrotech.desktop</id>
|
||||
<component type="desktop-application">
|
||||
<id>org.qelectrotech.QElectroTech</id>
|
||||
<launchable type="desktop-id">org.qelectrotech.QElectroTech.desktop</launchable>
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<project_license>GPL-2.0-or-later</project_license>
|
||||
<name>QElectroTech</name>
|
||||
@@ -25,7 +26,7 @@
|
||||
<summary xml:lang="ru">Редактор электрических схем</summary>
|
||||
<content_rating type="oars-1.1"/>
|
||||
<releases>
|
||||
<release version="0.9-dev" date="2021-02-21"/>
|
||||
<release version="0.100.1-dev" date="2026"/>
|
||||
</releases>
|
||||
<description>
|
||||
<p>
|
||||
@@ -93,9 +94,20 @@
|
||||
Приложение использует для хранения проектов и библиотек элементов файлы в XML формате. Приложение помимо редактора электричесих схем, содержит также редакторы элементов и редактор шаблонов листов.
|
||||
</p>
|
||||
</description>
|
||||
<url type="homepage">http://qelectrotech.org</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">http://download.tuxfamily.org/qet/screens/qelectrotech5.png</screenshot>
|
||||
</screenshots>
|
||||
<updatecontact>qet@lists.tuxfamily.org</updatecontact>
|
||||
</application>
|
||||
<url type="homepage">https://qelectrotech.org</url>
|
||||
<url type="bugtracker">https://qelectrotech.org/bugtracker</url>
|
||||
<url type="vcs-browser">https://github.com/qelectrotech/qelectrotech-source-mirror</url>
|
||||
<developer id="org.qelectrotech">
|
||||
<name>QElectroTech</name>
|
||||
</developer>
|
||||
<screenshot type="default">
|
||||
<image>https://qelectrotech.org/screenshots/qet_overview04.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://qelectrotech.org/screenshots/qet_overview06.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://qelectrotech.org/screenshots/qet_overview09.png</image>
|
||||
</screenshot>
|
||||
<update_contact>scorpio@qelectrotech.org</update_contact>
|
||||
</component>
|
||||
|
||||
+94
-12
@@ -68,6 +68,9 @@ ElementsPanel::ElementsPanel(QWidget *parent) :
|
||||
connect(this, &GenericPanel::firstActivated, [this]() {QTimer::singleShot(250, this, SLOT(reload()));});
|
||||
connect(this, &ElementsPanel::panelContentChanged, this, &ElementsPanel::panelContentChange);
|
||||
|
||||
// manage signal itemClicked
|
||||
connect(this, &ElementsPanel::itemClicked, this, &ElementsPanel::slot_clicked);
|
||||
|
||||
//Emit a signal instead au manage is own context menu
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
}
|
||||
@@ -139,15 +142,26 @@ QTreeWidgetItem *ElementsPanel::addProject(QETProject *project,
|
||||
Q_UNUSED(options)
|
||||
|
||||
bool first_add = (first_reload_ || !projects_to_display_.contains(project));
|
||||
clearSelection();
|
||||
|
||||
// create the QTreeWidgetItem representing the project
|
||||
// create the QTreeWidgetItem representing the project
|
||||
QTreeWidgetItem *qtwi_project = GenericPanel::addProject(project, nullptr, GenericPanel::All);
|
||||
// the project will be inserted right before the common tb templates collection
|
||||
// the project will be inserted right before the common tb templates collection
|
||||
invisibleRootItem() -> insertChild(
|
||||
indexOfTopLevelItem(common_tbt_collection_item_),
|
||||
qtwi_project
|
||||
);
|
||||
if (first_add) qtwi_project -> setExpanded(true);
|
||||
if (first_add){
|
||||
qtwi_project -> setExpanded(true);
|
||||
// on adding an project select first diagram
|
||||
setCurrentItem(qtwi_project -> child(0));
|
||||
qtwi_project -> child(0)->setSelected(true);
|
||||
}
|
||||
else {
|
||||
// on adding an diagram to project select the last diagram
|
||||
setCurrentItem(qtwi_project->child(qtwi_project->childCount()-2));
|
||||
qtwi_project->child(qtwi_project->childCount()-2)->setSelected(true);
|
||||
}
|
||||
|
||||
if (TitleBlockTemplatesCollection *tbt_collection = project -> embeddedTitleBlockTemplatesCollection()) {
|
||||
if (QTreeWidgetItem *tbt_collection_qtwi = itemForTemplatesCollection(tbt_collection)) {
|
||||
@@ -258,21 +272,28 @@ void ElementsPanel::reload()
|
||||
}
|
||||
|
||||
/**
|
||||
Gere le double-clic sur un element.
|
||||
Si un double-clic sur un projet est effectue, le signal requestForProject
|
||||
est emis.
|
||||
Si un double-clic sur un schema est effectue, le signal requestForDiagram
|
||||
est emis.
|
||||
@brief ElementsPanel::slot_clicked
|
||||
handle click on qtwi
|
||||
@param qtwi item that was clickerd on
|
||||
*/
|
||||
void ElementsPanel::slot_clicked(QTreeWidgetItem *clickedItem, int) {
|
||||
|
||||
requestForItem(clickedItem);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementsPanel::slot_doubleClick
|
||||
handle double click on qtwi
|
||||
@param qtwi
|
||||
*/
|
||||
void ElementsPanel::slot_doubleClick(QTreeWidgetItem *qtwi, int) {
|
||||
int qtwi_type = qtwi -> type();
|
||||
if (qtwi_type == QET::Project) {
|
||||
QETProject *project = valueForItem<QETProject *>(qtwi);
|
||||
emit(requestForProject(project));
|
||||
// open project properties
|
||||
emit(requestForProjectPropertiesEdition());
|
||||
} else if (qtwi_type == QET::Diagram) {
|
||||
Diagram *diagram = valueForItem<Diagram *>(qtwi);
|
||||
diagram->showMe();
|
||||
// open diagram properties
|
||||
emit(requestForDiagramPropertiesEdition());
|
||||
} else if (qtwi_type == QET::TitleBlockTemplate) {
|
||||
TitleBlockTemplateLocation tbt = valueForItem<TitleBlockTemplateLocation>(qtwi);
|
||||
emit(requestForTitleBlockTemplate(tbt));
|
||||
@@ -444,3 +465,64 @@ void ElementsPanel::ensureHierarchyIsVisible(const QList<QTreeWidgetItem *> &ite
|
||||
if (parent_qtwi -> isHidden()) parent_qtwi -> setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ElementsPanel::syncTabBars
|
||||
* set the project- or diagram Tab corresponding to
|
||||
* the selection in the treeView
|
||||
*/
|
||||
void ElementsPanel::requestForItem(QTreeWidgetItem *clickedItem)
|
||||
{
|
||||
// activate diagram
|
||||
if(clickedItem->type() == QET::Diagram){
|
||||
Diagram *diagram = valueForItem<Diagram *>(clickedItem);
|
||||
// if we click on diagramItem in annother project we need the other project
|
||||
emit(requestForProject(projectForItem(clickedItem->parent())));
|
||||
// required for keyPressEvent
|
||||
// after emit the focus is on the diagram editor, we put it back to elementsPanel
|
||||
this->setFocus();
|
||||
// activate diagram
|
||||
diagram->showMe();
|
||||
}
|
||||
// activate project
|
||||
else if(clickedItem->type() == QET::Project) {
|
||||
QETProject *project = projectForItem(clickedItem);
|
||||
emit(requestForProject(project));
|
||||
this->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ElementsPanel::keyPressEvent
|
||||
* @param event
|
||||
*/
|
||||
void ElementsPanel::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
switch (event->key())
|
||||
{
|
||||
case Qt::Key_Up:{
|
||||
// check if there is another item abbove
|
||||
if(!itemAbove(currentItem()))
|
||||
break;
|
||||
|
||||
setCurrentItem(itemAbove(currentItem()));
|
||||
if (currentItem()->type()==QET::Diagram || currentItem()->type()==QET::Project){
|
||||
requestForItem(currentItem());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Qt::Key_Down:{
|
||||
// check if there is another item below
|
||||
if(!itemBelow(currentItem()))
|
||||
break;
|
||||
|
||||
setCurrentItem(itemBelow(currentItem()));
|
||||
if (currentItem()->type()==QET::Diagram || currentItem()->type()==QET::Project){
|
||||
requestForItem(currentItem());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
QTreeView::keyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,13 @@ class ElementsPanel : public GenericPanel {
|
||||
signals:
|
||||
void requestForProject(QETProject *);
|
||||
void requestForTitleBlockTemplate(const TitleBlockTemplateLocation &);
|
||||
// Signal to open the project properties
|
||||
void requestForProjectPropertiesEdition();
|
||||
// Signal to open the diagram properties
|
||||
void requestForDiagramPropertiesEdition();
|
||||
|
||||
public slots:
|
||||
void slot_clicked(QTreeWidgetItem *, int);
|
||||
void slot_doubleClick(QTreeWidgetItem *, int);
|
||||
void reload();
|
||||
void filter(const QString &, QET::Filtering = QET::RegularFilter);
|
||||
@@ -63,6 +68,8 @@ class ElementsPanel : public GenericPanel {
|
||||
void buildFilterList();
|
||||
void applyCurrentFilter(const QList<QTreeWidgetItem *> &);
|
||||
void ensureHierarchyIsVisible(const QList<QTreeWidgetItem *> &);
|
||||
void requestForItem(QTreeWidgetItem *);
|
||||
void keyPressEvent(QKeyEvent *event)override;
|
||||
|
||||
protected:
|
||||
void startDrag(Qt::DropActions) override;
|
||||
|
||||
@@ -120,6 +120,12 @@ ElementsPanelWidget::ElementsPanelWidget(QWidget *parent) : QWidget(parent) {
|
||||
SLOT(openTitleBlockTemplate(const TitleBlockTemplateLocation &))
|
||||
);
|
||||
|
||||
// manage double click on TreeWidgetItem
|
||||
connect(elements_panel, SIGNAL(requestForProjectPropertiesEdition()), this, SLOT(editProjectProperties()) );
|
||||
connect(elements_panel, SIGNAL(requestForDiagramPropertiesEdition()), this, SLOT(editDiagramProperties()) );
|
||||
// manage project activation
|
||||
connect(elements_panel, SIGNAL(requestForProject(QETProject*)), this, SIGNAL(requestForProject(QETProject*)));
|
||||
|
||||
// disposition verticale
|
||||
QVBoxLayout *vlayout = new QVBoxLayout(this);
|
||||
vlayout -> setContentsMargins(0,0,0,0);
|
||||
@@ -236,6 +242,7 @@ void ElementsPanelWidget::deleteDiagram()
|
||||
{
|
||||
if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
|
||||
emit(requestForDiagramDeletion(selected_diagram));
|
||||
elements_panel->reload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -766,7 +766,6 @@ void GenericPanel::projectDiagramsOrderChanged(QETProject *project,
|
||||
if (!moved_qtwi_diagram) return;
|
||||
|
||||
// remove the QTWI then insert it back at the adequate location
|
||||
bool was_selected = moved_qtwi_diagram -> isSelected();
|
||||
qtwi_project -> removeChild (moved_qtwi_diagram);
|
||||
qtwi_project -> insertChild (to, moved_qtwi_diagram);
|
||||
|
||||
@@ -781,8 +780,8 @@ void GenericPanel::projectDiagramsOrderChanged(QETProject *project,
|
||||
updateDiagramItem(qtwi_diagram, diagram);
|
||||
}
|
||||
|
||||
if (was_selected)
|
||||
setCurrentItem(moved_qtwi_diagram);
|
||||
// select the moved diagram
|
||||
setCurrentItem(qtwi_project -> child(from));
|
||||
|
||||
emit(panelContentChanged());
|
||||
}
|
||||
|
||||
+56
-17
@@ -721,6 +721,14 @@ void ProjectView::initActions()
|
||||
|
||||
m_end_view = new QAction(QET::Icons::ArrowRightDouble, tr("Aller à la fin du projet"),this);
|
||||
connect(m_end_view, &QAction::triggered, [this](){this->m_tab->setCurrentWidget(lastDiagram());});
|
||||
|
||||
// button to scroll one page left
|
||||
m_next_view_left = new QAction(QET::Icons::ArrowLeft, tr("go one page left"),this);
|
||||
connect(m_next_view_left, &QAction::triggered, [this](){this->m_tab->setCurrentWidget(previousDiagram());});
|
||||
|
||||
// button to scroll one page right
|
||||
m_next_view_right = new QAction(QET::Icons::ArrowRight, tr("go one page right"),this);
|
||||
connect(m_next_view_right, &QAction::triggered, [this](){this->m_tab->setCurrentWidget(nextDiagram());});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -748,34 +756,65 @@ void ProjectView::initWidgets()
|
||||
m_tab = new QTabWidget(this);
|
||||
#endif
|
||||
m_tab -> setMovable(true);
|
||||
// setting UsesScrollButton ensures that when the tab bar is full, the tabs are scrolled.
|
||||
m_tab -> setUsesScrollButtons(true);
|
||||
// disable the internal scroll buttons of the TabWidget, we will use our own buttons.
|
||||
m_tab->setStyleSheet("QTabBar QToolButton {border-image: ;border-width: 0px}");
|
||||
m_tab->setStyleSheet("QTabBar::scroller {width: 0px;}");
|
||||
|
||||
// add layouts
|
||||
QHBoxLayout *TopRightCorner_Layout = new QHBoxLayout();
|
||||
TopRightCorner_Layout->setContentsMargins(0,0,0,0);
|
||||
// some place left to the 'next_right_view_button' button
|
||||
TopRightCorner_Layout->insertSpacing(1,10);
|
||||
|
||||
QToolButton *add_new_diagram_button = new QToolButton;
|
||||
add_new_diagram_button -> setDefaultAction(m_add_new_diagram);
|
||||
add_new_diagram_button -> setAutoRaise(true);
|
||||
TopRightCorner_Layout->addWidget(add_new_diagram_button);
|
||||
QHBoxLayout *TopLeftCorner_Layout = new QHBoxLayout();
|
||||
TopLeftCorner_Layout->setContentsMargins(0,0,0,0);
|
||||
|
||||
connect(m_tab, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
|
||||
connect(m_tab, SIGNAL(tabBarDoubleClicked(int)), this, SLOT(tabDoubleClicked(int)));
|
||||
connect(m_tab->tabBar(), SIGNAL(tabMoved(int, int)), this, SLOT(tabMoved(int, int)), Qt::QueuedConnection);
|
||||
// add buttons
|
||||
QToolButton *m_next_right_view_button =new QToolButton;
|
||||
m_next_right_view_button->setDefaultAction(m_next_view_right);
|
||||
m_next_right_view_button->setAutoRaise(true);
|
||||
TopRightCorner_Layout->addWidget(m_next_right_view_button);
|
||||
|
||||
//arrows button to return on first view
|
||||
QToolButton *m_first_view_button =new QToolButton;
|
||||
m_first_view_button->setDefaultAction(m_first_view);
|
||||
m_first_view_button->setAutoRaise(true);
|
||||
m_tab->setCornerWidget(m_first_view_button, Qt::TopLeftCorner);
|
||||
|
||||
//arrows button to go on last view
|
||||
QToolButton *m_end_view_button =new QToolButton;
|
||||
m_end_view_button->setDefaultAction(m_end_view);
|
||||
m_end_view_button->setAutoRaise(true);
|
||||
TopRightCorner_Layout->addWidget(m_end_view_button);
|
||||
|
||||
QWidget *tabwidget=new QWidget(this);
|
||||
tabwidget->setLayout(TopRightCorner_Layout);
|
||||
m_tab -> setCornerWidget(tabwidget, Qt::TopRightCorner);
|
||||
QToolButton *add_new_diagram_button = new QToolButton;
|
||||
add_new_diagram_button -> setDefaultAction(m_add_new_diagram);
|
||||
add_new_diagram_button -> setAutoRaise(true);
|
||||
TopRightCorner_Layout->addWidget(add_new_diagram_button);
|
||||
// some place right to the 'add_new_diagram_button' button
|
||||
TopRightCorner_Layout->addSpacing(5);
|
||||
|
||||
QToolButton *m_first_view_button =new QToolButton;
|
||||
m_first_view_button->setDefaultAction(m_first_view);
|
||||
m_first_view_button->setAutoRaise(true);
|
||||
TopLeftCorner_Layout->addWidget(m_first_view_button);
|
||||
|
||||
QToolButton *m_next_left_view_button =new QToolButton;
|
||||
m_next_left_view_button->setDefaultAction(m_next_view_left);
|
||||
m_next_left_view_button->setAutoRaise(true);
|
||||
TopLeftCorner_Layout->addWidget(m_next_left_view_button);
|
||||
|
||||
// some place right to the 'first_view_button' button
|
||||
TopLeftCorner_Layout->addSpacing(10);
|
||||
|
||||
// add widgets to tabbar
|
||||
QWidget *tabwidgetRight=new QWidget(this);
|
||||
tabwidgetRight->setLayout(TopRightCorner_Layout);
|
||||
m_tab -> setCornerWidget(tabwidgetRight, Qt::TopRightCorner);
|
||||
|
||||
QWidget *tabwidgetLeft=new QWidget(this);
|
||||
tabwidgetLeft->setLayout(TopLeftCorner_Layout);
|
||||
m_tab -> setCornerWidget(tabwidgetLeft, Qt::TopLeftCorner);
|
||||
|
||||
// manage signals
|
||||
connect(m_tab, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
|
||||
connect(m_tab, SIGNAL(tabBarDoubleClicked(int)), this, SLOT(tabDoubleClicked(int)));
|
||||
connect(m_tab->tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int)), Qt::QueuedConnection);
|
||||
|
||||
fallback_widget_ -> setVisible(false);
|
||||
m_tab -> setVisible(false);
|
||||
|
||||
@@ -168,8 +168,10 @@ class ProjectView : public QWidget
|
||||
// attributes
|
||||
private:
|
||||
QAction *m_add_new_diagram,
|
||||
*m_first_view,
|
||||
*m_end_view;
|
||||
*m_first_view,
|
||||
*m_end_view,
|
||||
*m_next_view_left,
|
||||
*m_next_view_right;
|
||||
QETProject *m_project;
|
||||
QVBoxLayout *layout_;
|
||||
QWidget *fallback_widget_;
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
#include "TerminalStrip/ui/terminalstripeditorwindow.h"
|
||||
#include "ui/diagrameditorhandlersizewidget.h"
|
||||
#include "TerminalStrip/ui/addterminalstripitemdialog.h"
|
||||
#include "wiringlistexport.h"
|
||||
#include "ui/terminalnumberingdialog.h"
|
||||
|
||||
#ifdef BUILD_WITHOUT_KF5
|
||||
#else
|
||||
@@ -465,13 +467,27 @@ void QETDiagramEditor::setUpActions()
|
||||
wne.toCsv();
|
||||
}
|
||||
});
|
||||
|
||||
#ifdef QET_EXPORT_PROJECT_DB
|
||||
m_export_project_db = new QAction(QET::Icons::DocumentSpreadsheet, tr("Exporter la base de donnée interne du projet"), this);
|
||||
connect(m_export_project_db, &QAction::triggered, [this]() {
|
||||
projectDataBase::exportDb(this->currentProject()->dataBase(), this);
|
||||
// Export wiring list to CSV
|
||||
m_project_export_wiring_list = new QAction(QET::Icons::DocumentSpreadsheet, tr("Exporter le plan de câblage"), this);
|
||||
connect(m_project_export_wiring_list, &QAction::triggered, [this]() {
|
||||
QETProject *project = this->currentProject();
|
||||
if (project)
|
||||
{
|
||||
WiringListExport wle(project, this);
|
||||
wle.toCsv();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
// Terminal Numbering
|
||||
m_terminal_numbering = new QAction(QET::Icons::TerminalStrip, tr("Numérotation automatique des bornes"), this);
|
||||
connect(m_terminal_numbering, &QAction::triggered, this, &QETDiagramEditor::slot_terminalNumbering);
|
||||
|
||||
#ifdef QET_EXPORT_PROJECT_DB
|
||||
m_export_project_db = new QAction(QET::Icons::DocumentSpreadsheet, tr("Exporter la base de donnée interne du projet"), this);
|
||||
connect(m_export_project_db, &QAction::triggered, [this]() {
|
||||
projectDataBase::exportDb(this->currentProject()->dataBase(), this);
|
||||
});
|
||||
#endif
|
||||
|
||||
//MDI view style
|
||||
m_tabbed_view_mode = new QAction(tr("en utilisant des onglets"), this);
|
||||
@@ -835,6 +851,8 @@ void QETDiagramEditor::setUpMenu()
|
||||
menu_project -> addAction(m_project_export_conductor_num);
|
||||
menu_project -> addAction(m_terminal_strip_dialog);
|
||||
menu_project -> addAction(m_project_terminalBloc);
|
||||
menu_project -> addAction(m_project_export_wiring_list);
|
||||
menu_project -> addAction(m_terminal_numbering);
|
||||
#ifdef QET_EXPORT_PROJECT_DB
|
||||
menu_project -> addSeparator();
|
||||
menu_project -> addAction(m_export_project_db);
|
||||
@@ -1168,18 +1186,6 @@ bool QETDiagramEditor::addProject(QETProject *project, bool update_panel)
|
||||
|
||||
// cree un ProjectView pour visualiser le projet
|
||||
ProjectView *project_view = new ProjectView(project);
|
||||
//Highlight the current page
|
||||
connect(project_view, &ProjectView::diagramActivated, this, [this](DiagramView *dv) {
|
||||
if (dv && dv->diagram() && pa) {
|
||||
// 1. Find the item in the tree that corresponds to this diagram
|
||||
QTreeWidgetItem *item = pa->elementsPanel().getItemForDiagram(dv->diagram());
|
||||
|
||||
// 2. If you find it, select it
|
||||
if (item) {
|
||||
pa->elementsPanel().setCurrentItem(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
addProjectView(project_view);
|
||||
|
||||
undo_group.addStack(project -> undoStack());
|
||||
@@ -1579,6 +1585,8 @@ void QETDiagramEditor::slot_updateActions()
|
||||
m_csv_export -> setEnabled(editable_project);
|
||||
m_project_export_conductor_num-> setEnabled(opened_project);
|
||||
m_terminal_strip_dialog -> setEnabled(editable_project);
|
||||
m_project_export_wiring_list -> setEnabled(opened_project);
|
||||
m_terminal_numbering -> setEnabled(editable_project);
|
||||
#ifdef QET_EXPORT_PROJECT_DB
|
||||
m_export_project_db -> setEnabled(editable_project);
|
||||
#endif
|
||||
@@ -1840,6 +1848,31 @@ void QETDiagramEditor::addProjectView(ProjectView *project_view)
|
||||
connect(project_view, SIGNAL(errorEncountered(QString)),
|
||||
this, SLOT(showError(const QString &)));
|
||||
|
||||
//Highlight the current page
|
||||
connect(project_view, &ProjectView::diagramActivated, this, [this](DiagramView *dv) {
|
||||
if (dv && dv->diagram() && pa) {
|
||||
// 1. Find the item in the tree that corresponds to this diagram
|
||||
QTreeWidgetItem *item = pa->elementsPanel().getItemForDiagram(dv->diagram());
|
||||
|
||||
// 2. If you find it, select it
|
||||
if (item) {
|
||||
pa->elementsPanel().setCurrentItem(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Highlight the current page in projectView on project activation
|
||||
connect(this, &QETDiagramEditor::syncElementsPanel, this, [this]() {
|
||||
if (pa && currentDiagramView()) {
|
||||
// In the tree, find the element that corresponds to the diagram of the selected project.
|
||||
QTreeWidgetItem *item = pa->elementsPanel().getItemForDiagram(currentDiagramView()->diagram());
|
||||
if (item) {
|
||||
// select the diagram
|
||||
pa->elementsPanel().setCurrentItem(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//We maximise the new window if the current window is inexistent or maximized
|
||||
QWidget *current_window = m_workspace.activeSubWindow();
|
||||
bool maximise = ((!current_window)
|
||||
@@ -2348,6 +2381,7 @@ void QETDiagramEditor::subWindowActivated(QMdiSubWindow *subWindows)
|
||||
|
||||
slot_updateActions();
|
||||
slot_updateWindowsMenu();
|
||||
emit syncElementsPanel();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2483,7 +2517,27 @@ void QETDiagramEditor::generateTerminalBlock()
|
||||
#endif
|
||||
if ( !success ) {
|
||||
QMessageBox::warning(nullptr,
|
||||
QObject::tr("Error launching qet_tb_generator plugin"),
|
||||
message);
|
||||
QObject::tr("Error launching qet_tb_generator plugin"),
|
||||
message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief QETDiagramEditor::slot_terminalNumbering
|
||||
* Opens the dialog for automatic terminal numbering and applies the generated undo command.
|
||||
*/
|
||||
void QETDiagramEditor::slot_terminalNumbering() {
|
||||
TerminalNumberingDialog dialog(this);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QETProject *project = currentProject();
|
||||
if (!project) return;
|
||||
|
||||
// Fetch the generated undo command from the dialog logic
|
||||
QUndoCommand *macro = dialog.getUndoCommand(project);
|
||||
|
||||
// If changes were made, push them to the global undo stack
|
||||
if (macro) {
|
||||
undo_group.activeStack()->push(macro);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ class RecentFiles;
|
||||
class DiagramPropertiesEditorDockWidget;
|
||||
class ElementsCollectionWidget;
|
||||
class AutoNumberingDockWidget;
|
||||
class TerminalNumberingDialog;
|
||||
|
||||
#ifdef BUILD_WITHOUT_KF5
|
||||
#else
|
||||
@@ -98,6 +99,9 @@ class QETDiagramEditor : public QETMainWindow
|
||||
ProjectView *findProject(const QString &) const;
|
||||
QMdiSubWindow *subWindowForWidget(QWidget *) const;
|
||||
|
||||
signals:
|
||||
void syncElementsPanel();
|
||||
|
||||
public slots:
|
||||
void save();
|
||||
void saveAs();
|
||||
@@ -129,6 +133,7 @@ class QETDiagramEditor : public QETMainWindow
|
||||
void projectWasClosed(ProjectView *);
|
||||
void editProjectProperties(ProjectView *);
|
||||
void editProjectProperties(QETProject *);
|
||||
void slot_terminalNumbering();
|
||||
void editDiagramProperties(DiagramView *);
|
||||
void editDiagramProperties(Diagram *);
|
||||
void addDiagramToProject(QETProject *);
|
||||
@@ -197,6 +202,8 @@ class QETDiagramEditor : public QETMainWindow
|
||||
*m_terminal_strip_dialog = nullptr, ///<Launch terminal strip dialog
|
||||
*m_project_terminalBloc, ///< generate terminal block
|
||||
*m_project_export_conductor_num,///<Export the wire num to csv
|
||||
*m_project_export_wiring_list, ///< Action to export the wiring list
|
||||
*m_terminal_numbering, ///< Action to launch terminal numbering
|
||||
*m_export_project_db, ///Export to file the internal database of the current project
|
||||
*m_tile_window, ///< Show MDI subwindows as tile
|
||||
*m_cascade_window, ///< Show MDI subwindows as cascade
|
||||
|
||||
@@ -160,6 +160,7 @@ void ElementInfoWidget::enableLiveEdit()
|
||||
{
|
||||
for (ElementInfoPartWidget *eipw : m_eipw_list)
|
||||
connect(eipw, &ElementInfoPartWidget::textChanged, this, &ElementInfoWidget::apply);
|
||||
connect(ui->m_auto_num_locked_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,6 +171,7 @@ void ElementInfoWidget::disableLiveEdit()
|
||||
{
|
||||
for (ElementInfoPartWidget *eipw : m_eipw_list)
|
||||
disconnect(eipw, &ElementInfoPartWidget::textChanged, this, &ElementInfoWidget::apply);
|
||||
disconnect(ui->m_auto_num_locked_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,6 +195,12 @@ void ElementInfoWidget::buildInterface()
|
||||
}
|
||||
|
||||
ui->scroll_vlayout->addStretch();
|
||||
// Show checkbox only if the element is a terminal
|
||||
if (m_element.data()->elementData().m_type == ElementData::Terminal) {
|
||||
ui->m_auto_num_locked_cb->setVisible(true);
|
||||
} else {
|
||||
ui->m_auto_num_locked_cb->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,6 +239,11 @@ void ElementInfoWidget::updateUi()
|
||||
for (ElementInfoPartWidget *eipw : m_eipw_list) {
|
||||
eipw -> setText (element_info[eipw->key()].toString());
|
||||
}
|
||||
// Load the lock status for auto numbering
|
||||
if (m_element->elementData().m_type == ElementData::Terminal) {
|
||||
QString lock_value = element_info.value(QStringLiteral("auto_num_locked")).toString();
|
||||
ui->m_auto_num_locked_cb->setChecked(lock_value == QLatin1String("true"));
|
||||
}
|
||||
|
||||
if (m_live_edit) {
|
||||
enableLiveEdit();
|
||||
@@ -259,6 +272,10 @@ DiagramContext ElementInfoWidget::currentInfo() const
|
||||
}
|
||||
}
|
||||
|
||||
// Save the auto numbering lock status
|
||||
if (m_element->elementData().m_type == ElementData::Terminal) {
|
||||
info_.addValue(QStringLiteral("auto_num_locked"), ui->m_auto_num_locked_cb->isChecked() ? QStringLiteral("true") : QStringLiteral("false"));
|
||||
}
|
||||
return info_;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,19 @@
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="m_auto_num_locked_cb">
|
||||
<property name="text">
|
||||
<string>Exclure de la numérotation auto</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin: 5px; font-weight: bold;</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="sizePolicy">
|
||||
|
||||
@@ -372,6 +372,10 @@ QWidget *ElementPropertiesWidget::generalWidget()
|
||||
description_string += QString(tr("Rotation : %1°\n")).arg(m_element.data()->rotation());
|
||||
description_string += QString(tr("Dimensions : %1*%2\n")).arg(m_element -> size().width()).arg(m_element -> size().height());
|
||||
description_string += QString(tr("Bornes : %1\n")).arg(m_element -> terminals().count());
|
||||
if (m_element->linkType() == Element::Master){
|
||||
description_string += QString(tr("Nombre maximum de contacts esclaves définis : %1\n")).arg(m_element -> elementData().m_max_slaves);
|
||||
description_string += QString(tr("Nombre de contacts esclaves utilisés : %1\n")).arg(m_element ->linkedElements().count());
|
||||
}
|
||||
description_string += QString(tr("Emplacement : %1\n")).arg(m_element.data()->location().toString());
|
||||
|
||||
// widget himself
|
||||
|
||||
@@ -366,7 +366,7 @@ void MasterPropertiesWidget::on_link_button_clicked()
|
||||
// Show a message box with the actual window as parent to ensure it's on top
|
||||
QMessageBox::warning(this->window(),
|
||||
tr("Nombre maximal d'esclaves atteint."),
|
||||
tr("Cet élément maître ne peut accepter aucun nouvel esclave car il est plein (Limit: %1).").arg(max_slaves));
|
||||
tr("Cet élément maître ne peut plus accepter aucun nouveau contact esclave, la limite fixée a été atteinte (Limite: %1).").arg(max_slaves));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
#include "terminalnumberingdialog.h"
|
||||
#include "ui_terminalnumberingdialog.h"
|
||||
#include "../qetproject.h"
|
||||
#include "../diagram.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../undocommand/changeelementinformationcommand.h"
|
||||
#include <QUndoCommand>
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
* @brief TerminalNumberingDialog::TerminalNumberingDialog
|
||||
* Constructor
|
||||
* @param parent
|
||||
*/
|
||||
TerminalNumberingDialog::TerminalNumberingDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::TerminalNumberingDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalNumberingDialog::~TerminalNumberingDialog
|
||||
* Destructor
|
||||
*/
|
||||
TerminalNumberingDialog::~TerminalNumberingDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalNumberingDialog::isXAxisPriority
|
||||
* @return true if X axis has priority, false if Y axis has priority
|
||||
*/
|
||||
bool TerminalNumberingDialog::isXAxisPriority() const
|
||||
{
|
||||
return ui->rb_priority_x->isChecked();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalNumberingDialog::isAlphanumeric
|
||||
* @return true if alphanumeric sorting is enabled, false if numeric only
|
||||
*/
|
||||
bool TerminalNumberingDialog::isAlphanumeric() const
|
||||
{
|
||||
return ui->rb_type_alpha->isChecked();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TerminalNumberingDialog::getUndoCommand
|
||||
* Scans the given project for terminals, sorts them according to user preferences
|
||||
* (X/Y axis, alphanumeric rules), and generates an undo command containing all label changes.
|
||||
* * @param project Pointer to the current QETProject
|
||||
* @return QUndoCommand* containing the modifications, or nullptr if no changes are needed.
|
||||
*/
|
||||
QUndoCommand* TerminalNumberingDialog::getUndoCommand(QETProject *project) const {
|
||||
if (!project) return nullptr;
|
||||
|
||||
bool axisX = isXAxisPriority();
|
||||
bool alpha = isAlphanumeric();
|
||||
|
||||
// 1. Helper structure to store and sort terminal data
|
||||
struct TermInfo {
|
||||
Element *elmt;
|
||||
QString prefix;
|
||||
QString suffix;
|
||||
int folioIndex;
|
||||
qreal x;
|
||||
qreal y;
|
||||
};
|
||||
QList<TermInfo> termList;
|
||||
|
||||
// 2. Collect all terminals from all folios in the project
|
||||
foreach (Diagram *diagram, project->diagrams()) {
|
||||
int fIndex = diagram->folioIndex();
|
||||
foreach (QGraphicsItem *qgi, diagram->items()) {
|
||||
if (Element *elmt = qgraphicsitem_cast<Element *>(qgi)) {
|
||||
|
||||
// Check if the element is actually a terminal
|
||||
if (elmt->elementData().m_type == ElementData::Terminal) {
|
||||
DiagramContext info = elmt->elementInformations();
|
||||
|
||||
// Ignore locked terminals (if the user checked a 'lock' property)
|
||||
if (info.value(QStringLiteral("auto_num_locked")).toString() == QLatin1String("true")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString label = elmt->actualLabel();
|
||||
if (label.isEmpty()) continue;
|
||||
|
||||
// Split prefix (e.g., "-X1") and suffix (e.g., "1" or "A")
|
||||
QString prefix = label;
|
||||
QString suffix = "";
|
||||
int colonIndex = label.lastIndexOf(':');
|
||||
if (colonIndex != -1) {
|
||||
prefix = label.left(colonIndex);
|
||||
suffix = label.mid(colonIndex + 1);
|
||||
}
|
||||
|
||||
// If user chose purely numeric, skip terminals with alphabetical suffixes
|
||||
if (!alpha && !suffix.isEmpty()) {
|
||||
bool isNum;
|
||||
suffix.toInt(&isNum);
|
||||
if (!isNum) continue;
|
||||
}
|
||||
|
||||
TermInfo ti;
|
||||
ti.elmt = elmt;
|
||||
ti.prefix = prefix;
|
||||
ti.suffix = suffix;
|
||||
ti.folioIndex = fIndex;
|
||||
ti.x = elmt->pos().x();
|
||||
ti.y = elmt->pos().y();
|
||||
termList.append(ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Sort terminals based on user selection (X or Y axis priority)
|
||||
std::sort(termList.begin(), termList.end(), [axisX](const TermInfo &a, const TermInfo &b) {
|
||||
// First sort by BMK Prefix alphabetically (case insensitive)
|
||||
int prefixCmp = a.prefix.compare(b.prefix, Qt::CaseInsensitive);
|
||||
if (prefixCmp != 0) return prefixCmp < 0;
|
||||
|
||||
// Then sort by folio (page) index
|
||||
if (a.folioIndex != b.folioIndex) return a.folioIndex < b.folioIndex;
|
||||
|
||||
// Finally sort by coordinates (with a 1.0px tolerance to handle slight misalignments)
|
||||
if (axisX) {
|
||||
if (qAbs(a.x - b.x) > 1.0) return a.x < b.x;
|
||||
return a.y < b.y;
|
||||
} else {
|
||||
if (qAbs(a.y - b.y) > 1.0) return a.y < b.y;
|
||||
return a.x < b.x;
|
||||
}
|
||||
});
|
||||
|
||||
// 4. Generate new numbering and create the undo command macro
|
||||
QUndoCommand *macro = new QUndoCommand(QObject::tr("Automatic terminal numbering"));
|
||||
QMap<QString, int> counters;
|
||||
|
||||
foreach (const TermInfo &ti, termList) {
|
||||
// Increment the counter for this terminal block (e.g., "-X3")
|
||||
counters[ti.prefix]++;
|
||||
int newNum = counters[ti.prefix];
|
||||
|
||||
// Determine if the original suffix was a pure number
|
||||
QString newLabel;
|
||||
bool isNum;
|
||||
ti.suffix.toInt(&isNum);
|
||||
|
||||
if (isNum || ti.suffix.isEmpty()) {
|
||||
// If it was a number (e.g., "1") or empty, update it with the new counter
|
||||
newLabel = ti.prefix + ":" + QString::number(newNum);
|
||||
} else {
|
||||
// If it was alphabetical (e.g., "N", "PE"), keep the original text but consume the count!
|
||||
newLabel = ti.prefix + ":" + ti.suffix;
|
||||
}
|
||||
|
||||
DiagramContext oldInfo = ti.elmt->elementInformations();
|
||||
DiagramContext newInfo = oldInfo;
|
||||
newInfo.addValue(QStringLiteral("label"), newLabel);
|
||||
|
||||
// Create an undo command only if the label actually changes
|
||||
if (oldInfo != newInfo) {
|
||||
new ChangeElementInformationCommand(ti.elmt, oldInfo, newInfo, macro);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Return the macro if it contains changes, otherwise delete and return null
|
||||
if (macro->childCount() > 0) {
|
||||
return macro;
|
||||
} else {
|
||||
delete macro;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef TERMINALNUMBERINGDIALOG_H
|
||||
#define TERMINALNUMBERINGDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QETProject;
|
||||
class QUndoCommand;
|
||||
|
||||
namespace Ui {
|
||||
class TerminalNumberingDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The TerminalNumberingDialog class
|
||||
* Dialog to configure the automatic numbering of terminals.
|
||||
*/
|
||||
class TerminalNumberingDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TerminalNumberingDialog(QWidget *parent = nullptr);
|
||||
~TerminalNumberingDialog();
|
||||
|
||||
// Getters for the user's choices
|
||||
bool isXAxisPriority() const;
|
||||
bool isAlphanumeric() const;
|
||||
|
||||
QUndoCommand* getUndoCommand(QETProject *project) const;
|
||||
|
||||
private:
|
||||
Ui::TerminalNumberingDialog *ui;
|
||||
};
|
||||
|
||||
#endif // TERMINALNUMBERINGDIALOG_H
|
||||
@@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TerminalNumberingDialog</class>
|
||||
<widget class="QDialog" name="TerminalNumberingDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Numérotation automatique des bornes</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_description">
|
||||
<property name="text">
|
||||
<string>Cette fonction numérote les bornes du projet selon leur position. Les bornes vides ou verrouillées sont ignorées.Le marquage des bornes doit être configuré au préalable comme suit : '-X:AB'. La partie avant les deux-points (le bornier) peut être nommée au choix. 'AB' peut être composé de chiffres ou de lettres."</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_axis">
|
||||
<property name="title">
|
||||
<string>Priorité des axes</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rb_priority_x">
|
||||
<property name="text">
|
||||
<string>Priorité à l'axe X (horizontal)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rb_priority_y">
|
||||
<property name="text">
|
||||
<string>Priorité à l'axe Y (vertical)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_type">
|
||||
<property name="title">
|
||||
<string>Type de numérotation</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rb_type_num">
|
||||
<property name="text">
|
||||
<string>Numérique uniquement (1, 2, 3...)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rb_type_alpha">
|
||||
<property name="text">
|
||||
<string>Alphanumérique (A, B, C... 1, 2...)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>TerminalNumberingDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>TerminalNumberingDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -0,0 +1,290 @@
|
||||
#include "wiringlistexport.h"
|
||||
#include "qetproject.h"
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QTextStream>
|
||||
#include <QDomDocument>
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include <QQueue>
|
||||
#include <QSet>
|
||||
|
||||
WiringListExport::WiringListExport(QETProject *project, QWidget *parent) :
|
||||
QObject(parent),
|
||||
m_project(project),
|
||||
m_parent(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString WiringListExport::normalizeUuid(const QString &u) const
|
||||
{
|
||||
QString res = u;
|
||||
res.remove('{').remove('}');
|
||||
return res.trimmed().toLower();
|
||||
}
|
||||
|
||||
QString WiringListExport::findDiagramFolio(const QDomElement &diagramElem) const
|
||||
{
|
||||
if (diagramElem.isNull()) return "";
|
||||
if (diagramElem.hasAttribute("folio")) return diagramElem.attribute("folio");
|
||||
if (diagramElem.hasAttribute("title")) return diagramElem.attribute("title");
|
||||
return "";
|
||||
}
|
||||
|
||||
QDomElement WiringListExport::climbToDiagram(QDomNode node) const
|
||||
{
|
||||
while (!node.isNull()) {
|
||||
if (node.isElement() && node.toElement().tagName().toLower() == "diagram") {
|
||||
return node.toElement();
|
||||
}
|
||||
node = node.parentNode();
|
||||
}
|
||||
return QDomElement();
|
||||
}
|
||||
|
||||
QMap<QString, ElementInfo> WiringListExport::collectElementsInfo(const QDomElement &root) const
|
||||
{
|
||||
QMap<QString, ElementInfo> infoMap;
|
||||
QDomNodeList elements = root.elementsByTagName("element");
|
||||
|
||||
for (int i = 0; i < elements.size(); ++i) {
|
||||
QDomElement el = elements.at(i).toElement();
|
||||
QString uuid = normalizeUuid(el.attribute("uuid", el.attribute("id", "")));
|
||||
if (uuid.isEmpty()) continue;
|
||||
|
||||
ElementInfo info;
|
||||
info.folio = findDiagramFolio(climbToDiagram(el));
|
||||
|
||||
QDomElement linksNode = el.firstChildElement("links_uuids");
|
||||
if (!linksNode.isNull()) {
|
||||
QDomNodeList linkUuids = linksNode.elementsByTagName("link_uuid");
|
||||
for (int j = 0; j < linkUuids.size(); ++j) {
|
||||
QString luuid = normalizeUuid(linkUuids.at(j).toElement().attribute("uuid"));
|
||||
if (!luuid.isEmpty()) info.links.append(luuid);
|
||||
}
|
||||
}
|
||||
|
||||
QDomElement elInfoNode = el.firstChildElement("elementInformations");
|
||||
if (!elInfoNode.isNull()) {
|
||||
QDomNodeList eics = elInfoNode.elementsByTagName("elementInformation");
|
||||
for (int j = 0; j < eics.size(); ++j) {
|
||||
QDomElement eic = eics.at(j).toElement();
|
||||
QString nameAttr = eic.attribute("name").toLower();
|
||||
if (nameAttr == "label") info.label = eic.text().trimmed();
|
||||
if (nameAttr == "name") info.name = eic.text().trimmed();
|
||||
}
|
||||
}
|
||||
|
||||
QString typeVal = el.attribute("type").toLower();
|
||||
if (typeVal.contains("naechste") || typeVal.contains("vorherige") ||
|
||||
typeVal.contains("next") || typeVal.contains("previous")) {
|
||||
info.isPlaceholder = true;
|
||||
}
|
||||
|
||||
infoMap.insert(uuid, info);
|
||||
}
|
||||
return infoMap;
|
||||
}
|
||||
|
||||
QList<ConductorData> WiringListExport::collectConductors(const QDomElement &root) const
|
||||
{
|
||||
QList<ConductorData> conductors;
|
||||
QDomNodeList conductorNodes = root.elementsByTagName("conductor");
|
||||
|
||||
for (int i = 0; i < conductorNodes.size(); ++i) {
|
||||
QDomElement cond = conductorNodes.at(i).toElement();
|
||||
|
||||
if (cond.attribute("num") == "Brücke") continue;
|
||||
|
||||
ConductorData data;
|
||||
data.index = i;
|
||||
data.el1_uuid = normalizeUuid(cond.attribute("element1", cond.attribute("element1id", "")));
|
||||
data.el2_uuid = normalizeUuid(cond.attribute("element2", cond.attribute("element2id", "")));
|
||||
|
||||
data.element1_label = cond.attribute("element1_label");
|
||||
data.element2_label = cond.attribute("element2_label");
|
||||
data.terminalname1 = cond.attribute("terminalname1");
|
||||
data.terminalname2 = cond.attribute("terminalname2");
|
||||
data.tension_protocol = cond.attribute("tension_protocol");
|
||||
data.conductor_color = cond.attribute("conductor_color");
|
||||
data.conductor_section = cond.attribute("conductor_section");
|
||||
data.function = cond.attribute("function");
|
||||
|
||||
QDomElement diag = climbToDiagram(cond);
|
||||
data.folio = findDiagramFolio(diag);
|
||||
if (data.folio.isEmpty()) data.folio = cond.attribute("folio", cond.attribute("page", ""));
|
||||
|
||||
conductors.append(data);
|
||||
}
|
||||
return conductors;
|
||||
}
|
||||
|
||||
void WiringListExport::resolveEndpoints(QList<ConductorData> &conductors, const QMap<QString, ElementInfo> &elementsInfo) const
|
||||
{
|
||||
QRegularExpression numericLabelRe("^\\d+(\\.\\d+)?$");
|
||||
|
||||
QMap<QString, QList<ConductorData>> el_to_cons;
|
||||
for (const ConductorData &c : conductors) {
|
||||
if (!c.el1_uuid.isEmpty()) el_to_cons[c.el1_uuid].append(c);
|
||||
if (!c.el2_uuid.isEmpty()) el_to_cons[c.el2_uuid].append(c);
|
||||
}
|
||||
|
||||
for (int i = 0; i < conductors.size(); ++i) {
|
||||
ConductorData &c = conductors[i];
|
||||
|
||||
auto resolveSide = [&](const QString &startUuid, QString &outLabel, QString &outTerminal) {
|
||||
if (startUuid.isEmpty() || !elementsInfo.contains(startUuid)) return;
|
||||
|
||||
const ElementInfo &startInfo = elementsInfo[startUuid];
|
||||
if (!startInfo.links.isEmpty() || startInfo.isPlaceholder) {
|
||||
QQueue<QString> q;
|
||||
QSet<QString> visited;
|
||||
q.enqueue(startUuid);
|
||||
visited.insert(startUuid);
|
||||
|
||||
int depth = 0;
|
||||
while (!q.isEmpty() && depth < 3) {
|
||||
int levelSize = q.size();
|
||||
for (int k = 0; k < levelSize; ++k) {
|
||||
QString curr = q.dequeue();
|
||||
|
||||
if (elementsInfo.contains(curr)) {
|
||||
const ElementInfo &currInfo = elementsInfo[curr];
|
||||
|
||||
if (!currInfo.isPlaceholder && !currInfo.label.isEmpty() && !numericLabelRe.match(currInfo.label).hasMatch()) {
|
||||
outLabel = currInfo.label;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const QString &lnk : currInfo.links) {
|
||||
if (!visited.contains(lnk)) {
|
||||
visited.insert(lnk);
|
||||
q.enqueue(lnk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const ConductorData &cond : el_to_cons.value(curr)) {
|
||||
if (cond.index == c.index) continue;
|
||||
|
||||
QString other;
|
||||
QString terminalHint;
|
||||
if (cond.el1_uuid == curr) {
|
||||
other = cond.el2_uuid;
|
||||
terminalHint = cond.terminalname2;
|
||||
} else {
|
||||
other = cond.el1_uuid;
|
||||
terminalHint = cond.terminalname1;
|
||||
}
|
||||
|
||||
if (!other.isEmpty() && !visited.contains(other)) {
|
||||
if (elementsInfo.contains(other)) {
|
||||
const ElementInfo &oInfo = elementsInfo[other];
|
||||
if (!oInfo.isPlaceholder && !oInfo.label.isEmpty() && !numericLabelRe.match(oInfo.label).hasMatch()) {
|
||||
outLabel = oInfo.label;
|
||||
if (outTerminal.isEmpty()) outTerminal = terminalHint;
|
||||
return;
|
||||
}
|
||||
}
|
||||
visited.insert(other);
|
||||
q.enqueue(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
depth++;
|
||||
}
|
||||
} else {
|
||||
if (outLabel.isEmpty()) {
|
||||
outLabel = startInfo.label.isEmpty() ? startInfo.name : startInfo.label;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool p1 = elementsInfo.value(c.el1_uuid).isPlaceholder;
|
||||
bool p2 = elementsInfo.value(c.el2_uuid).isPlaceholder;
|
||||
|
||||
if (c.element1_label.isEmpty() || p1) {
|
||||
if (p1) c.element1_label = "";
|
||||
resolveSide(c.el1_uuid, c.element1_label, c.terminalname1);
|
||||
}
|
||||
if (c.element2_label.isEmpty() || p2) {
|
||||
if (p2) c.element2_label = "";
|
||||
resolveSide(c.el2_uuid, c.element2_label, c.terminalname2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WiringListExport::toCsv()
|
||||
{
|
||||
if (!m_project) return;
|
||||
|
||||
QDomDocument doc = m_project->toXml();
|
||||
|
||||
if (doc.isNull()) {
|
||||
QMessageBox::warning(m_parent, tr("Erreur"), tr("Impossible de lire la structure en mémoire du projet."));
|
||||
return;
|
||||
}
|
||||
|
||||
QFileDialog dialog(m_parent);
|
||||
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
dialog.setWindowTitle(tr("Exporter le plan de câblage"));
|
||||
dialog.setDefaultSuffix("csv");
|
||||
dialog.setNameFilter(tr("Fichiers CSV (*.csv)"));
|
||||
|
||||
if (dialog.exec() != QDialog::Accepted) return;
|
||||
QString fileName = dialog.selectedFiles().first();
|
||||
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::warning(m_parent, tr("Erreur"), tr("Impossible d'ouvrir le fichier pour l'écriture."));
|
||||
return;
|
||||
}
|
||||
|
||||
QMap<QString, ElementInfo> elementsInfo = collectElementsInfo(doc.documentElement());
|
||||
QList<ConductorData> conductors = collectConductors(doc.documentElement());
|
||||
|
||||
resolveEndpoints(conductors, elementsInfo);
|
||||
|
||||
QList<ConductorData> uniqueConductors;
|
||||
QSet<QString> seenConnections;
|
||||
|
||||
for (const ConductorData &c : conductors) {
|
||||
if (c.element1_label.isEmpty() && c.element2_label.isEmpty()) continue;
|
||||
|
||||
QString sideA = c.element1_label + ":" + c.terminalname1;
|
||||
QString sideB = c.element2_label + ":" + c.terminalname2;
|
||||
|
||||
QString key = (sideA < sideB) ? (sideA + "||" + sideB) : (sideB + "||" + sideA);
|
||||
|
||||
if (!seenConnections.contains(key)) {
|
||||
seenConnections.insert(key);
|
||||
uniqueConductors.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
QTextStream out(&file);
|
||||
out << tr("Page", "Wiring list CSV header") << ";"
|
||||
<< tr("Composant 1", "Wiring list CSV header") << ";"
|
||||
<< tr("Borne 1", "Wiring list CSV header") << ";"
|
||||
<< tr("Composant 2", "Wiring list CSV header") << ";"
|
||||
<< tr("Borne 2", "Wiring list CSV header") << ";"
|
||||
<< tr("Tension / Protocole", "Wiring list CSV header") << ";"
|
||||
<< tr("Couleur du fil", "Wiring list CSV header") << ";"
|
||||
<< tr("Section du fil", "Wiring list CSV header") << ";"
|
||||
<< tr("Fonction", "Wiring list CSV header") << "\n";
|
||||
|
||||
for (const ConductorData &c : uniqueConductors) {
|
||||
out << c.folio << ";"
|
||||
<< c.element1_label << ";"
|
||||
<< c.terminalname1 << ";"
|
||||
<< c.element2_label << ";"
|
||||
<< c.terminalname2 << ";"
|
||||
<< c.tension_protocol << ";"
|
||||
<< c.conductor_color << ";"
|
||||
<< c.conductor_section << ";"
|
||||
<< c.function << "\n";
|
||||
}
|
||||
|
||||
file.close();
|
||||
QMessageBox::information(m_parent, tr("Export réussi"), tr("Le plan de câblage a été exporté avec succès !"));
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef WIRINGLISTEXPORT_H
|
||||
#define WIRINGLISTEXPORT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QList>
|
||||
#include <QStringList>
|
||||
|
||||
class QETProject;
|
||||
class QWidget;
|
||||
class QDomElement;
|
||||
class QDomNode;
|
||||
|
||||
// Internal data structures for parsing the XML graph
|
||||
struct ElementInfo {
|
||||
QString folio;
|
||||
QStringList links;
|
||||
QString label;
|
||||
QString name;
|
||||
bool isPlaceholder = false;
|
||||
};
|
||||
|
||||
struct ConductorData {
|
||||
int index = 0;
|
||||
QString el1_uuid;
|
||||
QString el2_uuid;
|
||||
QString element1_label;
|
||||
QString element2_label;
|
||||
QString terminalname1;
|
||||
QString terminalname2;
|
||||
QString tension_protocol;
|
||||
QString conductor_color;
|
||||
QString conductor_section;
|
||||
QString function;
|
||||
QString folio;
|
||||
|
||||
// Resolved endpoints
|
||||
QString chosen_a_uuid;
|
||||
QString chosen_a_label;
|
||||
QString chosen_b_uuid;
|
||||
QString chosen_b_label;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The WiringListExport class
|
||||
* Handles the export of the wiring list (Verdrahtungsplan) to a CSV file.
|
||||
* Automatically resolves links and placeholders to find physical endpoints.
|
||||
*/
|
||||
class WiringListExport : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WiringListExport(QETProject *project, QWidget *parent = nullptr);
|
||||
void toCsv();
|
||||
|
||||
private:
|
||||
QETProject *m_project;
|
||||
QWidget *m_parent;
|
||||
|
||||
QString normalizeUuid(const QString &u) const;
|
||||
QString findDiagramFolio(const QDomElement &diagramElem) const;
|
||||
QDomElement climbToDiagram(QDomNode node) const;
|
||||
|
||||
QMap<QString, ElementInfo> collectElementsInfo(const QDomElement &root) const;
|
||||
QList<ConductorData> collectConductors(const QDomElement &root) const;
|
||||
|
||||
void resolveEndpoints(QList<ConductorData> &conductors, const QMap<QString, ElementInfo> &elementsInfo) const;
|
||||
};
|
||||
|
||||
#endif // WIRINGLISTEXPORT_H
|
||||
Reference in New Issue
Block a user