From 4c1f82f81481ee37ed14cf3d0006ae95d37e2c18 Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Fri, 31 Jul 2026 21:02:07 +0200 Subject: [PATCH 1/2] Fix-PLC-Manager --- sources/autoNum/assignvariables.cpp | 5 + sources/editor/ui/dynamictextfieldeditor.cpp | 12 ++- .../ui/elementpropertieseditorwidget.cpp | 2 +- sources/qetgraphicsitem/element.cpp | 98 +++++++++++++++---- sources/qetgraphicsitem/terminal.cpp | 49 ++++++++-- sources/qetgraphicsitem/terminal.h | 1 + sources/qetinformation.h | 5 + sources/ui/masterpropertieswidget.cpp | 9 +- sources/undocommand/linkelementcommand.cpp | 64 +++++++++--- 9 files changed, 200 insertions(+), 45 deletions(-) diff --git a/sources/autoNum/assignvariables.cpp b/sources/autoNum/assignvariables.cpp index 8f58adff1..4cf4017ae 100644 --- a/sources/autoNum/assignvariables.cpp +++ b/sources/autoNum/assignvariables.cpp @@ -280,6 +280,11 @@ namespace autonum str.replace("%{plc_function}", dc.value("plc_function").toString()); str.replace("%{plc_comment}", dc.value("plc_comment").toString()); str.replace("%{plc_crossref}", dc.value("plc_crossref").toString()); + str.replace("%{plc_tc}", dc.value("plc_tc").toString()); + str.replace("%{plc_t1}", dc.value("plc_t1").toString()); + str.replace("%{plc_t2}", dc.value("plc_t2").toString()); + str.replace("%{plc_t3}", dc.value("plc_t3").toString()); + str.replace("%{plc_t4}", dc.value("plc_t4").toString()); return str; } diff --git a/sources/editor/ui/dynamictextfieldeditor.cpp b/sources/editor/ui/dynamictextfieldeditor.cpp index 636fee3e3..fb23c8141 100644 --- a/sources/editor/ui/dynamictextfieldeditor.cpp +++ b/sources/editor/ui/dynamictextfieldeditor.cpp @@ -240,7 +240,12 @@ void DynamicTextFieldEditor::fillInfoComboBox() QETInformation::ELMT_PLC_ADDRESS, QETInformation::ELMT_PLC_FUNCTION, QETInformation::ELMT_PLC_COMMENT, - QETInformation::ELMT_PLC_CROSSREF + QETInformation::ELMT_PLC_CROSSREF, + QETInformation::ELMT_PLC_TC, + QETInformation::ELMT_PLC_T1, + QETInformation::ELMT_PLC_T2, + QETInformation::ELMT_PLC_T3, + QETInformation::ELMT_PLC_T4 }; strl = plc_keys + strl; } else { @@ -249,6 +254,11 @@ void DynamicTextFieldEditor::fillInfoComboBox() strl.removeAll(QETInformation::ELMT_PLC_FUNCTION); strl.removeAll(QETInformation::ELMT_PLC_COMMENT); strl.removeAll(QETInformation::ELMT_PLC_CROSSREF); + strl.removeAll(QETInformation::ELMT_PLC_TC); + strl.removeAll(QETInformation::ELMT_PLC_T1); + strl.removeAll(QETInformation::ELMT_PLC_T2); + strl.removeAll(QETInformation::ELMT_PLC_T3); + strl.removeAll(QETInformation::ELMT_PLC_T4); } } diff --git a/sources/editor/ui/elementpropertieseditorwidget.cpp b/sources/editor/ui/elementpropertieseditorwidget.cpp index b0cf07018..9c0407d21 100644 --- a/sources/editor/ui/elementpropertieseditorwidget.cpp +++ b/sources/editor/ui/elementpropertieseditorwidget.cpp @@ -780,7 +780,7 @@ void ElementPropertiesEditorWidget::createPlcConfigWidgets() auto *sb = new QSpinBox(m_plc_gb); sb->setMinimum(10); - sb->setMaximum(200); + sb->setMaximum(500); sb->setValue(40); sb->setSuffix(tr(" mm")); m_plc_col_width_spinboxes.append(sb); diff --git a/sources/qetgraphicsitem/element.cpp b/sources/qetgraphicsitem/element.cpp index e8eb71e66..58d00dcd2 100644 --- a/sources/qetgraphicsitem/element.cpp +++ b/sources/qetgraphicsitem/element.cpp @@ -1396,6 +1396,23 @@ void Element::setElementInformations(DiagramContext dc) m_data.m_informations.addValue(QStringLiteral("label"), actual_label); //Update the label if there is a formula } emit elementInfoChange(old_info, m_data.m_informations); + + // Propagate label change to linked PLC slaves + if (m_data.m_type == ElementData::Master && m_data.m_master_type == ElementData::PLC) + { + if (!m_group_index_map.isEmpty()) { + const QString new_label = actualLabel(); + for (auto it = m_group_index_map.constBegin(); it != m_group_index_map.constEnd(); ++it) + { + Element *slave = it.key(); + if (!slave) + continue; + DiagramContext ctx = slave->elementInformations(); + ctx.addValue(QETInformation::ELMT_LABEL, new_label); + slave->setElementInformations(ctx); + } + } + } } /** @@ -1433,7 +1450,9 @@ void Element::setElementData(ElementData data) { const auto &new_plc = m_data.plcMasterData(); bool plc_changed = (old_plc.ios != new_plc.ios); - if (plc_changed && !m_group_index_map.isEmpty()) + bool label_changed = (old_info.value(QStringLiteral("label")) != + m_data.m_informations.value(QStringLiteral("label"))); + if (!m_group_index_map.isEmpty() && (plc_changed || label_changed)) { for (auto it = m_group_index_map.constBegin(); it != m_group_index_map.constEnd(); ++it) { @@ -1442,24 +1461,63 @@ void Element::setElementData(ElementData data) if (!slave || io_idx < 0 || io_idx >= new_plc.ios.size()) continue; const auto &io = new_plc.ios.at(io_idx); - DiagramContext ctx = slave->elementInformations(); - ctx.addValue(QETInformation::ELMT_PLC_TYPE, - ElementData::translatedPlcIOType(io.type)); - ctx.addValue(QETInformation::ELMT_PLC_ADDRESS, io.address); - ctx.addValue(QETInformation::ELMT_PLC_FUNCTION, io.functionText); - ctx.addValue(QETInformation::ELMT_PLC_COMMENT, io.comment); - ctx.addValue(QETInformation::ELMT_PLC_CROSSREF, - [&]() -> QString { - if (!diagram() || !diagram()->project()) - return QString(); - XRefProperties xrp = diagram()->project() - ->defaultXRefProperties("plc"); - autonum::sequentialNumbers seq; - return autonum::AssignVariables::formulaToLabel( - xrp.slaveLabel(), seq, diagram(), this); - }()); - ctx.addValue(QETInformation::ELMT_LABEL, actualLabel()); - slave->setElementInformations(ctx); + + if (plc_changed) + { + DiagramContext ctx = slave->elementInformations(); + ctx.addValue(QETInformation::ELMT_PLC_TYPE, + ElementData::translatedPlcIOType(io.type)); + ctx.addValue(QETInformation::ELMT_PLC_ADDRESS, io.address); + ctx.addValue(QETInformation::ELMT_PLC_FUNCTION, io.functionText); + ctx.addValue(QETInformation::ELMT_PLC_COMMENT, io.comment); + ctx.addValue(QETInformation::ELMT_PLC_CROSSREF, + [&]() -> QString { + if (!diagram() || !diagram()->project()) + return QString(); + XRefProperties xrp = diagram()->project() + ->defaultXRefProperties("plc"); + autonum::sequentialNumbers seq; + return autonum::AssignVariables::formulaToLabel( + xrp.slaveLabel(), seq, diagram(), this); + }()); + ctx.addValue(QETInformation::ELMT_PLC_TC, + QString::number(io.terminalCount)); + for (int t = 0; t < io.terminalCount && t < 4; ++t) + { + QString val = (t < io.terminals.size()) + ? io.terminals.at(t) : QString(); + ctx.addValue( + QStringList({ + QETInformation::ELMT_PLC_T1, + QETInformation::ELMT_PLC_T2, + QETInformation::ELMT_PLC_T3, + QETInformation::ELMT_PLC_T4 + }).at(t), val); + } + slave->setElementInformations(ctx); + + // Update master labels on slave terminals + QList slave_terms = slave->terminals(); + for (int t = 0; t < slave_terms.size(); ++t) + { + if (t < io.terminals.size()) + { + slave_terms.at(t)->setUseMasterLabel(true); + slave_terms.at(t)->setMasterLabelIndex(t); + } + else + { + slave_terms.at(t)->setUseMasterLabel(false); + } + } + } + else if (label_changed) + { + // Only label changed, update the label on the slave + DiagramContext ctx = slave->elementInformations(); + ctx.addValue(QETInformation::ELMT_LABEL, actualLabel()); + slave->setElementInformations(ctx); + } } } } @@ -1900,7 +1958,7 @@ void Element::drawPlcTable(QPainter *painter) } QRectF text_rect = cr.adjusted(1, 0, -1, 0); - painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, cell_text); + painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, cell_text); cx += col_widths[col]; } diff --git a/sources/qetgraphicsitem/terminal.cpp b/sources/qetgraphicsitem/terminal.cpp index 32584aebb..ee041c632 100644 --- a/sources/qetgraphicsitem/terminal.cpp +++ b/sources/qetgraphicsitem/terminal.cpp @@ -289,8 +289,17 @@ void Terminal::paint( painter->save(); painter->translate(label_pos); painter->rotate(d->m_label_rotation); - QRectF text_rect(-text_size.width()/2.0, -text_size.height()/2.0, - text_size.width(), text_size.height()); + + qreal rx = 0, ry = 0; + if (d->m_label_halignment & Qt::AlignLeft) rx = 0; + else if (d->m_label_halignment & Qt::AlignHCenter) rx = -text_size.width() / 2.0; + else if (d->m_label_halignment & Qt::AlignRight) rx = -text_size.width(); + + if (d->m_label_valignment & Qt::AlignTop) ry = 0; + else if (d->m_label_valignment & Qt::AlignVCenter) ry = -text_size.height() / 2.0; + else if (d->m_label_valignment & Qt::AlignBottom) ry = -text_size.height(); + + QRectF text_rect(QPointF(rx, ry), text_size); painter->drawText(text_rect, static_cast(d->m_label_halignment | d->m_label_valignment), display_name); painter->restore(); } else { @@ -307,7 +316,7 @@ void Terminal::paint( if (d->m_label_frame) { painter->drawRect(text_rect.adjusted(-1, -1, 1, 1)); } - painter->drawText(text_rect, static_cast(Qt::AlignLeft | Qt::AlignTop), display_name); + painter->drawText(text_rect, static_cast(d->m_label_halignment | d->m_label_valignment), display_name); } } @@ -816,12 +825,24 @@ QString Terminal::name() const if (elmt->linkType() == Element::Master) { int group_idx = elmt->groupIndexForElement(parent_element_); if (group_idx >= 0) { - const auto &groups = elmt->elementData().m_slave_contact_groups; - if (group_idx < groups.size()) { - int label_idx = d->m_master_label_index; - const QStringList &labels = groups.at(group_idx).labels; - if (label_idx >= 0 && label_idx < labels.size()) { - return labels.at(label_idx); + // For PLC masters, use io.terminals as labels + if (elmt->elementData().m_master_type == ElementData::PLC) { + const auto &plc_data = elmt->elementData().plcMasterData(); + if (group_idx < plc_data.ios.size()) { + int label_idx = d->m_master_label_index; + const QStringList &labels = plc_data.ios.at(group_idx).terminals; + if (label_idx >= 0 && label_idx < labels.size()) { + return labels.at(label_idx); + } + } + } else { + const auto &groups = elmt->elementData().m_slave_contact_groups; + if (group_idx < groups.size()) { + int label_idx = d->m_master_label_index; + const QStringList &labels = groups.at(group_idx).labels; + if (label_idx >= 0 && label_idx < labels.size()) { + return labels.at(label_idx); + } } } } @@ -832,6 +853,16 @@ QString Terminal::name() const return d->m_name; } +/** + @brief Terminal::baseName + Return the original terminal name (T1, T2...) without master label override. + Used for sorting when linking. +*/ +QString Terminal::baseName() const +{ + return d->m_name; +} + /** @brief Terminal::terminalType @return the type of this terminal (Generic, Inner, Outer, No, Nc, Common) diff --git a/sources/qetgraphicsitem/terminal.h b/sources/qetgraphicsitem/terminal.h index eac284553..53606b698 100644 --- a/sources/qetgraphicsitem/terminal.h +++ b/sources/qetgraphicsitem/terminal.h @@ -76,6 +76,7 @@ class Terminal : public QGraphicsObject Element *parentElement () const; QUuid uuid () const; QString name () const; + QString baseName () const; TerminalData::Type terminalType() const; bool useMasterLabel() const { return d->m_use_master_label; } void setUseMasterLabel(bool use); diff --git a/sources/qetinformation.h b/sources/qetinformation.h index 2dd566ffd..2aab78e95 100644 --- a/sources/qetinformation.h +++ b/sources/qetinformation.h @@ -130,6 +130,11 @@ namespace QETInformation static QString ELMT_PLC_FUNCTION = "plc_function"; static QString ELMT_PLC_COMMENT = "plc_comment"; static QString ELMT_PLC_CROSSREF = "plc_crossref"; + static QString ELMT_PLC_TC = "plc_tc"; + static QString ELMT_PLC_T1 = "plc_t1"; + static QString ELMT_PLC_T2 = "plc_t2"; + static QString ELMT_PLC_T3 = "plc_t3"; + static QString ELMT_PLC_T4 = "plc_t4"; diff --git a/sources/ui/masterpropertieswidget.cpp b/sources/ui/masterpropertieswidget.cpp index 25aede52a..d7f4d1182 100644 --- a/sources/ui/masterpropertieswidget.cpp +++ b/sources/ui/masterpropertieswidget.cpp @@ -978,7 +978,7 @@ void MasterPropertiesWidget::plcUpdateDisplaySettings() ElementData::PlcMasterData plc_data = ed.plcMasterData(); plc_data.ios.clear(); - // Read IOs from table + // Read IOs from table, preserving terminal data from original IOs for (int row = 0; row < m_plc_table->rowCount(); ++row) { ElementData::PlcIO io; @@ -1002,6 +1002,13 @@ void MasterPropertiesWidget::plcUpdateDisplaySettings() if (crossref_item) io.crossRef = crossref_item->text(); + // Preserve terminal data from the original IO + if (row < ed.plcMasterData().ios.size()) { + const auto &orig_io = ed.plcMasterData().ios.at(row); + io.terminalCount = orig_io.terminalCount; + io.terminals = orig_io.terminals; + } + plc_data.ios.append(io); } diff --git a/sources/undocommand/linkelementcommand.cpp b/sources/undocommand/linkelementcommand.cpp index 924d11c56..6f52387ea 100644 --- a/sources/undocommand/linkelementcommand.cpp +++ b/sources/undocommand/linkelementcommand.cpp @@ -406,22 +406,46 @@ void LinkElementCommand::makeLink(const QList &element_list) elmt->setGroupIndexForElement(m_element, group_idx); // Set master labels on slave terminals - const auto &groups = elmt->elementData().m_slave_contact_groups; - if (group_idx < groups.size()) + if (elmt->elementData().m_master_type == ElementData::PLC) { - const QStringList &labels = groups.at(group_idx).labels; - QList slave_terms = m_element->terminals(); - // Sort terminals by name (T1, T2, T3...) to match label order - std::sort(slave_terms.begin(), slave_terms.end(), - [](Terminal *a, Terminal *b) { - return a->name() < b->name(); - }); - for (int i = 0; i < slave_terms.size(); ++i) + // For PLC masters, use io.terminals as labels + const auto &plc_data = elmt->elementData().plcMasterData(); + if (group_idx < plc_data.ios.size()) { - if (i < labels.size()) + const QStringList &labels = plc_data.ios.at(group_idx).terminals; + QList slave_terms = m_element->terminals(); + std::sort(slave_terms.begin(), slave_terms.end(), + [](Terminal *a, Terminal *b) { + return a->baseName() < b->baseName(); + }); + for (int i = 0; i < slave_terms.size(); ++i) { - slave_terms.at(i)->setUseMasterLabel(true); - slave_terms.at(i)->setMasterLabelIndex(i); + if (i < labels.size()) + { + slave_terms.at(i)->setUseMasterLabel(true); + slave_terms.at(i)->setMasterLabelIndex(i); + } + } + } + } + else + { + const auto &groups = elmt->elementData().m_slave_contact_groups; + if (group_idx < groups.size()) + { + const QStringList &labels = groups.at(group_idx).labels; + QList slave_terms = m_element->terminals(); + std::sort(slave_terms.begin(), slave_terms.end(), + [](Terminal *a, Terminal *b) { + return a->baseName() < b->baseName(); + }); + for (int i = 0; i < slave_terms.size(); ++i) + { + if (i < labels.size()) + { + slave_terms.at(i)->setUseMasterLabel(true); + slave_terms.at(i)->setMasterLabelIndex(i); + } } } } @@ -443,6 +467,20 @@ void LinkElementCommand::makeLink(const QList &element_list) plcCrossRefText(elmt, m_element)); ctx.addValue(QETInformation::ELMT_LABEL, elmt->actualLabel()); + ctx.addValue(QETInformation::ELMT_PLC_TC, + QString::number(io.terminalCount)); + for (int t = 0; t < io.terminalCount && t < 4; ++t) + { + QString val = (t < io.terminals.size()) + ? io.terminals.at(t) : QString(); + ctx.addValue( + QStringList({ + QETInformation::ELMT_PLC_T1, + QETInformation::ELMT_PLC_T2, + QETInformation::ELMT_PLC_T3, + QETInformation::ELMT_PLC_T4 + }).at(t), val); + } m_element->setElementInformations(ctx); } } From e91bab14fe11f71698ad0163672becee1b8e5e7a Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Sat, 1 Aug 2026 14:26:39 +0200 Subject: [PATCH 2/2] Update --- sources/dataBase/projectdatabase.cpp | 12 +- sources/dataBase/ui/elementquerywidget.cpp | 24 ++- sources/dataBase/ui/elementquerywidget.ui | 170 +++++++++--------- .../ui/elementpropertieseditorwidget.cpp | 4 +- sources/qetgraphicsitem/element.cpp | 14 +- sources/ui/masterpropertieswidget.cpp | 36 +++- 6 files changed, 162 insertions(+), 98 deletions(-) diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index b1cd048bb..eacbeb712 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -388,9 +388,15 @@ void projectDataBase::createElementNomenclatureView() "ei.supplier_auxiliary4 AS supplier_auxiliary4," "ei.quantity_auxiliary4 AS quantity_auxiliary4," "ei.unity_auxiliary4 AS unity_auxiliary4," - "ei.exclude_from_bom AS exclude_from_bom," - - "d.pos AS diagram_position," + "ei.exclude_from_bom AS exclude_from_bom," + + "ei.plc_type AS plc_type," + "ei.plc_address AS plc_address," + "ei.plc_function AS plc_function," + "ei.plc_comment AS plc_comment," + "ei.plc_crossref AS plc_crossref," + + "d.pos AS diagram_position," "e.type AS element_type," "e.sub_type AS element_sub_type," "di.title AS title," diff --git a/sources/dataBase/ui/elementquerywidget.cpp b/sources/dataBase/ui/elementquerywidget.cpp index f7eb0dd46..ccc59fb6d 100644 --- a/sources/dataBase/ui/elementquerywidget.cpp +++ b/sources/dataBase/ui/elementquerywidget.cpp @@ -47,6 +47,7 @@ ElementQueryWidget::ElementQueryWidget(QWidget *parent) : m_button_group.addButton(ui->m_coil_cb, 4); m_button_group.addButton(ui->m_protection_cb, 5); m_button_group.addButton(ui->m_thumbnail_cb, 6); + m_button_group.addButton(ui->m_plc_cb, 7); #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) // ### Qt 6: remove connect(&m_button_group, static_cast(&QButtonGroup::buttonClicked), [this](int id) #else @@ -76,7 +77,7 @@ ElementQueryWidget::ElementQueryWidget(QWidget *parent) : else { int checked = 0; - for (int i=1 ; i<7 ; ++i) { + for (int i=1 ; i<8 ; ++i) { if (m_button_group.button(i)->isChecked()) {++checked;} } @@ -85,7 +86,7 @@ ElementQueryWidget::ElementQueryWidget(QWidget *parent) : case 0 : check_box->setCheckState(Qt::Unchecked); break; - case 6: + case 7: check_box->setCheckState(Qt::Checked); break; default: @@ -187,12 +188,16 @@ void ElementQueryWidget::setQuery(const QString &query) if (ui->m_protection_cb) { ++c; } - ui->m_thumbnail_cb->setChecked (str_type.contains(ElementData::typeToString(ElementData::Thumbnail)) ? true : false); - if (ui->m_thumbnail_cb->isChecked()) { - ++c; - } + ui->m_thumbnail_cb->setChecked (str_type.contains(ElementData::typeToString(ElementData::Thumbnail)) ? true : false); + if (ui->m_thumbnail_cb->isChecked()) { + ++c; + } + ui->m_plc_cb->setChecked (str_type.contains(ElementData::masterTypeToString(ElementData::PLC)) ? true : false); + if (ui->m_plc_cb->isChecked()) { + ++c; + } - if (c == 6) { + if (c == 7) { ui->m_all_cb->setCheckState(Qt::Checked); } else if (c > 0) { ui->m_all_cb->setCheckState(Qt::PartiallyChecked); @@ -366,6 +371,11 @@ QString ElementQueryWidget::queryStr() const if (ui->m_protection_cb->isChecked()) { if (b) where +=" OR"; where += QStringLiteral(" element_sub_type = '") += ElementData::masterTypeToString(ElementData::Protection) += "'"; + b = true; + } + if (ui->m_plc_cb->isChecked()) { + if (b) where +=" OR"; + where += QStringLiteral(" element_sub_type = '") += ElementData::masterTypeToString(ElementData::PLC) += "'"; } where.append(")"); diff --git a/sources/dataBase/ui/elementquerywidget.ui b/sources/dataBase/ui/elementquerywidget.ui index d1b04f227..7f3ae30fb 100644 --- a/sources/dataBase/ui/elementquerywidget.ui +++ b/sources/dataBase/ui/elementquerywidget.ui @@ -234,86 +234,96 @@ 0 - - - Type d'éléments - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - Simples - - - true - - - - - - - Organes de protection - - - true - - - - - - - Tous - - - true - - - - - - - Boutons et commutateurs - - - true - - - - - - - Borniers - - - true - - - - - - - Vignettes - - - true - - - - - - - Contacteurs et relais - - - true - - - - - + + + Type d'éléments + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + Tous + + + true + + + + + + + Simples + + + true + + + + + + + Borniers + + + true + + + + + + + Organes de protection + + + true + + + + + + + Contacteurs et relais + + + true + + + + + + + Vignettes + + + true + + + + + + + Boutons et commutateurs + + + true + + + + + + + Automates (MAE/SPS) + + + true + + + + + diff --git a/sources/editor/ui/elementpropertieseditorwidget.cpp b/sources/editor/ui/elementpropertieseditorwidget.cpp index 9c0407d21..4500a1734 100644 --- a/sources/editor/ui/elementpropertieseditorwidget.cpp +++ b/sources/editor/ui/elementpropertieseditorwidget.cpp @@ -683,7 +683,7 @@ void ElementPropertiesEditorWidget::createPlcConfigWidgets() m_plc_table->horizontalHeader()->resizeSection(2, 150); m_plc_table->horizontalHeader()->resizeSection(3, 150); m_plc_table->horizontalHeader()->resizeSection(4, 100); - m_plc_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_plc_table->setSelectionBehavior(QAbstractItemView::SelectItems); m_plc_table->setSelectionMode(QAbstractItemView::ExtendedSelection); m_plc_table->setMinimumHeight(200); tables_splitter->addWidget(m_plc_table); @@ -697,7 +697,7 @@ void ElementPropertiesEditorWidget::createPlcConfigWidgets() m_plc_terminal_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); m_plc_terminal_table->horizontalHeader()->resizeSection(0, 50); m_plc_terminal_table->horizontalHeader()->resizeSection(1, 80); - m_plc_terminal_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_plc_terminal_table->setSelectionBehavior(QAbstractItemView::SelectItems); m_plc_terminal_table->setSelectionMode(QAbstractItemView::ExtendedSelection); m_plc_terminal_table->setMinimumHeight(200); tables_splitter->addWidget(m_plc_terminal_table); diff --git a/sources/qetgraphicsitem/element.cpp b/sources/qetgraphicsitem/element.cpp index 58d00dcd2..d6d12e312 100644 --- a/sources/qetgraphicsitem/element.cpp +++ b/sources/qetgraphicsitem/element.cpp @@ -1397,16 +1397,20 @@ void Element::setElementInformations(DiagramContext dc) } emit elementInfoChange(old_info, m_data.m_informations); - // Propagate label change to linked PLC slaves + // Propagate label change to linked PLC slaves (label is changed via + // setElementInformations through the undo stack, not via setElementData) if (m_data.m_type == ElementData::Master && m_data.m_master_type == ElementData::PLC) { - if (!m_group_index_map.isEmpty()) { + if (!m_group_index_map.isEmpty()) + { const QString new_label = actualLabel(); for (auto it = m_group_index_map.constBegin(); it != m_group_index_map.constEnd(); ++it) { Element *slave = it.key(); if (!slave) continue; + if (slave->elementInformations().value(QETInformation::ELMT_LABEL).toString() == new_label) + continue; DiagramContext ctx = slave->elementInformations(); ctx.addValue(QETInformation::ELMT_LABEL, new_label); slave->setElementInformations(ctx); @@ -1480,6 +1484,7 @@ void Element::setElementData(ElementData data) return autonum::AssignVariables::formulaToLabel( xrp.slaveLabel(), seq, diagram(), this); }()); + ctx.addValue(QETInformation::ELMT_LABEL, actualLabel()); ctx.addValue(QETInformation::ELMT_PLC_TC, QString::number(io.terminalCount)); for (int t = 0; t < io.terminalCount && t < 4; ++t) @@ -1511,7 +1516,7 @@ void Element::setElementData(ElementData data) } } } - else if (label_changed) + if (label_changed) { // Only label changed, update the label on the slave DiagramContext ctx = slave->elementInformations(); @@ -1958,7 +1963,10 @@ void Element::drawPlcTable(QPainter *painter) } QRectF text_rect = cr.adjusted(1, 0, -1, 0); + painter->save(); + painter->setClipRect(text_rect, Qt::IntersectClip); painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, cell_text); + painter->restore(); cx += col_widths[col]; } diff --git a/sources/ui/masterpropertieswidget.cpp b/sources/ui/masterpropertieswidget.cpp index d7f4d1182..e5952a5e5 100644 --- a/sources/ui/masterpropertieswidget.cpp +++ b/sources/ui/masterpropertieswidget.cpp @@ -216,6 +216,16 @@ void MasterPropertiesWidget::reset() */ QUndoCommand* MasterPropertiesWidget::associatedUndo() const { + // PLC masters manage their slave links via the IO table (setElementData), + // not via the link tree widget. The link tree is always empty for PLC + // masters, so we must not create an unlinkAll command. + if (m_element && + m_element->elementData().m_type == ElementData::Master && + m_element->elementData().m_master_type == ElementData::PLC) + { + return nullptr; + } + QList to_link; QList linked_ = m_element->linkedElements(); @@ -880,6 +890,8 @@ void MasterPropertiesWidget::plcRemoveRow() if (selected.isEmpty()) return; + m_plc_updating = true; + // Remove from bottom to top to preserve indices std::sort(selected.begin(), selected.end(), [](const QModelIndex &a, const QModelIndex &b) { return a.row() > b.row(); }); @@ -887,6 +899,9 @@ void MasterPropertiesWidget::plcRemoveRow() for (const QModelIndex &idx : selected) { m_plc_table->removeRow(idx.row()); } + + m_plc_updating = false; + plcUpdateDisplaySettings(); } /** @@ -902,6 +917,8 @@ void MasterPropertiesWidget::plcMoveRowUp() if (row <= 0) return; + m_plc_updating = true; + // Swap with row above for (int col = 0; col < m_plc_table->columnCount(); ++col) { QWidget *w1 = m_plc_table->cellWidget(row, col); @@ -916,6 +933,8 @@ void MasterPropertiesWidget::plcMoveRowUp() } m_plc_table->setCurrentCell(row - 1, m_plc_table->currentColumn()); + m_plc_updating = false; + plcUpdateDisplaySettings(); } /** @@ -931,6 +950,8 @@ void MasterPropertiesWidget::plcMoveRowDown() if (row < 0 || row >= m_plc_table->rowCount() - 1) return; + m_plc_updating = true; + // Swap with row below for (int col = 0; col < m_plc_table->columnCount(); ++col) { QWidget *w1 = m_plc_table->cellWidget(row, col); @@ -945,6 +966,8 @@ void MasterPropertiesWidget::plcMoveRowDown() } m_plc_table->setCurrentCell(row + 1, m_plc_table->currentColumn()); + m_plc_updating = false; + plcUpdateDisplaySettings(); } /** @@ -978,6 +1001,13 @@ void MasterPropertiesWidget::plcUpdateDisplaySettings() ElementData::PlcMasterData plc_data = ed.plcMasterData(); plc_data.ios.clear(); + // Build address -> original IO lookup to correctly reattach terminal data + // after row reorder (move up/down) or row removal + QHash addr_to_orig_idx; + for (int i = 0; i < ed.plcMasterData().ios.size(); ++i) { + addr_to_orig_idx[ed.plcMasterData().ios.at(i).address] = i; + } + // Read IOs from table, preserving terminal data from original IOs for (int row = 0; row < m_plc_table->rowCount(); ++row) { ElementData::PlcIO io; @@ -1002,9 +1032,9 @@ void MasterPropertiesWidget::plcUpdateDisplaySettings() if (crossref_item) io.crossRef = crossref_item->text(); - // Preserve terminal data from the original IO - if (row < ed.plcMasterData().ios.size()) { - const auto &orig_io = ed.plcMasterData().ios.at(row); + // Preserve terminal data by looking up original IO via address + if (addr_to_orig_idx.contains(io.address)) { + const auto &orig_io = ed.plcMasterData().ios.at(addr_to_orig_idx.value(io.address)); io.terminalCount = orig_io.terminalCount; io.terminals = orig_io.terminals; }