mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-07-31 16:44:14 +02:00
Implement slave contact groups — label transfer, terminal assignment and UI fixes
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
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 "contactgroupselectiondialog.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QTableWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QHeaderView>
|
||||
#include <QItemSelectionModel>
|
||||
|
||||
ContactGroupSelectionDialog::ContactGroupSelectionDialog(
|
||||
const QVector<ElementData::SlaveContactGroup> &groups,
|
||||
const QSet<int> &usedGroupIndices,
|
||||
const ElementData &slaveData,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
m_used_indices = usedGroupIndices;
|
||||
setWindowTitle(tr("Sélectionner un groupe de contacts"));
|
||||
|
||||
auto *main_layout = new QVBoxLayout(this);
|
||||
|
||||
auto *info_label = new QLabel(
|
||||
tr("Sélectionnez le groupe de contacts à assigner à cet élément esclave :"));
|
||||
main_layout->addWidget(info_label);
|
||||
|
||||
// Determine max terminal count for dynamic columns
|
||||
int max_terminals = 0;
|
||||
for (const auto &g : groups) {
|
||||
if (g.terminalCount > max_terminals)
|
||||
max_terminals = g.terminalCount;
|
||||
}
|
||||
|
||||
// Build column headers: #, Type, Sous-type, Contacts, Bornes, T1, T2, ...
|
||||
QStringList headers;
|
||||
headers << tr("#")
|
||||
<< tr("Type")
|
||||
<< tr("Sous-type")
|
||||
<< tr("Contacts")
|
||||
<< tr("Bornes");
|
||||
for (int t = 0; t < max_terminals; ++t) {
|
||||
headers << tr("T%1").arg(t + 1);
|
||||
}
|
||||
|
||||
m_table = new QTableWidget(groups.size(), headers.size(), this);
|
||||
m_table->setHorizontalHeaderLabels(headers);
|
||||
m_table->setSelectionBehavior(QTableWidget::SelectRows);
|
||||
m_table->setSelectionMode(QTableWidget::SingleSelection);
|
||||
m_table->setEditTriggers(QTableWidget::NoEditTriggers);
|
||||
m_table->verticalHeader()->setVisible(false);
|
||||
m_table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
m_table->horizontalHeader()->setStretchLastSection(false);
|
||||
m_table->verticalHeader()->setDefaultSectionSize(24);
|
||||
|
||||
// Populate table rows
|
||||
for (int row = 0; row < groups.size(); ++row) {
|
||||
const auto &g = groups.at(row);
|
||||
|
||||
auto *num_item = new QTableWidgetItem(QString::number(row + 1));
|
||||
num_item->setTextAlignment(Qt::AlignCenter);
|
||||
m_table->setItem(row, 0, num_item);
|
||||
|
||||
m_table->setItem(row, 1, new QTableWidgetItem(typeToString(g.type)));
|
||||
m_table->setItem(row, 2, new QTableWidgetItem(subtypeToString(g.subtype)));
|
||||
|
||||
auto *ctc_item = new QTableWidgetItem(QString::number(g.contactCount));
|
||||
ctc_item->setTextAlignment(Qt::AlignCenter);
|
||||
m_table->setItem(row, 3, ctc_item);
|
||||
|
||||
auto *term_item = new QTableWidgetItem(QString::number(g.terminalCount));
|
||||
term_item->setTextAlignment(Qt::AlignCenter);
|
||||
m_table->setItem(row, 4, term_item);
|
||||
|
||||
// Fill T1..TN label columns
|
||||
for (int t = 0; t < max_terminals; ++t) {
|
||||
int col = 5 + t;
|
||||
QString label;
|
||||
if (t < g.labels.size()) {
|
||||
label = g.labels.at(t);
|
||||
} else if (t < g.terminalCount) {
|
||||
label = tr("T%1").arg(t + 1);
|
||||
}
|
||||
auto *label_item = new QTableWidgetItem(label);
|
||||
label_item->setTextAlignment(Qt::AlignCenter);
|
||||
m_table->setItem(row, col, label_item);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark used group rows as disabled (grayed out)
|
||||
QFont disabled_font;
|
||||
disabled_font.setStrikeOut(true);
|
||||
QColor disabled_color(Qt::gray);
|
||||
|
||||
for (int row = 0; row < groups.size(); ++row) {
|
||||
bool disabled = false;
|
||||
QString reason;
|
||||
|
||||
if (m_used_indices.contains(row)) {
|
||||
disabled = true;
|
||||
reason = tr("(déjà assigné)");
|
||||
} else {
|
||||
const auto &g = groups.at(row);
|
||||
if (g.type != slaveData.m_slave_state) {
|
||||
disabled = true;
|
||||
reason = tr("(état ne correspond pas)");
|
||||
} else if (g.subtype != slaveData.m_slave_type) {
|
||||
disabled = true;
|
||||
reason = tr("(sous-type ne correspond pas)");
|
||||
} else if (g.contactCount != slaveData.m_contact_count) {
|
||||
disabled = true;
|
||||
reason = tr("(nombre de contacts ne correspond pas)");
|
||||
}
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
m_disabled_rows.insert(row);
|
||||
for (int col = 0; col < m_table->columnCount(); ++col) {
|
||||
auto *item = m_table->item(row, col);
|
||||
if (item) {
|
||||
item->setForeground(disabled_color);
|
||||
item->setFont(disabled_font);
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsSelectable);
|
||||
item->setToolTip(reason);
|
||||
}
|
||||
}
|
||||
// Show circled "?" as a widget in the first column
|
||||
auto *num_item = m_table->item(row, 0);
|
||||
if (num_item) {
|
||||
num_item->setToolTip(reason);
|
||||
auto *question_label = new QLabel(this);
|
||||
question_label->setText("?");
|
||||
question_label->setAlignment(Qt::AlignCenter);
|
||||
question_label->setStyleSheet(
|
||||
"QLabel {"
|
||||
" color: #1a73e8;"
|
||||
" font-weight: bold;"
|
||||
" font-size: 8px;"
|
||||
" border: 1.5px solid #1a73e8;"
|
||||
" border-radius: 7px;"
|
||||
" min-width: 12px; max-width: 12px;"
|
||||
" min-height: 12px; max-height: 12px;"
|
||||
"}");
|
||||
question_label->setToolTip(reason);
|
||||
m_table->setCellWidget(row, 0, question_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Select first available (non-disabled) row
|
||||
int first_available = 0;
|
||||
for (int row = 0; row < groups.size(); ++row) {
|
||||
if (!m_disabled_rows.contains(row)) {
|
||||
first_available = row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_table->selectRow(first_available);
|
||||
main_layout->addWidget(m_table);
|
||||
|
||||
// Calculate width based on column count: base + ~60px per column
|
||||
int table_width = 50 + headers.size() * 65;
|
||||
int table_height = 60 + groups.size() * 26;
|
||||
m_table->setMinimumWidth(table_width);
|
||||
m_table->setMinimumHeight(table_height);
|
||||
|
||||
// Buttons
|
||||
auto *button_layout = new QHBoxLayout();
|
||||
button_layout->addStretch();
|
||||
|
||||
m_ok_button = new QPushButton(tr("OK"), this);
|
||||
button_layout->addWidget(m_ok_button);
|
||||
|
||||
auto *cancel_button = new QPushButton(tr("Annuler"), this);
|
||||
button_layout->addWidget(cancel_button);
|
||||
|
||||
main_layout->addLayout(button_layout);
|
||||
|
||||
// Connections
|
||||
connect(m_ok_button, &QPushButton::clicked, this, [this]() {
|
||||
auto *item = m_table->currentItem();
|
||||
if (item && !m_disabled_rows.contains(item->row())) {
|
||||
m_selected_index = item->row();
|
||||
accept();
|
||||
}
|
||||
});
|
||||
connect(cancel_button, &QPushButton::clicked, this, &QDialog::reject);
|
||||
connect(m_table, &QTableWidget::cellDoubleClicked, this, [this](int row, int) {
|
||||
if (!m_disabled_rows.contains(row)) {
|
||||
m_selected_index = row;
|
||||
accept();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int ContactGroupSelectionDialog::selectedIndex() const
|
||||
{
|
||||
return m_selected_index;
|
||||
}
|
||||
|
||||
QString ContactGroupSelectionDialog::typeToString(ElementData::SlaveState type)
|
||||
{
|
||||
switch (type) {
|
||||
case ElementData::NO: return tr("Normalement ouvert");
|
||||
case ElementData::NC: return tr("Normalement fermé");
|
||||
case ElementData::SW: return tr("Inverseur");
|
||||
case ElementData::Other: return tr("Autre");
|
||||
default: return tr("Inconnu");
|
||||
}
|
||||
}
|
||||
|
||||
QString ContactGroupSelectionDialog::subtypeToString(ElementData::SlaveType subtype)
|
||||
{
|
||||
switch (subtype) {
|
||||
case ElementData::SSimple: return tr("Simple");
|
||||
case ElementData::Power: return tr("Puissance");
|
||||
case ElementData::DelayOn: return tr("Temporisé travail");
|
||||
case ElementData::DelayOff: return tr("Temporisé repos");
|
||||
case ElementData::delayOnOff: return tr("Temporisé travail & repos");
|
||||
default: return tr("Inconnu");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
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 CONTACTGROUPSELECTIONDIALOG_H
|
||||
#define CONTACTGROUPSELECTIONDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVector>
|
||||
#include <QSet>
|
||||
|
||||
#include "../properties/elementdata.h"
|
||||
|
||||
class QTableWidget;
|
||||
class QTableWidgetItem;
|
||||
class QPushButton;
|
||||
|
||||
/**
|
||||
@brief The ContactGroupSelectionDialog class
|
||||
A dialog that displays all slave contact groups defined by a master element
|
||||
in a table format. The user can select one group to assign to a slave element.
|
||||
Columns: #, Type, Subtype, Contacts, Terminals, T1, T2, ..., TN
|
||||
*/
|
||||
class ContactGroupSelectionDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ContactGroupSelectionDialog(
|
||||
const QVector<ElementData::SlaveContactGroup> &groups,
|
||||
const QSet<int> &usedGroupIndices,
|
||||
const ElementData &slaveData,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
int selectedIndex() const;
|
||||
|
||||
private:
|
||||
QTableWidget *m_table = nullptr;
|
||||
QPushButton *m_ok_button = nullptr;
|
||||
int m_selected_index = -1;
|
||||
QSet<int> m_used_indices;
|
||||
QSet<int> m_disabled_rows;
|
||||
|
||||
static QString typeToString(ElementData::SlaveState type);
|
||||
static QString subtypeToString(ElementData::SlaveType subtype);
|
||||
};
|
||||
|
||||
#endif // CONTACTGROUPSELECTIONDIALOG_H
|
||||
@@ -16,6 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "linksingleelementwidget.h"
|
||||
#include "contactgroupselectiondialog.h"
|
||||
#include "../qetgraphicsitem/masterelement.h"
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../diagram.h"
|
||||
@@ -29,6 +30,7 @@
|
||||
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
|
||||
/**
|
||||
@brief LinkSingleElementWidget::LinkSingleElementWidget
|
||||
Default constructor
|
||||
@@ -175,6 +177,7 @@ void LinkSingleElementWidget::apply()
|
||||
m_unlink = false;
|
||||
m_element_to_link = nullptr;
|
||||
m_pending_qtwi = nullptr;
|
||||
m_pending_group_index = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,8 +191,12 @@ QUndoCommand *LinkSingleElementWidget::associatedUndo() const
|
||||
|
||||
if (m_element_to_link || m_unlink)
|
||||
{
|
||||
if (m_element_to_link)
|
||||
if (m_element_to_link) {
|
||||
undo->setLink(m_element_to_link);
|
||||
if (m_pending_group_index >= 0) {
|
||||
undo->setGroupIndex(m_pending_group_index);
|
||||
}
|
||||
}
|
||||
else if (m_unlink)
|
||||
undo->unlinkAll();
|
||||
|
||||
@@ -531,7 +538,36 @@ void LinkSingleElementWidget::linkTriggered()
|
||||
return;
|
||||
|
||||
m_element_to_link = m_qtwi_elmt_hash.value(m_qtwi_at_context_menu);
|
||||
|
||||
m_pending_group_index = -1;
|
||||
|
||||
//If linking a slave to a master with contact groups, show group selection dialog
|
||||
if (m_element->linkType() == Element::Slave
|
||||
&& m_element_to_link
|
||||
&& m_element_to_link->linkType() == Element::Master)
|
||||
{
|
||||
const auto &groups = m_element_to_link->elementData().m_slave_contact_groups;
|
||||
if (!groups.isEmpty())
|
||||
{
|
||||
// Collect already-used group indices from the master
|
||||
QSet<int> used_indices;
|
||||
for (Element *linked : m_element_to_link->linkedElements()) {
|
||||
int idx = m_element_to_link->groupIndexForElement(linked);
|
||||
if (idx >= 0) {
|
||||
used_indices.insert(idx);
|
||||
}
|
||||
}
|
||||
|
||||
ContactGroupSelectionDialog dlg(groups, used_indices,
|
||||
m_element->elementData(), this);
|
||||
if (dlg.exec() == QDialog::Accepted && dlg.selectedIndex() >= 0) {
|
||||
m_pending_group_index = dlg.selectedIndex();
|
||||
} else {
|
||||
m_element_to_link = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_live_edit)
|
||||
{
|
||||
apply();
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "abstractelementpropertieseditorwidget.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QDialog>
|
||||
#include <QComboBox>
|
||||
|
||||
class QTreeWidgetItem;
|
||||
class Element;
|
||||
@@ -101,6 +103,8 @@ class LinkSingleElementWidget : public AbstractElementPropertiesEditorWidget
|
||||
Element *m_showed_element = nullptr,
|
||||
*m_element_to_link = nullptr;
|
||||
|
||||
int m_pending_group_index = -1;
|
||||
|
||||
QMenu *m_context_menu{nullptr};
|
||||
QAction *m_link_action{nullptr},
|
||||
*m_show_qtwi{nullptr},
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "masterpropertieswidget.h"
|
||||
#include "contactgroupselectiondialog.h"
|
||||
#include "../qetproject.h"
|
||||
#include "../diagram.h"
|
||||
#include "../diagramposition.h"
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <QListWidgetItem>
|
||||
#include <QMessageBox>
|
||||
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::MasterPropertiesWidget
|
||||
* Default constructor
|
||||
@@ -176,6 +178,8 @@ void MasterPropertiesWidget::apply()
|
||||
{
|
||||
if (QUndoCommand *undo = associatedUndo())
|
||||
m_element -> diagram() -> undoStack().push(undo);
|
||||
|
||||
m_pending_group_indices.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,8 +228,23 @@ QUndoCommand* MasterPropertiesWidget::associatedUndo() const
|
||||
if (to_link.isEmpty())
|
||||
undo->unlinkAll();
|
||||
else
|
||||
{
|
||||
undo->setLink(to_link);
|
||||
|
||||
//Pass group indices for newly linked slaves
|
||||
if (!m_pending_group_indices.isEmpty())
|
||||
{
|
||||
QMap<Element*, int> indices;
|
||||
for (Element *slave : to_link)
|
||||
{
|
||||
if (m_pending_group_indices.contains(slave))
|
||||
indices[slave] = m_pending_group_indices.value(slave);
|
||||
}
|
||||
if (!indices.isEmpty())
|
||||
undo->setGroupIndices(indices);
|
||||
}
|
||||
}
|
||||
|
||||
return undo;
|
||||
}
|
||||
|
||||
@@ -375,6 +394,38 @@ void MasterPropertiesWidget::on_link_button_clicked()
|
||||
QTreeWidgetItem *qtwi = ui->m_free_tree_widget->currentItem();
|
||||
if (qtwi)
|
||||
{
|
||||
Element *slave_elmt = m_qtwi_hash.value(qtwi);
|
||||
|
||||
//If master has contact groups, show group selection dialog
|
||||
const auto &groups = m_element->elementData().m_slave_contact_groups;
|
||||
if (!groups.isEmpty() && slave_elmt)
|
||||
{
|
||||
// Collect already-used group indices from the master
|
||||
QSet<int> used_indices;
|
||||
for (Element *linked : m_element->linkedElements()) {
|
||||
int idx = m_element->groupIndexForElement(linked);
|
||||
if (idx >= 0) {
|
||||
used_indices.insert(idx);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't mark the current slave as used (it might be relinked)
|
||||
if (slave_elmt->linkedElements().contains(m_element)) {
|
||||
int current_idx = m_element->groupIndexForElement(slave_elmt);
|
||||
if (current_idx >= 0) {
|
||||
used_indices.remove(current_idx);
|
||||
}
|
||||
}
|
||||
|
||||
ContactGroupSelectionDialog dlg(groups, used_indices,
|
||||
slave_elmt->elementData(), this);
|
||||
if (dlg.exec() == QDialog::Accepted && dlg.selectedIndex() >= 0) {
|
||||
m_pending_group_indices[slave_elmt] = dlg.selectedIndex();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ui->m_free_tree_widget->takeTopLevelItem(
|
||||
ui->m_free_tree_widget->indexOfTopLevelItem(qtwi));
|
||||
ui->m_link_tree_widget->insertTopLevelItem(0, qtwi);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
|
||||
#include "abstractelementpropertieseditorwidget.h"
|
||||
|
||||
@@ -77,6 +78,7 @@ class MasterPropertiesWidget : public AbstractElementPropertiesEditorWidget
|
||||
private:
|
||||
Ui::MasterPropertiesWidget *ui;
|
||||
QHash <QTreeWidgetItem *, Element *> m_qtwi_hash;
|
||||
QMap <Element *, int> m_pending_group_indices;
|
||||
QTreeWidgetItem *m_qtwi_at_context_menu = nullptr;
|
||||
QPointer <Element> m_showed_element;
|
||||
QETProject *m_project;
|
||||
|
||||
Reference in New Issue
Block a user