mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-22 09:14:14 +02:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d09041dce | |||
| 1a12d440b2 | |||
| 85f46f2b48 | |||
| 9637420756 | |||
| 6c68c461b1 | |||
| c830101ba6 | |||
| a1c0907811 | |||
| ffd829bb69 | |||
| 957bbe5edb | |||
| ce0ba4a681 | |||
| 8295083f05 | |||
| a2441a6f81 | |||
| c1f9af8544 | |||
| 033c2f93a8 | |||
| 83623a0fa6 | |||
| c85f80bbcf | |||
| f18eda845b | |||
| 73e9473db4 | |||
| 7307128fc3 | |||
| 799ff5573f | |||
| 6f2c66afef | |||
| c39c7414cf | |||
| c33f250910 | |||
| c265f0206c | |||
| 36dd1624d2 | |||
| a583b3c43c | |||
| 1738c3ad6c | |||
| 82026d8f7c | |||
| a494a8fd6c | |||
| 4c038a868b | |||
| 6208c7e5df | |||
| fb0649ceee | |||
| d7c75ea5a5 | |||
| b9153269a4 | |||
| eab9603d8a |
+18
-3
@@ -19,7 +19,7 @@ include(cmake/hoto_update_cmake_message.cmake)
|
||||
cmake_minimum_required(VERSION 3.5...4.2)
|
||||
|
||||
project(qelectrotech
|
||||
VERSION 0.100.1
|
||||
VERSION 0.200.1
|
||||
DESCRIPTION "QET is a CAD/CAE editor focusing on schematics drawing features."
|
||||
HOMEPAGE_URL "https://qelectrotech.org/"
|
||||
LANGUAGES C CXX)
|
||||
@@ -36,7 +36,7 @@ set(QET_DIR ${PROJECT_SOURCE_DIR})
|
||||
# includes below, so every subdirectory and every FetchContent dependency
|
||||
# sees a consistent, already-defined value.
|
||||
if(NOT DEFINED QT_VERSION_MAJOR)
|
||||
set(QT_VERSION_MAJOR 5)
|
||||
set(QT_VERSION_MAJOR 6)
|
||||
endif()
|
||||
|
||||
# Some third-party CMake projects we pull in via FetchContent (e.g.
|
||||
@@ -163,7 +163,22 @@ else()
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
# CFBundleIdentifier must not be empty. CMake's default Info.plist
|
||||
# template fills it from MACOSX_BUNDLE_GUI_IDENTIFIER; with that unset
|
||||
# the bundle ships an empty identifier, LaunchServices never registers
|
||||
# the .app, and AppKit's open/save panel service (which is keyed on the
|
||||
# client's bundle id) silently presents nothing -- every
|
||||
# QFileDialog::getOpenFileName()/getSaveFileName() call returns an empty
|
||||
# string without a panel ever appearing, so File > Open and File > Save
|
||||
# as do nothing at all.
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "org.qelectrotech.QElectroTech"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "QElectroTech"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_COPYRIGHT "Copyright 2006-2026 The QElectroTech Team"
|
||||
)
|
||||
endif()
|
||||
|
||||
# The default build only compiles the tracked .ts files to .qm (lrelease).
|
||||
|
||||
@@ -48,7 +48,7 @@ PROJECT_NAME = QElectroTech
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = " 0.100.0-dev"
|
||||
PROJECT_NUMBER = " 0.200.1-dev"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
||||
@@ -48,8 +48,8 @@ Here are the technical choices made for the software development:
|
||||
* Coding language: [C++](https://en.wikipedia.org/wiki/C%2B%2B)
|
||||
* GUI translations: [Qt Linguist](http://doc.qt.io/qt-5/qtlinguist-index.html)
|
||||
* Version control: [GIT](https://github.com/qelectrotech/qelectrotech-source-mirror.git)
|
||||
* Doxygen documentation :[Doxygen](https://qelectrotech.github.io/qelectrotech-source-mirror/)
|
||||
* QtCreator qch doxygen :[QElectroTech.qch](https://github.com/qelectrotech/qelectrotech-source-mirror/blob/master/doc/QElectroTech.qch)
|
||||
* Doxygen documentation :[Doxygen](https://download.qelectrotech.org/qet/doxygen/html/)
|
||||
* QtCreator qch doxygen :[QElectroTech.qch](https://download.qelectrotech.org/qet/doxygen/QElectroTech.qch)
|
||||
* File format for projects, elements and titleblocks: [XML](http://www.w3schools.com/xml/xml_whatis.asp)
|
||||
* Main development platform: [GNU/Linux](http://getgnulinux.org/en/linux/)
|
||||
* Targeted platforms: Windows, GNU/Linux, Mac OS X, BSDs
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>qelectrotech</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.qelectrotech</string>
|
||||
<string>org.qelectrotech.QElectroTech</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
|
||||
@@ -175,7 +175,6 @@ QET_LANG_DIR="${current_dir}/lang/"
|
||||
QET_EXAMPLES_DIR="${current_dir}/examples/"
|
||||
QET_FONTS_DIR="${current_dir}/fonts/"
|
||||
QET_LICENSES_DIR="${current_dir}/licenses/"
|
||||
LANG_DIR="${current_dir}/lang1/"
|
||||
|
||||
if [ -d "${QET_ELMT_DIR}" ]; then
|
||||
cp -R ${QET_ELMT_DIR} $BUNDLE/Contents/Resources/elements
|
||||
@@ -187,9 +186,6 @@ if [ -d "${QET_LANG_DIR}" ]; then
|
||||
mkdir $BUNDLE/Contents/Resources/lang
|
||||
cp ${current_dir}/lang/*.qm $BUNDLE/Contents/Resources/lang
|
||||
fi
|
||||
if [ -d "${LANG_DIR}" ]; then
|
||||
cp ${current_dir}/lang1/*.qm $BUNDLE/Contents/Resources/lang
|
||||
fi
|
||||
if [ -d "${QET_EXAMPLES_DIR}" ]; then
|
||||
mkdir $BUNDLE/Contents/Resources/examples
|
||||
cp ${current_dir}/examples/*.qet $BUNDLE/Contents/Resources/examples
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 CONTACTUSAGE_H
|
||||
#define CONTACTUSAGE_H
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
@brief The ContactUsage struct
|
||||
How many slave contacts a master element currently uses, broken down
|
||||
by contact type.
|
||||
|
||||
Header-only and free of any graphics dependency so that the counting
|
||||
rules can be unit tested on their own. MasterElement::contactUsage()
|
||||
is the thin wrapper that feeds it the linked elements.
|
||||
|
||||
This counts contacts, which is what tells you how many contacts an
|
||||
auxiliary block must provide. It is deliberately not the count that
|
||||
MasterElement::isFull() uses: a master's max_slaves is a number of
|
||||
slots, and a slave fills exactly one slot however many contacts it
|
||||
carries.
|
||||
|
||||
Two rules are easy to get wrong, and both live here so that every
|
||||
caller gets them right:
|
||||
- a slave stands for as many contacts as its "number" kind
|
||||
information says, so a 4 pole contact counts as 4, not as 1
|
||||
- a changeover contact is counted once, as sw. CrossRefItem's
|
||||
NOElements() and NCElements() both return it, so adding those two
|
||||
lists together would count it twice.
|
||||
*/
|
||||
struct ContactUsage
|
||||
{
|
||||
/**
|
||||
Contact types a slave can declare. Mirrors
|
||||
ElementData::SlaveState, which is not used directly so that this
|
||||
header stays free of the element data dependencies and can be
|
||||
unit tested on its own. MasterElement::contactUsage() maps
|
||||
between the two.
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
NO, ///< Normally open
|
||||
NC, ///< Normally closed
|
||||
SW, ///< Changeover
|
||||
Other ///< Neither of the above
|
||||
};
|
||||
|
||||
int no = 0; ///< Normally open
|
||||
int nc = 0; ///< Normally closed
|
||||
int sw = 0; ///< Changeover
|
||||
int other = 0; ///< Neither of the above
|
||||
|
||||
int total() const { return no + nc + sw + other; }
|
||||
|
||||
/**
|
||||
Add one slave element to the tally.
|
||||
@param type the contact type the slave declares
|
||||
@param contacts how many contacts it stands for. Values below 1
|
||||
are treated as 1: an element which declares no
|
||||
contact count is still one contact.
|
||||
*/
|
||||
void addSlave(Type type, int contacts)
|
||||
{
|
||||
const int n = std::max(1, contacts);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case NO: no += n; break;
|
||||
case NC: nc += n; break;
|
||||
case SW: sw += n; break;
|
||||
case Other: other += n; break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONTACTUSAGE_H
|
||||
@@ -245,7 +245,8 @@ void DynamicTextFieldEditor::fillInfoComboBox()
|
||||
else {
|
||||
strl = QETInformation::elementInfoKeys();
|
||||
|
||||
bool is_plc_slave = (type == ElementData::Slave
|
||||
bool is_slave = (type == ElementData::Slave);
|
||||
bool is_plc_slave = (is_slave
|
||||
&& ed.m_slave_type == ElementData::PLCSlave);
|
||||
|
||||
if (is_plc_slave) {
|
||||
@@ -274,6 +275,10 @@ void DynamicTextFieldEditor::fillInfoComboBox()
|
||||
strl.removeAll(QETInformation::ELMT_PLC_T3);
|
||||
strl.removeAll(QETInformation::ELMT_PLC_T4);
|
||||
}
|
||||
|
||||
if (is_slave) {
|
||||
strl.prepend(QETInformation::ELMT_XREF);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<strl.size();++i) {
|
||||
|
||||
@@ -371,7 +371,7 @@ void ElementPropertiesEditorWidget::on_m_buttonBox_accepted()
|
||||
m_data.m_master_type = ui->m_master_type_cb->currentData().value<ElementData::MasterType>();
|
||||
|
||||
//If the checkbox is checked, save the number; otherwise, -1 (infinity)
|
||||
if (ui->max_slaves_checkbox->isVisible() && ui->max_slaves_checkbox->isChecked()) {
|
||||
if ((m_data.m_master_type == ElementData::Coil || m_data.m_master_type == ElementData::Protection || m_data.m_master_type == ElementData::Commutator) && ui->max_slaves_checkbox->isChecked()) {
|
||||
m_data.m_max_slaves = ui->max_slaves_spinbox->value();
|
||||
} else {
|
||||
m_data.m_max_slaves = -1;
|
||||
|
||||
@@ -36,6 +36,7 @@ XRefProperties::XRefProperties()
|
||||
m_master_label = "%f-%l%c";
|
||||
m_slave_label = "(%f-%l%c)";
|
||||
m_offset = 0;
|
||||
m_slave_offset = 0;
|
||||
m_xref_pos = Qt::AlignBottom;
|
||||
}
|
||||
|
||||
@@ -56,6 +57,8 @@ void XRefProperties::toSettings(QSettings &settings,
|
||||
settings.setValue(prefix % "snapto", snap);
|
||||
int offset = m_offset;
|
||||
settings.setValue(prefix % "offset", offset);
|
||||
int slave_offset = m_slave_offset;
|
||||
settings.setValue(prefix % "slave_offset", slave_offset);
|
||||
QString master_label = m_master_label;
|
||||
settings.setValue(prefix % "master_label", master_label);
|
||||
QString slave_label = m_slave_label;
|
||||
@@ -86,6 +89,7 @@ void XRefProperties::fromSettings(const QSettings &settings,
|
||||
QString snap = settings.value(prefix % "snapto", "label").toString();
|
||||
snap == "bottom"? m_snap_to = Bottom : m_snap_to = Label;
|
||||
m_offset = settings.value(prefix % "offset", "0").toInt();
|
||||
m_slave_offset = settings.value(prefix % "slave_offset", "0").toInt();
|
||||
m_master_label = settings.value(prefix % "master_label", "%f-%l%c").toString();
|
||||
m_slave_label = settings.value(prefix % "slave_label", "(%f-%l%c)").toString();
|
||||
|
||||
@@ -123,6 +127,7 @@ QDomElement XRefProperties::toXml(QDomDocument &xml_document) const
|
||||
|
||||
int offset = m_offset;
|
||||
xml_element.setAttribute("offset", QString::number(offset));
|
||||
xml_element.setAttribute("slave_offset", QString::number(m_slave_offset));
|
||||
QString master_label = m_master_label;
|
||||
xml_element.setAttribute("master_label", master_label);
|
||||
QString slave_label = m_slave_label;
|
||||
@@ -157,6 +162,7 @@ bool XRefProperties::fromXml(const QDomElement &xml_element) {
|
||||
m_xref_pos = Qt::AlignBottom;
|
||||
|
||||
m_offset = xml_element.attribute("offset", "0").toInt();
|
||||
m_slave_offset = xml_element.attribute("slave_offset", "0").toInt();
|
||||
m_master_label = xml_element.attribute("master_label", "%f-%l%c");
|
||||
m_slave_label = xml_element.attribute("slave_label","(%f-%l%c)");
|
||||
foreach (QString key, m_prefix_keys) {
|
||||
@@ -199,6 +205,7 @@ bool XRefProperties::operator ==(const XRefProperties &xrp) const{
|
||||
&& m_prefix == xrp.m_prefix
|
||||
&& m_master_label== xrp.m_master_label
|
||||
&& m_offset == xrp.m_offset
|
||||
&& m_slave_offset== xrp.m_slave_offset
|
||||
&& m_xref_pos == xrp.m_xref_pos
|
||||
&& m_slave_label == xrp.m_slave_label);
|
||||
}
|
||||
|
||||
@@ -80,6 +80,9 @@ class XRefProperties : public PropertiesInterface
|
||||
void setOffset(const int offset) {m_offset = offset;}
|
||||
int offset() const {return m_offset;}
|
||||
|
||||
void setSlaveOffset(const int offset) {m_slave_offset = offset;}
|
||||
int slaveOffset() const {return m_slave_offset;}
|
||||
|
||||
void setKey(QString& key) {m_key = key;}
|
||||
|
||||
private:
|
||||
@@ -93,6 +96,7 @@ class XRefProperties : public PropertiesInterface
|
||||
QString m_master_label;
|
||||
QString m_slave_label;
|
||||
int m_offset;
|
||||
int m_slave_offset;
|
||||
QString m_key;
|
||||
};
|
||||
|
||||
|
||||
@@ -1382,36 +1382,68 @@ void DynamicElementTextItem::updateXref()
|
||||
!m_parent_element.data()->linkedElements().isEmpty())
|
||||
{
|
||||
Element *master_elmt = m_parent_element.data()->linkedElements().first();
|
||||
if(master_elmt && !parentGroup() &&
|
||||
(
|
||||
if(master_elmt && !parentGroup())
|
||||
{
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
||||
|
||||
//Champ de texte: store xref in element informations
|
||||
if(xrp.getXrefPos() == Qt::AlignHCenter)
|
||||
{
|
||||
if(m_text_from == DynamicElementTextItem::ElementInfo && m_info_name == "xref")
|
||||
{
|
||||
QString xref_label = xrp.slaveLabel();
|
||||
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
||||
|
||||
DiagramContext dc = m_parent_element->elementInformations();
|
||||
if(dc.value("xref").toString() != xref_label)
|
||||
{
|
||||
dc.addValue("xref", xref_label);
|
||||
m_parent_element->setElementInformations(dc);
|
||||
}
|
||||
|
||||
//Set up connections for future updates
|
||||
if(m_update_slave_Xref_connection.isEmpty())
|
||||
{
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::xChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::yChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::elementInfoChange, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram(), &Diagram::diagramInformationChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::projectDiagramsOrderChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::diagramRemoved, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::XRefPropertiesChanged, this, &DynamicElementTextItem::updateXref);
|
||||
}
|
||||
return;
|
||||
}
|
||||
//For label/composite text: fall through to cleanup (delete m_slave_Xref_item)
|
||||
}
|
||||
else if(
|
||||
(m_text_from == DynamicElementTextItem::ElementInfo && m_info_name == "label") ||
|
||||
(m_text_from == DynamicElementTextItem::CompositeText && m_composite_text.contains("%{label}"))
|
||||
)
|
||||
)
|
||||
{
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
||||
QString xref_label = xrp.slaveLabel();
|
||||
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
||||
|
||||
if(!m_slave_Xref_item)
|
||||
{
|
||||
m_slave_Xref_item = new QGraphicsTextItem(xref_label, this);
|
||||
m_slave_Xref_item->setFont(QETApp::diagramTextsFont(5));
|
||||
m_slave_Xref_item->setDefaultTextColor(Qt::black);
|
||||
m_slave_Xref_item->installSceneEventFilter(this);
|
||||
QString xref_label = xrp.slaveLabel();
|
||||
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
||||
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::xChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::yChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::elementInfoChange, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram(), &Diagram::diagramInformationChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::projectDiagramsOrderChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::diagramRemoved, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::XRefPropertiesChanged, this, &DynamicElementTextItem::updateXref);
|
||||
if(!m_slave_Xref_item)
|
||||
{
|
||||
m_slave_Xref_item = new QGraphicsTextItem(xref_label, this);
|
||||
m_slave_Xref_item->setFont(QETApp::diagramTextsFont(5));
|
||||
m_slave_Xref_item->setDefaultTextColor(Qt::black);
|
||||
m_slave_Xref_item->installSceneEventFilter(this);
|
||||
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::xChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::yChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::elementInfoChange, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram(), &Diagram::diagramInformationChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::projectDiagramsOrderChanged, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::diagramRemoved, this, &DynamicElementTextItem::updateXref);
|
||||
m_update_slave_Xref_connection << connect(diagram()->project(), &QETProject::XRefPropertiesChanged, this, &DynamicElementTextItem::updateXref);
|
||||
}
|
||||
else
|
||||
m_slave_Xref_item->setPlainText(xref_label);
|
||||
setXref_item(xrp.getXrefPos(), xrp.slaveOffset());
|
||||
return;
|
||||
}
|
||||
else
|
||||
m_slave_Xref_item->setPlainText(xref_label);
|
||||
setXref_item(xrp.getXrefPos());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1428,6 +1460,29 @@ void DynamicElementTextItem::updateXref()
|
||||
delete m_slave_Xref_item;
|
||||
m_slave_Xref_item = nullptr;
|
||||
m_update_slave_Xref_connection.clear();
|
||||
|
||||
//If position changed to Champ de texte, store xref in element info
|
||||
if(m_parent_element->linkType() == Element::Slave &&
|
||||
!m_parent_element->linkedElements().isEmpty())
|
||||
{
|
||||
Element *master_elmt = m_parent_element->linkedElements().first();
|
||||
if(master_elmt && diagram())
|
||||
{
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
||||
if(xrp.getXrefPos() == Qt::AlignHCenter)
|
||||
{
|
||||
QString xref_label = xrp.slaveLabel();
|
||||
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
||||
|
||||
DiagramContext dc = m_parent_element->elementInformations();
|
||||
if(dc.value("xref").toString() != xref_label)
|
||||
{
|
||||
dc.addValue("xref", xref_label);
|
||||
m_parent_element->setElementInformations(dc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1476,7 +1531,7 @@ void DynamicElementTextItem::setPlainText(const QString &text)
|
||||
? nullptr : m_parent_element.data()->linkedElements().first();
|
||||
if (master_elmt) {
|
||||
XRefProperties xrp = diagram()->project()->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
||||
setXref_item(xrp.getXrefPos());
|
||||
setXref_item(xrp.getXrefPos(), xrp.slaveOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1488,44 +1543,44 @@ void DynamicElementTextItem::setTextWidth(qreal width)
|
||||
emit textWidthChanged(width);
|
||||
}
|
||||
|
||||
void DynamicElementTextItem::setXref_item(Qt::AlignmentFlag m_exHrefPos)
|
||||
void DynamicElementTextItem::setXref_item(Qt::AlignmentFlag m_exHrefPos, int slave_offset)
|
||||
{
|
||||
QRectF r = boundingRect();
|
||||
QPointF pos;
|
||||
//QPointF pos(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.top());
|
||||
if (m_exHrefPos == Qt::AlignBottom)
|
||||
{
|
||||
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.bottom());
|
||||
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.bottom() + slave_offset);
|
||||
}
|
||||
else if (m_exHrefPos == Qt::AlignTop)
|
||||
{
|
||||
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.top() - m_slave_Xref_item->boundingRect().height());
|
||||
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.top() - m_slave_Xref_item->boundingRect().height() - slave_offset);
|
||||
}
|
||||
else if (m_exHrefPos == Qt::AlignLeft) //
|
||||
{
|
||||
pos = QPointF(r.left() - m_slave_Xref_item->boundingRect().width(),r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
|
||||
pos = QPointF(r.left() - m_slave_Xref_item->boundingRect().width() - slave_offset,r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
|
||||
}
|
||||
else if (m_exHrefPos == Qt::AlignRight) //
|
||||
{
|
||||
pos = QPointF(r.right() ,r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
|
||||
pos = QPointF(r.right() + slave_offset ,r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
|
||||
}
|
||||
else if (m_exHrefPos == Qt::AlignBaseline) //
|
||||
{
|
||||
if(this->alignment() &Qt::AlignBottom)
|
||||
{
|
||||
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.bottom());
|
||||
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.bottom() + slave_offset);
|
||||
}
|
||||
else if(this->alignment() &Qt::AlignTop)
|
||||
{
|
||||
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.top() - m_slave_Xref_item->boundingRect().height());
|
||||
pos = QPointF(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,r.top() - m_slave_Xref_item->boundingRect().height() - slave_offset);
|
||||
}
|
||||
else if(this->alignment() &Qt::AlignLeft)
|
||||
{
|
||||
pos = QPointF(r.left() - m_slave_Xref_item->boundingRect().width(),r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
|
||||
pos = QPointF(r.left() - m_slave_Xref_item->boundingRect().width() - slave_offset,r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
|
||||
}
|
||||
else if(this->alignment() &Qt::AlignRight)
|
||||
{
|
||||
pos = QPointF(r.right() ,r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
|
||||
pos = QPointF(r.right() + slave_offset ,r.center().y() - m_slave_Xref_item->boundingRect().height()/2);
|
||||
}
|
||||
}
|
||||
m_slave_Xref_item->setPos(pos);
|
||||
|
||||
@@ -112,7 +112,7 @@ class DynamicElementTextItem : public DiagramTextItem
|
||||
void updateXref();
|
||||
void setPlainText(const QString &text);
|
||||
void setTextWidth(qreal width);
|
||||
void setXref_item(Qt::AlignmentFlag m_exHrefPos);
|
||||
void setXref_item(Qt::AlignmentFlag m_exHrefPos, int slave_offset = 0);
|
||||
|
||||
void setKeepVisualRotation(bool set);
|
||||
bool keepVisualRotation() const;
|
||||
|
||||
@@ -263,7 +263,16 @@ void ElementTextItemGroup::updateAlignment()
|
||||
if(m_Xref_item)
|
||||
m_Xref_item->autoPos();
|
||||
if(m_slave_Xref_item)
|
||||
adjustSlaveXrefPos();
|
||||
{
|
||||
int slave_offset = 0;
|
||||
Element *master_elmt = m_parent_element->linkedElements().isEmpty()
|
||||
? nullptr : m_parent_element->linkedElements().first();
|
||||
if (master_elmt && m_parent_element->diagram()) {
|
||||
XRefProperties xrp = m_parent_element->diagram()->project()->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
||||
slave_offset = xrp.slaveOffset();
|
||||
}
|
||||
adjustSlaveXrefPos(slave_offset);
|
||||
}
|
||||
if(m_hold_to_bottom_of_page)
|
||||
autoPos();
|
||||
}
|
||||
@@ -786,12 +795,46 @@ void ElementTextItemGroup::updateXref()
|
||||
!m_parent_element->linkedElements().isEmpty())
|
||||
{
|
||||
Element *master_elmt = m_parent_element->linkedElements().first();
|
||||
XRefProperties xrp = project->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
||||
|
||||
//Champ de texte: store xref in element informations
|
||||
if(xrp.getXrefPos() == Qt::AlignHCenter)
|
||||
{
|
||||
for(DynamicElementTextItem *deti : texts())
|
||||
{
|
||||
if(deti->textFrom() == DynamicElementTextItem::ElementInfo && deti->infoName() == "xref")
|
||||
{
|
||||
QString xref_label = xrp.slaveLabel();
|
||||
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
||||
|
||||
DiagramContext dc = m_parent_element->elementInformations();
|
||||
if(dc.value("xref").toString() != xref_label)
|
||||
{
|
||||
dc.addValue("xref", xref_label);
|
||||
m_parent_element->setElementInformations(dc);
|
||||
}
|
||||
|
||||
//Set up connections for future updates
|
||||
if(m_update_slave_Xref_connection.isEmpty())
|
||||
{
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::xChanged, this, &ElementTextItemGroup::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::yChanged, this, &ElementTextItemGroup::updateXref);
|
||||
m_update_slave_Xref_connection << connect(master_elmt, &Element::elementInfoChange, this, &ElementTextItemGroup::updateXref);
|
||||
m_update_slave_Xref_connection << connect(project, &QETProject::projectDiagramsOrderChanged, this, &ElementTextItemGroup::updateXref);
|
||||
m_update_slave_Xref_connection << connect(project, &QETProject::diagramRemoved, this, &ElementTextItemGroup::updateXref);
|
||||
m_update_slave_Xref_connection << connect(project, &QETProject::XRefPropertiesChanged, this, &ElementTextItemGroup::updateXref);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
//No "xref" text found: fall through to cleanup
|
||||
}
|
||||
|
||||
for(DynamicElementTextItem *deti : texts())
|
||||
{
|
||||
if((deti->textFrom() == DynamicElementTextItem::ElementInfo && deti->infoName() == "label") ||
|
||||
(deti->textFrom() == DynamicElementTextItem::CompositeText && deti->compositeText().contains("%{label")))
|
||||
{
|
||||
XRefProperties xrp = project->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
||||
QString xref_label = xrp.slaveLabel();
|
||||
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
||||
|
||||
@@ -810,7 +853,7 @@ void ElementTextItemGroup::updateXref()
|
||||
else
|
||||
m_slave_Xref_item->setPlainText(xref_label);
|
||||
|
||||
adjustSlaveXrefPos();
|
||||
adjustSlaveXrefPos(xrp.slaveOffset());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -830,14 +873,38 @@ void ElementTextItemGroup::updateXref()
|
||||
delete m_slave_Xref_item;
|
||||
m_slave_Xref_item = nullptr;
|
||||
m_update_slave_Xref_connection.clear();
|
||||
|
||||
//If position changed to Champ de texte, store xref in element info
|
||||
if(m_parent_element->linkType() == Element::Slave &&
|
||||
!m_parent_element->linkedElements().isEmpty() &&
|
||||
m_parent_element->diagram())
|
||||
{
|
||||
Element *master_elmt = m_parent_element->linkedElements().first();
|
||||
if(master_elmt)
|
||||
{
|
||||
XRefProperties xrp = m_parent_element->diagram()->project()->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
||||
if(xrp.getXrefPos() == Qt::AlignHCenter)
|
||||
{
|
||||
QString xref_label = xrp.slaveLabel();
|
||||
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
||||
|
||||
DiagramContext dc = m_parent_element->elementInformations();
|
||||
if(dc.value("xref").toString() != xref_label)
|
||||
{
|
||||
dc.addValue("xref", xref_label);
|
||||
m_parent_element->setElementInformations(dc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ElementTextItemGroup::adjustSlaveXrefPos()
|
||||
void ElementTextItemGroup::adjustSlaveXrefPos(int slave_offset)
|
||||
{
|
||||
QRectF r = boundingRect();
|
||||
QPointF pos(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,
|
||||
r.bottom());
|
||||
r.bottom() + slave_offset);
|
||||
m_slave_Xref_item->setPos(pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ class ElementTextItemGroup : public QObject, public QGraphicsItemGroup
|
||||
|
||||
private:
|
||||
void updateXref();
|
||||
void adjustSlaveXrefPos();
|
||||
void adjustSlaveXrefPos(int slave_offset = 0);
|
||||
void autoPos();
|
||||
|
||||
private:
|
||||
|
||||
@@ -227,6 +227,73 @@ void MasterElement::aboutDeleteXref()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterElement::contactUsage
|
||||
* Count the slave contacts currently linked to this master, by type.
|
||||
* This is the single place where that count is worked out: the cross ref
|
||||
* item, the properties dialog and the link widgets all read it from here,
|
||||
* so they cannot disagree with each other.
|
||||
* @return the per type usage
|
||||
*/
|
||||
namespace {
|
||||
|
||||
/**
|
||||
Map the element data's contact type onto the tally's own, so that
|
||||
the used count and the declared capacity cannot classify the same
|
||||
contact type differently.
|
||||
*/
|
||||
ContactUsage::Type contactType(ElementData::SlaveState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ElementData::NO: return ContactUsage::NO;
|
||||
case ElementData::NC: return ContactUsage::NC;
|
||||
case ElementData::SW: return ContactUsage::SW;
|
||||
case ElementData::Other: break;
|
||||
}
|
||||
|
||||
return ContactUsage::Other;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ContactUsage MasterElement::contactUsage() const
|
||||
{
|
||||
ContactUsage usage;
|
||||
|
||||
for (Element *elmt : connected_elements)
|
||||
{
|
||||
if (!elmt) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ElementData &data = elmt->elementData();
|
||||
usage.addSlave(contactType(data.m_slave_state), data.m_contact_count);
|
||||
}
|
||||
|
||||
return usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterElement::contactCapacity
|
||||
* The contacts this master declares it provides, by type, summed over its
|
||||
* contact groups. A group stands for contactCount contacts of its type.
|
||||
* Returns an empty tally when the element declares no groups, which is the
|
||||
* case for every element in the standard collection today -- callers use
|
||||
* that to decide whether a capacity is worth showing at all.
|
||||
* @return the per type capacity
|
||||
*/
|
||||
ContactUsage MasterElement::contactCapacity() const
|
||||
{
|
||||
ContactUsage capacity;
|
||||
|
||||
for (const auto &group : m_data.m_slave_contact_groups) {
|
||||
capacity.addSlave(contactType(group.type), group.contactCount);
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterElement::isFull
|
||||
* @return true if the master has reached its maximum number of slaves
|
||||
@@ -247,7 +314,10 @@ bool MasterElement::isFull() const
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true if current connected elements reached or exceeded the limit
|
||||
// max_slaves is a number of slots, not of contacts: it sizes the
|
||||
// element's contact group table, and a slave occupies exactly one
|
||||
// group however many contacts that group stands for. So the slots
|
||||
// in use are the linked elements, not the contacts they carry.
|
||||
return connected_elements.size() >= max_slaves;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define MASTERELEMENT_H
|
||||
|
||||
#include "element.h"
|
||||
#include "../contactusage.h"
|
||||
#include <QHash>
|
||||
#include <QMetaObject>
|
||||
|
||||
@@ -47,6 +48,8 @@ class MasterElement : public Element
|
||||
void initLink (QETProject *project) override;
|
||||
QRectF XrefBoundingRect() const;
|
||||
|
||||
ContactUsage contactUsage() const;
|
||||
ContactUsage contactCapacity() const;
|
||||
bool isFull() const; // Check Slave-Limit
|
||||
|
||||
protected:
|
||||
|
||||
@@ -194,8 +194,9 @@ QStringList QETInformation::elementInfoKeys()
|
||||
ELMT_PLC_ADDRESS,
|
||||
ELMT_PLC_FUNCTION,
|
||||
ELMT_PLC_COMMENT,
|
||||
ELMT_PLC_CROSSREF,
|
||||
"exclude_from_bom" };
|
||||
ELMT_PLC_CROSSREF,
|
||||
ELMT_XREF,
|
||||
"exclude_from_bom" };
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -316,6 +317,7 @@ QString QETInformation::translatedInfoKey(const QString &info)
|
||||
else if (info == ELMT_PLC_FUNCTION) return QObject::tr("Fonction PLC");
|
||||
else if (info == ELMT_PLC_COMMENT) return QObject::tr("Commentaire PLC");
|
||||
else if (info == ELMT_PLC_CROSSREF) return QObject::tr("Réf. croisée PLC");
|
||||
else if (info == ELMT_XREF) return QObject::tr("Réf. croisée");
|
||||
else return QString();
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ namespace QETInformation
|
||||
static QString ELMT_SUPPLIER_AUX4 = "supplier_auxiliary4";
|
||||
static QString ELMT_QUANTITY_AUX4 = "quantity_auxiliary4";
|
||||
static QString ELMT_UNITY_AUX4 = "unity_auxiliary4";
|
||||
static QString ELMT_XREF = "xref";
|
||||
|
||||
|
||||
/** Default information related to conductor **/
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "../qetgraphicsitem/dynamicelementtextitem.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../qetgraphicsitem/elementtextitemgroup.h"
|
||||
#include "../qetgraphicsitem/masterelement.h"
|
||||
#include "../qeticons.h"
|
||||
#include "dynamicelementtextitemeditor.h"
|
||||
#include "elementinfowidget.h"
|
||||
@@ -380,9 +381,48 @@ 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());
|
||||
if (m_element->linkType() == Element::Master)
|
||||
{
|
||||
//The declared limit is optional: -1 means the element sets no
|
||||
//limit at all, which is worth saying rather than printing "-1".
|
||||
const int max_slaves = m_element->elementData().m_max_slaves;
|
||||
description_string += max_slaves == -1
|
||||
? QString(tr("Nombre maximum de contacts esclaves définis : non défini\n"))
|
||||
: QString(tr("Nombre maximum de contacts esclaves définis : %1\n")).arg(max_slaves);
|
||||
|
||||
//Left as a count of linked elements: the line above is a number
|
||||
//of slots, and a slave fills one slot however many contacts it
|
||||
//carries, so the two stay in the same unit.
|
||||
description_string += QString(tr("Nombre de contacts esclaves utilisés : %1\n")).arg(m_element->linkedElements().count());
|
||||
|
||||
//The breakdown below is in contacts, not slots: it answers how
|
||||
//many contacts an auxiliary block must provide.
|
||||
const MasterElement *master =
|
||||
static_cast<const MasterElement *>(m_element.data());
|
||||
const ContactUsage usage = master->contactUsage();
|
||||
const ContactUsage capacity = master->contactCapacity();
|
||||
|
||||
if (capacity.total() > 0)
|
||||
{
|
||||
//The element declares contact groups, so it can say not
|
||||
//only what has been used but what it has to offer. A type
|
||||
//used beyond what is declared shows as e.g. "1/0", which
|
||||
//is the point: it says this contact does not fit the part.
|
||||
description_string += QString(tr(" Contacts : NO : %1/%2, NC : %3/%4, inverseurs : %5/%6, autres : %7/%8\n"))
|
||||
.arg(usage.no).arg(capacity.no)
|
||||
.arg(usage.nc).arg(capacity.nc)
|
||||
.arg(usage.sw).arg(capacity.sw)
|
||||
.arg(usage.other).arg(capacity.other);
|
||||
}
|
||||
else if (usage.total() > 0)
|
||||
{
|
||||
//No declared groups, so a plain count of what is in use.
|
||||
description_string += QString(tr(" Contacts : NO : %1, NC : %2, inverseurs : %3, autres : %4\n"))
|
||||
.arg(usage.no)
|
||||
.arg(usage.nc)
|
||||
.arg(usage.sw)
|
||||
.arg(usage.other);
|
||||
}
|
||||
}
|
||||
description_string += QString(tr("Emplacement : %1\n")).arg(m_element.data()->location().toString());
|
||||
|
||||
|
||||
@@ -65,7 +65,17 @@ ClickableImageLabel::ClickableImageLabel(const QImage &sourceImage, QWidget *par
|
||||
*/
|
||||
void ClickableImageLabel::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() != Qt::LeftButton || pixmap().isNull())
|
||||
// QLabel::pixmap() returns a pointer in Qt5 and a value in Qt6.
|
||||
// Qt 5.15 offers the by-value form behind Qt::ReturnByValue; the
|
||||
// pointer overload is deprecated there, so take the by-value one
|
||||
// on both and the difference reduces to the argument.
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
const QPixmap label_pixmap = pixmap(Qt::ReturnByValue);
|
||||
#else
|
||||
const QPixmap label_pixmap = pixmap();
|
||||
#endif
|
||||
|
||||
if (event->button() != Qt::LeftButton || label_pixmap.isNull())
|
||||
return;
|
||||
|
||||
// The label may be larger than its pixmap (layout stretching); the
|
||||
@@ -73,9 +83,9 @@ void ClickableImageLabel::mousePressEvent(QMouseEvent *event)
|
||||
// so the click has to be re-based against the pixmap's own rect
|
||||
// within the label, not the label's own top-left.
|
||||
const QRect pixmapRect(
|
||||
(width() - pixmap().width()) / 2,
|
||||
(height() - pixmap().height()) / 2,
|
||||
pixmap().width(), pixmap().height());
|
||||
(width() - label_pixmap.width()) / 2,
|
||||
(height() - label_pixmap.height()) / 2,
|
||||
label_pixmap.width(), label_pixmap.height());
|
||||
if (!pixmapRect.contains(event->pos()))
|
||||
return;
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
#include "linksingleelementwidget.h"
|
||||
#include "contactgroupselectiondialog.h"
|
||||
#include "../qetgraphicsitem/masterelement.h"
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../diagram.h"
|
||||
#include "../diagramposition.h"
|
||||
@@ -353,8 +352,16 @@ void LinkSingleElementWidget::buildTree()
|
||||
|
||||
QSettings settings;
|
||||
QVariant v = settings.value(QStringLiteral("link-element-widget/report-state"));
|
||||
if(!v.isNull())
|
||||
ui->m_tree_widget->header()->restoreState(v.toByteArray());
|
||||
auto *header = ui->m_tree_widget->header();
|
||||
if (v.isNull() || !header->restoreState(v.toByteArray()))
|
||||
{
|
||||
// Keep logical column IDs stable for saved layouts, but show the
|
||||
// folio identity first even when the candidate has no conductor.
|
||||
for (int column = 5; column < 8; ++column)
|
||||
header->moveSection(header->visualIndex(column), column - 5);
|
||||
ui->m_tree_widget->resizeColumnToContents(5);
|
||||
ui->m_tree_widget->resizeColumnToContents(6);
|
||||
}
|
||||
}
|
||||
|
||||
setUpCompleter();
|
||||
@@ -410,11 +417,12 @@ QVector <QPointer<Element>> LinkSingleElementWidget::availableElements()
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the master is full, we'll remove it from the list!
|
||||
MasterElement *master = static_cast<MasterElement*>(elmt);
|
||||
if (master->isFull()) {
|
||||
elmt_vector.removeAt(i);
|
||||
}
|
||||
// A master at its declared limit stays in the list. Removing
|
||||
// it made a full master indistinguishable from one that does
|
||||
// not exist: the candidate simply was not there, with nothing
|
||||
// to say why. The limit is advisory -- see the prompt in
|
||||
// MasterPropertiesWidget::on_link_button_clicked() -- so the
|
||||
// user decides, rather than the list deciding for them.
|
||||
}
|
||||
}
|
||||
return elmt_vector;
|
||||
|
||||
@@ -307,15 +307,24 @@ void MasterPropertiesWidget::on_link_button_clicked()
|
||||
int max_slaves = max_slaves_variant.toInt();
|
||||
int current_slaves = ui->m_link_tree_widget->topLevelItemCount();
|
||||
|
||||
// If a limit is set and reached
|
||||
// If a limit is set and reached, say so but let the user decide.
|
||||
// The limit records how many contacts the part is expected to
|
||||
// carry; it is not a rule the drawing has to obey, and refusing
|
||||
// the link obstructs drawing a schematic before the hardware has
|
||||
// been chosen.
|
||||
if (max_slaves != -1 && current_slaves >= max_slaves) {
|
||||
|
||||
|
||||
// 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 plus accepter aucun nouveau contact esclave, la limite fixée a été atteinte (Limite: %1).").arg(max_slaves));
|
||||
return;
|
||||
const auto answer = QMessageBox::warning(
|
||||
this->window(),
|
||||
tr("Nombre maximal d'esclaves atteint."),
|
||||
tr("La limite fixée pour cet élément maître est atteinte (Limite: %1).\n\n"
|
||||
"Voulez-vous tout de même lier ce contact esclave ?").arg(max_slaves),
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::Yes);
|
||||
if (answer != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ void XRefPropertiesWidget::buildUi()
|
||||
ui -> m_xrefpos_cb -> addItem(tr("Left"),"left");
|
||||
ui -> m_xrefpos_cb -> addItem(tr("Right"),"right");
|
||||
ui -> m_xrefpos_cb -> addItem(tr("Text alignment"),"alignment");
|
||||
ui -> m_xrefpos_cb -> addItem(tr("Champ de texte"),"text_field");
|
||||
m_previous_type_index = ui -> m_type_cb -> currentIndex();
|
||||
}
|
||||
|
||||
@@ -139,6 +140,7 @@ void XRefPropertiesWidget::saveProperties(int index) {
|
||||
else if(ui->m_xrefpos_cb->itemData(ui->m_xrefpos_cb->currentIndex()).toString() == "left") xrp.setXrefPos(Qt::AlignLeft);
|
||||
else if(ui->m_xrefpos_cb->itemData(ui->m_xrefpos_cb->currentIndex()).toString() == "right") xrp.setXrefPos(Qt::AlignRight);
|
||||
else if(ui->m_xrefpos_cb->itemData(ui->m_xrefpos_cb->currentIndex()).toString() == "alignment") xrp.setXrefPos(Qt::AlignBaseline);
|
||||
else if(ui->m_xrefpos_cb->itemData(ui->m_xrefpos_cb->currentIndex()).toString() == "text_field") xrp.setXrefPos(Qt::AlignHCenter);
|
||||
xrp.setShowPowerContac(ui->m_show_power_cb->isChecked());
|
||||
xrp.setShowTerminalName(ui->m_show_terminal_name_cb->isChecked());
|
||||
xrp.setPrefix("power", ui->m_power_prefix_le->text());
|
||||
@@ -147,6 +149,7 @@ void XRefPropertiesWidget::saveProperties(int index) {
|
||||
xrp.setMasterLabel(ui->m_master_le->text());
|
||||
xrp.setSlaveLabel(ui->m_slave_le->text());
|
||||
xrp.setOffset(ui->m_offset_sb->value());
|
||||
xrp.setSlaveOffset(ui->m_slave_offset_sb->value());
|
||||
|
||||
m_properties.insert(type, xrp);
|
||||
}
|
||||
@@ -177,6 +180,9 @@ void XRefPropertiesWidget::updateDisplay()
|
||||
int offset = xrp.offset();
|
||||
ui->m_offset_sb->setValue(offset);
|
||||
|
||||
int slave_offset = xrp.slaveOffset();
|
||||
ui->m_slave_offset_sb->setValue(slave_offset);
|
||||
|
||||
if (xrp.snapTo() == XRefProperties::Bottom){
|
||||
ui->m_snap_to_cb->setCurrentIndex(ui->m_snap_to_cb->findData("bottom"));
|
||||
ui->m_offset_sb->setEnabled(true);
|
||||
@@ -191,6 +197,7 @@ void XRefPropertiesWidget::updateDisplay()
|
||||
else if(xrp.getXrefPos() == Qt::AlignRight) ui->m_xrefpos_cb->setCurrentIndex(ui->m_xrefpos_cb->findData("right"));
|
||||
else if(xrp.getXrefPos() == Qt::AlignBaseline) ui->m_xrefpos_cb->setCurrentIndex(ui->m_xrefpos_cb->findData("alignment"));
|
||||
else if(xrp.getXrefPos() == Qt::AlignBottom) ui->m_xrefpos_cb->setCurrentIndex(ui->m_xrefpos_cb->findData("bottom"));
|
||||
else if(xrp.getXrefPos() == Qt::AlignHCenter) ui->m_xrefpos_cb->setCurrentIndex(ui->m_xrefpos_cb->findData("text_field"));
|
||||
ui->m_show_power_cb->setChecked(xrp.showPowerContact());
|
||||
ui->m_show_terminal_name_cb->setChecked(xrp.showTerminalName());
|
||||
ui->m_power_prefix_le-> setText(xrp.prefix("power"));
|
||||
|
||||
@@ -90,20 +90,56 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>XRef slave position</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="m_xrefpos_cb"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>XRef slave position</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="m_xrefpos_cb"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Distance label - slave :</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="m_slave_offset_sb">
|
||||
<property name="toolTip">
|
||||
<string>Distance in pixels between the label and the slave cross reference</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string notr="true">px</string>
|
||||
</property>
|
||||
<property name="correctionMode">
|
||||
<enum>QAbstractSpinBox::CorrectToNearestValue</enum>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-50</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
|
||||
@@ -85,3 +85,9 @@ add_test(NAME tst_diagramsortkeys COMMAND tst_diagramsortkeys)
|
||||
target_include_directories(tst_diagramsortkeys PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_diagramsortkeys PRIVATE Qt::Test)
|
||||
|
||||
# contactusage.h is a header-only helper holding the contact counting
|
||||
# rules, so this test builds independently of the rest of the QET sources.
|
||||
add_executable(tst_contactusage tst_contactusage.cpp)
|
||||
add_test(NAME tst_contactusage COMMAND tst_contactusage)
|
||||
target_include_directories(tst_contactusage PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_contactusage PRIVATE Qt::Test)
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
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 <QtTest>
|
||||
|
||||
#include "contactusage.h"
|
||||
|
||||
class tst_contactusage : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
// An empty master uses nothing.
|
||||
void emptyUsesNothing()
|
||||
{
|
||||
ContactUsage usage;
|
||||
|
||||
QCOMPARE(usage.no, 0);
|
||||
QCOMPARE(usage.nc, 0);
|
||||
QCOMPARE(usage.sw, 0);
|
||||
QCOMPARE(usage.other, 0);
|
||||
QCOMPARE(usage.total(), 0);
|
||||
}
|
||||
|
||||
// Each type accumulates into its own field only.
|
||||
void countsEachTypeSeparately()
|
||||
{
|
||||
ContactUsage usage;
|
||||
usage.addSlave(ContactUsage::NO, 1);
|
||||
usage.addSlave(ContactUsage::NO, 1);
|
||||
usage.addSlave(ContactUsage::NC, 1);
|
||||
usage.addSlave(ContactUsage::SW, 1);
|
||||
usage.addSlave(ContactUsage::Other, 1);
|
||||
|
||||
QCOMPARE(usage.no, 2);
|
||||
QCOMPARE(usage.nc, 1);
|
||||
QCOMPARE(usage.sw, 1);
|
||||
QCOMPARE(usage.other, 1);
|
||||
QCOMPARE(usage.total(), 5);
|
||||
}
|
||||
|
||||
// A slave standing for several contacts counts once per contact.
|
||||
// Counting elements rather than contacts made a 4 pole contact
|
||||
// consume a single contact from the master's budget.
|
||||
void countsContactsNotElements()
|
||||
{
|
||||
ContactUsage usage;
|
||||
usage.addSlave(ContactUsage::NO, 4);
|
||||
|
||||
QCOMPARE(usage.no, 4);
|
||||
QCOMPARE(usage.total(), 4);
|
||||
}
|
||||
|
||||
// A changeover is one contact of its own kind, never one NO plus
|
||||
// one NC. CrossRefItem::NOElements() and NCElements() both return
|
||||
// changeovers, so a count built by adding those two lists would
|
||||
// report a single changeover as two contacts.
|
||||
void changeoverIsCountedOnce()
|
||||
{
|
||||
ContactUsage usage;
|
||||
usage.addSlave(ContactUsage::SW, 1);
|
||||
|
||||
QCOMPARE(usage.sw, 1);
|
||||
QCOMPARE(usage.no, 0);
|
||||
QCOMPARE(usage.nc, 0);
|
||||
QCOMPARE(usage.total(), 1);
|
||||
}
|
||||
|
||||
// An element which declares no contact count, or a nonsensical one,
|
||||
// is still a contact.
|
||||
void missingContactCountIsOneContact_data()
|
||||
{
|
||||
QTest::addColumn<int>("declared");
|
||||
|
||||
QTest::newRow("zero") << 0;
|
||||
QTest::newRow("negative") << -1;
|
||||
}
|
||||
|
||||
void missingContactCountIsOneContact()
|
||||
{
|
||||
QFETCH(int, declared);
|
||||
|
||||
ContactUsage usage;
|
||||
usage.addSlave(ContactUsage::NO, declared);
|
||||
|
||||
QCOMPARE(usage.no, 1);
|
||||
QCOMPARE(usage.total(), 1);
|
||||
}
|
||||
|
||||
// A declared capacity is summed across groups, so two NO groups of two
|
||||
// contacts each declare four NO contacts, not two groups.
|
||||
void capacitySumsAcrossGroups()
|
||||
{
|
||||
ContactUsage capacity;
|
||||
capacity.addSlave(ContactUsage::NO, 2);
|
||||
capacity.addSlave(ContactUsage::NO, 2);
|
||||
capacity.addSlave(ContactUsage::NC, 1);
|
||||
|
||||
QCOMPARE(capacity.no, 4);
|
||||
QCOMPARE(capacity.nc, 1);
|
||||
QCOMPARE(capacity.total(), 5);
|
||||
}
|
||||
|
||||
// The mix a coil would actually carry: two single NO, one 4 pole NO,
|
||||
// one NC and one changeover.
|
||||
void tallysARealisticMix()
|
||||
{
|
||||
ContactUsage usage;
|
||||
usage.addSlave(ContactUsage::NO, 1);
|
||||
usage.addSlave(ContactUsage::NO, 1);
|
||||
usage.addSlave(ContactUsage::NO, 4);
|
||||
usage.addSlave(ContactUsage::NC, 1);
|
||||
usage.addSlave(ContactUsage::SW, 1);
|
||||
|
||||
QCOMPARE(usage.no, 6);
|
||||
QCOMPARE(usage.nc, 1);
|
||||
QCOMPARE(usage.sw, 1);
|
||||
QCOMPARE(usage.total(), 8);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_contactusage)
|
||||
#include "tst_contactusage.moc"
|
||||
Reference in New Issue
Block a user