Merge pull request #444 from Kellermorph/master

Follow-up: Address review comments for slave limit feature
This commit is contained in:
Laurent Trinques
2026-04-02 11:47:21 +02:00
committed by GitHub
10 changed files with 305 additions and 173 deletions

View File

@@ -1,4 +1,4 @@
/* /*
Copyright 2006-2026 The QElectroTech Team Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech. This file is part of QElectroTech.
@@ -98,6 +98,16 @@ void ElementPropertiesEditorWidget::upDateInterface()
ui->m_master_type_cb->setCurrentIndex( ui->m_master_type_cb->setCurrentIndex(
ui->m_master_type_cb->findData ( ui->m_master_type_cb->findData (
m_data.m_master_type)); m_data.m_master_type));
// NEU: Checkbox und Zahlenbox für max_slaves einstellen
if (m_data.m_max_slaves == -1) {
ui->max_slaves_checkbox->setChecked(false);
ui->max_slaves_spinbox->setEnabled(false);
} else {
ui->max_slaves_checkbox->setChecked(true);
ui->max_slaves_spinbox->setEnabled(true);
ui->max_slaves_spinbox->setValue(m_data.m_max_slaves);
}
} else if (m_data.m_type == ElementData::Terminal) { } else if (m_data.m_type == ElementData::Terminal) {
ui->m_terminal_type_cb->setCurrentIndex( ui->m_terminal_type_cb->setCurrentIndex(
ui->m_terminal_type_cb->findData( ui->m_terminal_type_cb->findData(
@@ -154,7 +164,10 @@ void ElementPropertiesEditorWidget::setUpInterface()
//Disable the edition of the first column of the information tree //Disable the edition of the first column of the information tree
//by this little workaround //by this little workaround
ui->m_tree->setItemDelegate(new EditorDelegate(this)); ui->m_tree->setItemDelegate(new EditorDelegate(this));
ui->m_tree->header()->resizeSection(0, 150);
// NEU: Checkbox mit der Zahlenbox verbinden (Aktivieren/Deaktivieren)
connect(ui->max_slaves_checkbox, SIGNAL(toggled(bool)), ui->max_slaves_spinbox, SLOT(setEnabled(bool)));
populateTree(); populateTree();
} }
@@ -226,6 +239,13 @@ void ElementPropertiesEditorWidget::on_m_buttonBox_accepted()
} }
else if (m_data.m_type == ElementData::Master) { else if (m_data.m_type == ElementData::Master) {
m_data.m_master_type = ui->m_master_type_cb->currentData().value<ElementData::MasterType>(); 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->isChecked()) {
m_data.m_max_slaves = ui->max_slaves_spinbox->value();
} else {
m_data.m_max_slaves = -1;
}
} }
else if (m_data.m_type == ElementData::Terminal) else if (m_data.m_type == ElementData::Terminal)
{ {

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>527</width> <width>527</width>
<height>442</height> <height>492</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -104,6 +104,23 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="m_master_type_cb"/> <widget class="QComboBox" name="m_master_type_cb"/>
</item> </item>
<item row="1" column="0">
<widget class="QCheckBox" name="max_slaves_checkbox">
<property name="text">
<string>Définir le nombre maximal d'esclaves</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="max_slaves_spinbox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@@ -76,6 +76,16 @@ QDomElement ElementData::kindInfoToXml(QDomDocument &document)
xml_type.appendChild(type_txt); xml_type.appendChild(type_txt);
returned_elmt.appendChild(xml_type); returned_elmt.appendChild(xml_type);
// Save max_slaves only if a specific limit is set (not default -1)
if (m_max_slaves != -1) {
auto xml_max_slaves = document.createElement(QStringLiteral("kindInformation"));
xml_max_slaves.setAttribute(QStringLiteral("name"), QStringLiteral("max_slaves"));
auto max_slaves_txt = document.createTextNode(QString::number(m_max_slaves));
xml_max_slaves.appendChild(max_slaves_txt);
returned_elmt.appendChild(xml_max_slaves);
}
} }
else if (m_type == ElementData::Slave) else if (m_type == ElementData::Slave)
{ {
@@ -558,9 +568,12 @@ void ElementData::kindInfoFromXml(const QDomElement &xml_element)
} }
auto name = dom_elmt.attribute(QStringLiteral("name")); auto name = dom_elmt.attribute(QStringLiteral("name"));
if (m_type == ElementData::Master && if (m_type == ElementData::Master) {
name == QLatin1String("type")) { if (name == QLatin1String("type")) {
m_master_type = masterTypeFromString(dom_elmt.text()); m_master_type = masterTypeFromString(dom_elmt.text());
} else if (name == QLatin1String("max_slaves")) {
m_max_slaves = dom_elmt.text().toInt();
}
} }
else if (m_type == ElementData::Slave ) { else if (m_type == ElementData::Slave ) {
if (name == QLatin1String("type")) { if (name == QLatin1String("type")) {

View File

@@ -134,6 +134,7 @@ class ElementData : public PropertiesInterface
ElementData::Type m_type = ElementData::Simple; ElementData::Type m_type = ElementData::Simple;
ElementData::MasterType m_master_type = ElementData::Coil; ElementData::MasterType m_master_type = ElementData::Coil;
int m_max_slaves{-1};
ElementData::SlaveType m_slave_type = ElementData::SSimple; ElementData::SlaveType m_slave_type = ElementData::SSimple;
ElementData::SlaveState m_slave_state = ElementData::NO; ElementData::SlaveState m_slave_state = ElementData::NO;
@@ -141,7 +142,7 @@ class ElementData : public PropertiesInterface
ElementData::TerminalType m_terminal_type = ElementData::TTGeneric; ElementData::TerminalType m_terminal_type = ElementData::TTGeneric;
ElementData::TerminalFunction m_terminal_function = ElementData::TFGeneric; ElementData::TerminalFunction m_terminal_function = ElementData::TFGeneric;
int m_contact_count = 1; int m_contact_count{1};
DiagramContext m_informations; DiagramContext m_informations;
NamesList m_names_list; NamesList m_names_list;
QString m_drawing_information; QString m_drawing_information;

View File

@@ -1351,6 +1351,7 @@ void DynamicElementTextItem::updateXref()
{ {
m_slave_Xref_item = new QGraphicsTextItem(xref_label, this); m_slave_Xref_item = new QGraphicsTextItem(xref_label, this);
m_slave_Xref_item->setFont(QETApp::diagramTextsFont(5)); m_slave_Xref_item->setFont(QETApp::diagramTextsFont(5));
m_slave_Xref_item->setDefaultTextColor(Qt::black);
m_slave_Xref_item->installSceneEventFilter(this); m_slave_Xref_item->installSceneEventFilter(this);
m_update_slave_Xref_connection << connect(m_master_element.data(), &Element::xChanged, this, &DynamicElementTextItem::updateXref); m_update_slave_Xref_connection << connect(m_master_element.data(), &Element::xChanged, this, &DynamicElementTextItem::updateXref);

View File

@@ -183,3 +183,27 @@ void MasterElement::aboutDeleteXref()
return; return;
} }
} }
/**
* @brief MasterElement::isFull
* @return true if the master has reached its maximum number of slaves
*/
bool MasterElement::isFull() const
{
// Set default value to -1 (unlimited slaves)
int max_slaves = -1;
QVariant max_slaves_variant = kindInformations().value("max_slaves");
// Overwrite default if a valid limit is defined in the element's XML
if (max_slaves_variant.isValid() && !max_slaves_variant.toString().isEmpty()) {
max_slaves = max_slaves_variant.toInt();
}
// If no limit is set (-1), the master is never full
if (max_slaves == -1) {
return false;
}
// Return true if current connected elements reached or exceeded the limit
return connected_elements.size() >= max_slaves;
}

View File

@@ -45,6 +45,8 @@ class MasterElement : public Element
void initLink (QETProject *project) override; void initLink (QETProject *project) override;
QRectF XrefBoundingRect() const; QRectF XrefBoundingRect() const;
bool isFull() const; // Check Slave-Limit
protected: protected:
QVariant itemChange( QVariant itemChange(
GraphicsItemChange change, GraphicsItemChange change,

View File

@@ -16,7 +16,7 @@
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>. along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "linksingleelementwidget.h" #include "linksingleelementwidget.h"
#include "../qetgraphicsitem/masterelement.h"
#include "../qetgraphicsitem/conductor.h" #include "../qetgraphicsitem/conductor.h"
#include "../diagram.h" #include "../diagram.h"
#include "../diagramposition.h" #include "../diagramposition.h"
@@ -386,7 +386,22 @@ QVector <QPointer<Element>> LinkSingleElementWidget::availableElements()
//If element is linked, remove is parent from the list //If element is linked, remove is parent from the list
if(!m_element->isFree()) elmt_vector.removeAll(m_element->linkedElements().first()); if(!m_element->isFree()) elmt_vector.removeAll(m_element->linkedElements().first());
// Filter out all master elements from the list
for (int i = elmt_vector.size() - 1; i >= 0; --i) {
Element *elmt = elmt_vector.at(i);
// If the item in the list is a master
if (elmt->linkType() == Element::Master) {
// We convert the generic element pointer into a MasterElement pointer
MasterElement *master = static_cast<MasterElement*>(elmt);
// If the master is full, we'll remove it from the list!
if (master->isFull()) {
elmt_vector.removeAt(i);
}
}
}
return elmt_vector; return elmt_vector;
} }

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>389</width> <width>389</width>
<height>442</height> <height>460</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -64,6 +64,23 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0" colspan="3">
<widget class="QLabel" name="m_hidden_masters_label">
<property name="text">
<string>Remarque : les éléments maîtres ayant atteint leur nombre maximal d'esclaves sont masqués.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@@ -1,20 +1,20 @@
/* /*
Copyright 2006-2026 The QElectroTech Team * Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech. * This file is part of QElectroTech.
*
QElectroTech is free software: you can redistribute it and/or modify * QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or * the Free Software Foundation, either version 2 of the License, or
(at your option) any later version. * (at your option) any later version.
*
QElectroTech is distributed in the hope that it will be useful, * QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. * GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>. * along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "masterpropertieswidget.h" #include "masterpropertieswidget.h"
#include "../diagram.h" #include "../diagram.h"
@@ -25,17 +25,18 @@
#include "ui_masterpropertieswidget.h" #include "ui_masterpropertieswidget.h"
#include <QListWidgetItem> #include <QListWidgetItem>
#include <QMessageBox>
/** /**
@brief MasterPropertiesWidget::MasterPropertiesWidget * @brief MasterPropertiesWidget::MasterPropertiesWidget
Default constructor * Default constructor
@param elmt * @param elmt
@param parent * @param parent
*/ */
MasterPropertiesWidget::MasterPropertiesWidget(Element *elmt, QWidget *parent) : MasterPropertiesWidget::MasterPropertiesWidget(Element *elmt, QWidget *parent) :
AbstractElementPropertiesEditorWidget(parent), AbstractElementPropertiesEditorWidget(parent),
ui(new Ui::MasterPropertiesWidget), ui(new Ui::MasterPropertiesWidget),
m_project(nullptr) m_project(nullptr)
{ {
ui->setupUi(this); ui->setupUi(this);
@@ -106,9 +107,9 @@ MasterPropertiesWidget::MasterPropertiesWidget(Element *elmt, QWidget *parent) :
} }
/** /**
@brief MasterPropertiesWidget::~MasterPropertiesWidget * @brief MasterPropertiesWidget::~MasterPropertiesWidget
Destructor * Destructor
*/ */
MasterPropertiesWidget::~MasterPropertiesWidget() MasterPropertiesWidget::~MasterPropertiesWidget()
{ {
if (m_showed_element) if (m_showed_element)
@@ -121,10 +122,10 @@ MasterPropertiesWidget::~MasterPropertiesWidget()
} }
/** /**
@brief MasterPropertiesWidget::setElement * @brief MasterPropertiesWidget::setElement
Set the element to be edited * Set the element to be edited
@param element * @param element
*/ */
void MasterPropertiesWidget::setElement(Element *element) void MasterPropertiesWidget::setElement(Element *element)
{ {
if (m_element == element) if (m_element == element)
@@ -164,13 +165,13 @@ void MasterPropertiesWidget::setElement(Element *element)
} }
/** /**
@brief MasterPropertiesWidget::apply * @brief MasterPropertiesWidget::apply
If link between edited element and other change, * If link between edited element and other change,
apply the change with a QUndoCommand (got with method associatedUndo) * apply the change with a QUndoCommand (got with method associatedUndo)
pushed to the stack of element project. * pushed to the stack of element project.
Return true if link change, else false * Return true if link change, else false
@note is void no Return ??? * @note is void no Return ???
*/ */
void MasterPropertiesWidget::apply() void MasterPropertiesWidget::apply()
{ {
if (QUndoCommand *undo = associatedUndo()) if (QUndoCommand *undo = associatedUndo())
@@ -178,9 +179,9 @@ void MasterPropertiesWidget::apply()
} }
/** /**
@brief MasterPropertiesWidget::reset * @brief MasterPropertiesWidget::reset
Reset current widget, clear eveything and rebuild widget. * Reset current widget, clear eveything and rebuild widget.
*/ */
void MasterPropertiesWidget::reset() void MasterPropertiesWidget::reset()
{ {
foreach (QTreeWidgetItem *qtwi, m_qtwi_hash.keys()) foreach (QTreeWidgetItem *qtwi, m_qtwi_hash.keys())
@@ -191,12 +192,12 @@ void MasterPropertiesWidget::reset()
} }
/** /**
@brief MasterPropertiesWidget::associatedUndo * @brief MasterPropertiesWidget::associatedUndo
If link between the edited element and other change, * If link between the edited element and other change,
return a QUndoCommand with this change. * return a QUndoCommand with this change.
If no change return nullptr. * If no change return nullptr.
@return * @return
*/ */
QUndoCommand* MasterPropertiesWidget::associatedUndo() const QUndoCommand* MasterPropertiesWidget::associatedUndo() const
{ {
QList <Element *> to_link; QList <Element *> to_link;
@@ -229,11 +230,11 @@ QUndoCommand* MasterPropertiesWidget::associatedUndo() const
} }
/** /**
@brief MasterPropertiesWidget::setLiveEdit * @brief MasterPropertiesWidget::setLiveEdit
@param live_edit = true : live edit is enable * @param live_edit = true : live edit is enable
else false : live edit is disable. * else false : live edit is disable.
@return always true because live edit is handled by this editor widget * @return always true because live edit is handled by this editor widget
*/ */
bool MasterPropertiesWidget::setLiveEdit(bool live_edit) bool MasterPropertiesWidget::setLiveEdit(bool live_edit)
{ {
m_live_edit = live_edit; m_live_edit = live_edit;
@@ -241,9 +242,9 @@ bool MasterPropertiesWidget::setLiveEdit(bool live_edit)
} }
/** /**
@brief MasterPropertiesWidget::updateUi * @brief MasterPropertiesWidget::updateUi
Build the interface of the widget * Build the interface of the widget
*/ */
void MasterPropertiesWidget::updateUi() void MasterPropertiesWidget::updateUi()
{ {
ui->m_free_tree_widget->clear(); ui->m_free_tree_widget->clear();
@@ -334,9 +335,9 @@ void MasterPropertiesWidget::updateUi()
} }
/** /**
@brief MasterPropertiesWidget::headerCustomContextMenuRequested * @brief MasterPropertiesWidget::headerCustomContextMenuRequested
@param pos * @param pos
*/ */
void MasterPropertiesWidget::headerCustomContextMenuRequested(const QPoint &pos) void MasterPropertiesWidget::headerCustomContextMenuRequested(const QPoint &pos)
{ {
m_context_menu->clear(); m_context_menu->clear();
@@ -345,12 +346,32 @@ void MasterPropertiesWidget::headerCustomContextMenuRequested(const QPoint &pos)
} }
/** /**
@brief MasterPropertiesWidget::on_link_button_clicked * @brief MasterPropertiesWidget::on_link_button_clicked
move current item in the free_list to linked_list * Moves the current item from the free_list to the linked_list,
*/ * provided the master's slave limit has not been reached.
*/
void MasterPropertiesWidget::on_link_button_clicked() void MasterPropertiesWidget::on_link_button_clicked()
{ {
//take the current item from free_list and push it to linked_list // Get the maximum number of allowed slaves from the element's information
QVariant max_slaves_variant = m_element->kindInformations().value("max_slaves");
if (max_slaves_variant.isValid() && !max_slaves_variant.toString().isEmpty()) {
int max_slaves = max_slaves_variant.toInt();
int current_slaves = ui->m_link_tree_widget->topLevelItemCount();
// If a limit is set and reached
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 accepter aucun nouvel esclave car il est plein (Limit: %1).").arg(max_slaves));
return;
}
}
// Move current item from free_list to linked_list
QTreeWidgetItem *qtwi = ui->m_free_tree_widget->currentItem(); QTreeWidgetItem *qtwi = ui->m_free_tree_widget->currentItem();
if (qtwi) if (qtwi)
{ {
@@ -363,10 +384,11 @@ void MasterPropertiesWidget::on_link_button_clicked()
} }
} }
/** /**
@brief MasterPropertiesWidget::on_unlink_button_clicked * @brief MasterPropertiesWidget::on_unlink_button_clicked
move current item in linked_list to free_list * move current item in linked_list to free_list
*/ */
void MasterPropertiesWidget::on_unlink_button_clicked() void MasterPropertiesWidget::on_unlink_button_clicked()
{ {
//take the current item from linked_list and push it to free_list //take the current item from linked_list and push it to free_list
@@ -383,11 +405,11 @@ void MasterPropertiesWidget::on_unlink_button_clicked()
} }
/** /**
@brief MasterPropertiesWidget::showElementFromTWI * @brief MasterPropertiesWidget::showElementFromTWI
Show the element corresponding to the given QTreeWidgetItem * Show the element corresponding to the given QTreeWidgetItem
@param qtwi * @param qtwi
@param column * @param column
*/ */
void MasterPropertiesWidget::showElementFromTWI(QTreeWidgetItem *qtwi, int column) void MasterPropertiesWidget::showElementFromTWI(QTreeWidgetItem *qtwi, int column)
{ {
Q_UNUSED(column); Q_UNUSED(column);
@@ -408,19 +430,19 @@ void MasterPropertiesWidget::showElementFromTWI(QTreeWidgetItem *qtwi, int colum
} }
/** /**
@brief MasterPropertiesWidget::showedElementWasDeleted * @brief MasterPropertiesWidget::showedElementWasDeleted
Set to nullptr the current showed element when he was deleted * Set to nullptr the current showed element when he was deleted
*/ */
void MasterPropertiesWidget::showedElementWasDeleted() void MasterPropertiesWidget::showedElementWasDeleted()
{ {
m_showed_element = nullptr; m_showed_element = nullptr;
} }
/** /**
@brief MasterPropertiesWidget::diagramWasdeletedFromProject * @brief MasterPropertiesWidget::diagramWasdeletedFromProject
This slot is called when a diagram is removed from the parent project * This slot is called when a diagram is removed from the parent project
of edited element to update the content of this widget * of edited element to update the content of this widget
*/ */
void MasterPropertiesWidget::diagramWasdeletedFromProject() void MasterPropertiesWidget::diagramWasdeletedFromProject()
{ {
// We use a timer because if the removed diagram // We use a timer because if the removed diagram
@@ -431,11 +453,11 @@ void MasterPropertiesWidget::diagramWasdeletedFromProject()
} }
/** /**
@brief MasterPropertiesWidget::customContextMenu * @brief MasterPropertiesWidget::customContextMenu
Display a context menu * Display a context menu
@param pos * @param pos
@param i : the tree widget where the context menu was requested. * @param i : the tree widget where the context menu was requested.
*/ */
void MasterPropertiesWidget::customContextMenu(const QPoint &pos, int i) void MasterPropertiesWidget::customContextMenu(const QPoint &pos, int i)
{ {
// add the size of the header to display the topleft of the QMenu // add the size of the header to display the topleft of the QMenu