mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-03 10:34:14 +02:00
PLC Manager
This commit is contained in:
@@ -24,9 +24,21 @@
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../undocommand/linkelementcommand.h"
|
||||
#include "ui_masterpropertieswidget.h"
|
||||
#include "../properties/elementdata.h"
|
||||
|
||||
#include <QListWidgetItem>
|
||||
#include <QMessageBox>
|
||||
#include <QTableWidget>
|
||||
#include <QSpinBox>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QClipboard>
|
||||
#include <QApplication>
|
||||
#include <QShortcut>
|
||||
#include <QPushButton>
|
||||
|
||||
|
||||
/**
|
||||
@@ -260,98 +272,7 @@ bool MasterPropertiesWidget::setLiveEdit(bool live_edit)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::updateUi
|
||||
* Build the interface of the widget
|
||||
*/
|
||||
void MasterPropertiesWidget::updateUi()
|
||||
{
|
||||
ui->m_free_tree_widget->clear();
|
||||
ui->m_link_tree_widget->clear();
|
||||
m_qtwi_hash.clear();
|
||||
|
||||
if (Q_UNLIKELY(!m_project))
|
||||
return;
|
||||
|
||||
ElementProvider elmt_prov(m_project);
|
||||
QSettings settings;
|
||||
|
||||
//Build the list of free available element
|
||||
QList <QTreeWidgetItem *> items_list;
|
||||
for(const auto &elmt : elmt_prov.freeElement(ElementData::Slave))
|
||||
{
|
||||
QTreeWidgetItem *qtwi = new QTreeWidgetItem(ui->m_free_tree_widget);
|
||||
qtwi->setIcon(0, elmt->pixmap());
|
||||
|
||||
if(settings.value("genericpanel/folio", false).toBool())
|
||||
{
|
||||
autonum::sequentialNumbers seq;
|
||||
QString F =autonum::AssignVariables::formulaToLabel(
|
||||
elmt->diagram()->border_and_titleblock.folio(),
|
||||
seq,
|
||||
elmt->diagram(),
|
||||
elmt);
|
||||
qtwi->setText(1, F);
|
||||
}
|
||||
else
|
||||
{
|
||||
qtwi->setText(1, QString::number(
|
||||
elmt->diagram()->folioIndex()
|
||||
+ 1));
|
||||
}
|
||||
|
||||
|
||||
qtwi->setText(2, elmt->diagram()->title());
|
||||
qtwi->setText(4, elmt->diagram()->convertPosition(
|
||||
elmt->scenePos()).toString());
|
||||
items_list.append(qtwi);
|
||||
m_qtwi_hash.insert(qtwi, elmt);
|
||||
}
|
||||
|
||||
ui->m_free_tree_widget->addTopLevelItems(items_list);
|
||||
items_list.clear();
|
||||
|
||||
//Build the list of already linked element
|
||||
const QList<Element *> link_list = m_element->linkedElements();
|
||||
for(Element *elmt : link_list)
|
||||
{
|
||||
QTreeWidgetItem *qtwi = new QTreeWidgetItem(ui->m_link_tree_widget);
|
||||
qtwi->setIcon(0, elmt->pixmap());
|
||||
|
||||
if(settings.value("genericpanel/folio", false).toBool())
|
||||
{
|
||||
autonum::sequentialNumbers seq;
|
||||
QString F =autonum::AssignVariables::formulaToLabel(
|
||||
elmt->diagram()->border_and_titleblock.folio(),
|
||||
seq,
|
||||
elmt->diagram(),
|
||||
elmt);
|
||||
qtwi->setText(1, F);
|
||||
}
|
||||
else
|
||||
{
|
||||
qtwi->setText(1, QString::number(
|
||||
elmt->diagram()->folioIndex()
|
||||
+ 1));
|
||||
}
|
||||
|
||||
qtwi->setText(2, elmt->diagram()->title());
|
||||
qtwi->setText(3, elmt->diagram()->convertPosition(
|
||||
elmt->scenePos()).toString());
|
||||
items_list.append(qtwi);
|
||||
m_qtwi_hash.insert(qtwi, elmt);
|
||||
}
|
||||
|
||||
if(items_list.count())
|
||||
ui->m_link_tree_widget->addTopLevelItems(items_list);
|
||||
|
||||
QVariant v = settings.value("link-element-widget/master-state");
|
||||
if(!v.isNull())
|
||||
{
|
||||
ui->m_free_tree_widget->header()->restoreState(v.toByteArray());
|
||||
ui->m_link_tree_widget->header()->restoreState(v.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::headerCustomContextMenuRequested
|
||||
@@ -548,3 +469,564 @@ void MasterPropertiesWidget::customContextMenu(const QPoint &pos, int i)
|
||||
m_context_menu->addAction(m_show_element);
|
||||
m_context_menu->popup(point);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::updateUi
|
||||
* Build the interface of the widget
|
||||
*/
|
||||
void MasterPropertiesWidget::updateUi()
|
||||
{
|
||||
ui->m_free_tree_widget->clear();
|
||||
ui->m_link_tree_widget->clear();
|
||||
m_qtwi_hash.clear();
|
||||
|
||||
// Check if this is a PLC master
|
||||
bool is_plc = m_element &&
|
||||
m_element->elementData().m_type == ElementData::Master &&
|
||||
m_element->elementData().m_master_type == ElementData::PLC;
|
||||
|
||||
// Show/hide normal master UI and PLC UI
|
||||
ui->m_free_tree_widget->setVisible(!is_plc);
|
||||
ui->m_link_tree_widget->setVisible(!is_plc);
|
||||
ui->label->setVisible(!is_plc);
|
||||
ui->label_2->setVisible(!is_plc);
|
||||
ui->link_button->setVisible(!is_plc);
|
||||
ui->unlink_button->setVisible(!is_plc);
|
||||
|
||||
// In PLC mode, make hidden widgets take no space in the grid layout
|
||||
if (is_plc) {
|
||||
for (QWidget *w : {static_cast<QWidget*>(ui->m_free_tree_widget),
|
||||
static_cast<QWidget*>(ui->m_link_tree_widget),
|
||||
static_cast<QWidget*>(ui->label),
|
||||
static_cast<QWidget*>(ui->label_2),
|
||||
static_cast<QWidget*>(ui->link_button),
|
||||
static_cast<QWidget*>(ui->unlink_button)}) {
|
||||
w->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
||||
w->setMinimumSize(0, 0);
|
||||
w->setMaximumSize(0, 0);
|
||||
}
|
||||
for (int col = 0; col < ui->gridLayout->columnCount(); ++col)
|
||||
ui->gridLayout->setColumnStretch(col, col < 2 ? 1 : 0);
|
||||
}
|
||||
|
||||
if (is_plc) {
|
||||
// Create PLC widget if not yet created
|
||||
if (!m_plc_widget) {
|
||||
m_plc_widget = new QWidget(ui->gridLayout->parentWidget());
|
||||
|
||||
auto *plc_layout = new QVBoxLayout(m_plc_widget);
|
||||
plc_layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// Table
|
||||
m_plc_table = new QTableWidget(m_plc_widget);
|
||||
m_plc_table->setColumnCount(5);
|
||||
m_plc_table->setHorizontalHeaderLabels({
|
||||
tr("Type"), tr("Adresse"), tr("Fonction"),
|
||||
tr("Commentaire"), tr("Réf. croisée")
|
||||
});
|
||||
m_plc_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||||
m_plc_table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_plc_table->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
m_plc_table->setMinimumHeight(200);
|
||||
|
||||
plc_layout->addWidget(m_plc_table);
|
||||
|
||||
connect(m_plc_table, &QTableWidget::cellChanged, this, &MasterPropertiesWidget::plcIOCellChanged);
|
||||
|
||||
// Context menu for the PLC table
|
||||
m_plc_table->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_plc_table, &QTableWidget::customContextMenuRequested,
|
||||
this, &MasterPropertiesWidget::plcShowTableContextMenu);
|
||||
|
||||
// Ctrl+V shortcut for paste
|
||||
auto *paste_shortcut = new QShortcut(QKeySequence::Paste, m_plc_table);
|
||||
connect(paste_shortcut, &QShortcut::activated, this, &MasterPropertiesWidget::plcPasteFromClipboard);
|
||||
}
|
||||
|
||||
ui->gridLayout->addWidget(m_plc_widget, 0, 0, 1, 5);
|
||||
m_plc_widget->setVisible(true);
|
||||
|
||||
// Load PLC data into table (block signals to prevent cellChanged during population)
|
||||
m_plc_updating = true;
|
||||
m_plc_table->blockSignals(true);
|
||||
|
||||
// Clear existing rows
|
||||
m_plc_table->setRowCount(0);
|
||||
|
||||
ElementData::PlcMasterData plc_data = m_element->elementData().plcMasterData();
|
||||
|
||||
m_plc_table->setRowCount(plc_data.ios.size());
|
||||
for (int row = 0; row < plc_data.ios.size(); ++row) {
|
||||
const ElementData::PlcIO &io = plc_data.ios.at(row);
|
||||
|
||||
// Type combo (read-only — type is defined in the editor)
|
||||
auto *type_cb = new QComboBox(m_plc_table);
|
||||
type_cb->setEnabled(false);
|
||||
type_cb->setStyleSheet("QComboBox { background-color: #f0f0f0; }");
|
||||
QStringList plc_types = ElementData::plcIOTypeList();
|
||||
for (int t = 0; t < plc_types.size(); ++t) {
|
||||
type_cb->addItem(plc_types.at(t), t);
|
||||
}
|
||||
type_cb->setCurrentIndex(static_cast<int>(io.type));
|
||||
m_plc_table->setCellWidget(row, 0, type_cb);
|
||||
|
||||
// Address
|
||||
auto *addr_item = new QTableWidgetItem(io.address);
|
||||
m_plc_table->setItem(row, 1, addr_item);
|
||||
|
||||
// Function text
|
||||
auto *func_item = new QTableWidgetItem(io.functionText);
|
||||
m_plc_table->setItem(row, 2, func_item);
|
||||
|
||||
// Comment
|
||||
auto *comment_item = new QTableWidgetItem(io.comment);
|
||||
m_plc_table->setItem(row, 3, comment_item);
|
||||
|
||||
// CrossRef (read-only)
|
||||
auto *crossref_item = new QTableWidgetItem(io.crossRef);
|
||||
crossref_item->setFlags(crossref_item->flags() & ~Qt::ItemIsEditable);
|
||||
m_plc_table->setItem(row, 4, crossref_item);
|
||||
}
|
||||
|
||||
m_plc_table->blockSignals(false);
|
||||
m_plc_updating = false;
|
||||
} else {
|
||||
// Hide PLC widget if it was shown before
|
||||
if (m_plc_widget)
|
||||
m_plc_widget->setVisible(false);
|
||||
|
||||
// Restore normal widget size policies
|
||||
for (QWidget *w : {static_cast<QWidget*>(ui->m_free_tree_widget),
|
||||
static_cast<QWidget*>(ui->m_link_tree_widget),
|
||||
static_cast<QWidget*>(ui->label),
|
||||
static_cast<QWidget*>(ui->label_2),
|
||||
static_cast<QWidget*>(ui->link_button),
|
||||
static_cast<QWidget*>(ui->unlink_button)}) {
|
||||
w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
w->setMinimumSize(0, 0);
|
||||
w->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
}
|
||||
for (int col = 0; col < ui->gridLayout->columnCount(); ++col)
|
||||
ui->gridLayout->setColumnStretch(col, 0);
|
||||
|
||||
if (Q_UNLIKELY(!m_project))
|
||||
return;
|
||||
|
||||
ElementProvider elmt_prov(m_project);
|
||||
QSettings settings;
|
||||
|
||||
//Build the list of free available element
|
||||
QList <QTreeWidgetItem *> items_list;
|
||||
for(const auto &elmt : elmt_prov.freeElement(ElementData::Slave))
|
||||
{
|
||||
// Filter out PLC-Slave elements — they should only link to PLC-Master
|
||||
if (elmt->elementData().m_slave_type == ElementData::PLCSlave)
|
||||
continue;
|
||||
|
||||
QTreeWidgetItem *qtwi = new QTreeWidgetItem(ui->m_free_tree_widget);
|
||||
qtwi->setIcon(0, elmt->pixmap());
|
||||
|
||||
if(settings.value("genericpanel/folio", false).toBool())
|
||||
{
|
||||
autonum::sequentialNumbers seq;
|
||||
QString F =autonum::AssignVariables::formulaToLabel(
|
||||
elmt->diagram()->border_and_titleblock.folio(),
|
||||
seq,
|
||||
elmt->diagram(),
|
||||
elmt);
|
||||
qtwi->setText(1, F);
|
||||
}
|
||||
else
|
||||
{
|
||||
qtwi->setText(1, QString::number(
|
||||
elmt->diagram()->folioIndex()
|
||||
+ 1));
|
||||
}
|
||||
|
||||
|
||||
qtwi->setText(2, elmt->diagram()->title());
|
||||
qtwi->setText(4, elmt->diagram()->convertPosition(
|
||||
elmt->scenePos()).toString());
|
||||
items_list.append(qtwi);
|
||||
m_qtwi_hash.insert(qtwi, elmt);
|
||||
}
|
||||
|
||||
ui->m_free_tree_widget->addTopLevelItems(items_list);
|
||||
items_list.clear();
|
||||
|
||||
//Build the list of already linked element
|
||||
const QList<Element *> link_list = m_element->linkedElements();
|
||||
for(Element *elmt : link_list)
|
||||
{
|
||||
QTreeWidgetItem *qtwi = new QTreeWidgetItem(ui->m_link_tree_widget);
|
||||
qtwi->setIcon(0, elmt->pixmap());
|
||||
|
||||
if(settings.value("genericpanel/folio", false).toBool())
|
||||
{
|
||||
autonum::sequentialNumbers seq;
|
||||
QString F =autonum::AssignVariables::formulaToLabel(
|
||||
elmt->diagram()->border_and_titleblock.folio(),
|
||||
seq,
|
||||
elmt->diagram(),
|
||||
elmt);
|
||||
qtwi->setText(1, F);
|
||||
}
|
||||
else
|
||||
{
|
||||
qtwi->setText(1, QString::number(
|
||||
elmt->diagram()->folioIndex()
|
||||
+ 1));
|
||||
}
|
||||
|
||||
qtwi->setText(2, elmt->diagram()->title());
|
||||
qtwi->setText(3, elmt->diagram()->convertPosition(
|
||||
elmt->scenePos()).toString());
|
||||
items_list.append(qtwi);
|
||||
m_qtwi_hash.insert(qtwi, elmt);
|
||||
}
|
||||
|
||||
if(items_list.count())
|
||||
ui->m_link_tree_widget->addTopLevelItems(items_list);
|
||||
|
||||
QVariant v = settings.value("link-element-widget/master-state");
|
||||
if(!v.isNull())
|
||||
{
|
||||
ui->m_free_tree_widget->header()->restoreState(v.toByteArray());
|
||||
ui->m_link_tree_widget->header()->restoreState(v.toByteArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PLC IO Table methods
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::plcPasteFromClipboard
|
||||
* Paste IO data from clipboard.
|
||||
*
|
||||
* Two modes:
|
||||
* 1. Single-column data (no tabs, e.g. copied vertically from A1:A6):
|
||||
* Values are pasted vertically down the current column.
|
||||
* 2. Multi-column data (tab-separated, e.g. copied from a row in Excel):
|
||||
* Each line becomes a separate IO row.
|
||||
*/
|
||||
void MasterPropertiesWidget::plcPasteFromClipboard()
|
||||
{
|
||||
if (!m_plc_table || !m_element)
|
||||
return;
|
||||
|
||||
QString clipboard_text = QApplication::clipboard()->text();
|
||||
if (clipboard_text.isEmpty())
|
||||
return;
|
||||
|
||||
QStringList lines = clipboard_text.split('\n', Qt::SkipEmptyParts);
|
||||
if (lines.isEmpty())
|
||||
return;
|
||||
|
||||
bool has_tabs = false;
|
||||
for (const QString &line : lines) {
|
||||
if (line.contains('\t')) {
|
||||
has_tabs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_plc_updating = true;
|
||||
|
||||
if (!has_tabs) {
|
||||
// Vertical paste: values go down the same column, within existing rows only
|
||||
int target_col = m_plc_table->currentColumn();
|
||||
if (target_col < 0) target_col = 0;
|
||||
int target_row = m_plc_table->currentRow();
|
||||
if (target_row < 0) target_row = 0;
|
||||
int max_rows = m_plc_table->rowCount();
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i) {
|
||||
int row = target_row + i;
|
||||
if (row >= max_rows) break;
|
||||
setCellFromValue(row, target_col, lines.at(i).trimmed());
|
||||
}
|
||||
} else {
|
||||
// Horizontal paste: each line is a separate IO row, within existing rows only
|
||||
int target_row = m_plc_table->currentRow();
|
||||
if (target_row < 0) target_row = 0;
|
||||
int max_rows = m_plc_table->rowCount();
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i) {
|
||||
int row = target_row + i;
|
||||
if (row >= max_rows) break;
|
||||
QStringList cells = lines.at(i).split('\t');
|
||||
for (int c = 0; c < cells.size(); ++c) {
|
||||
if (c > 6) break;
|
||||
setCellFromValue(row, c, cells.at(c).trimmed());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_plc_updating = false;
|
||||
plcUpdateDisplaySettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::setCellFromValue
|
||||
* Set a single table cell value, respecting the column widget type.
|
||||
*/
|
||||
void MasterPropertiesWidget::setCellFromValue(int row, int col, const QString &val)
|
||||
{
|
||||
if (!m_plc_table || row < 0 || col < 0 || col > 6)
|
||||
return;
|
||||
|
||||
if (col == 0) {
|
||||
// Type combo
|
||||
auto *type_cb = qobject_cast<QComboBox*>(m_plc_table->cellWidget(row, col));
|
||||
if (!type_cb) {
|
||||
type_cb = new QComboBox(m_plc_table);
|
||||
QStringList plc_types = ElementData::plcIOTypeList();
|
||||
for (int t = 0; t < plc_types.size(); ++t)
|
||||
type_cb->addItem(plc_types.at(t), t);
|
||||
m_plc_table->setCellWidget(row, col, type_cb);
|
||||
connect(type_cb, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, [this, row](int) { plcIOCellChanged(row, 0); });
|
||||
}
|
||||
if (!val.isEmpty()) {
|
||||
QStringList plc_types = ElementData::plcIOTypeList();
|
||||
for (int t = 0; t < plc_types.size(); ++t) {
|
||||
if (plc_types.at(t).compare(val, Qt::CaseInsensitive) == 0) {
|
||||
type_cb->setCurrentIndex(t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (col == 5) {
|
||||
// Terminal count spinbox
|
||||
auto *tc_sb = qobject_cast<QSpinBox*>(m_plc_table->cellWidget(row, col));
|
||||
if (!tc_sb) {
|
||||
tc_sb = new QSpinBox(m_plc_table);
|
||||
tc_sb->setMinimum(1);
|
||||
tc_sb->setMaximum(4);
|
||||
m_plc_table->setCellWidget(row, col, tc_sb);
|
||||
connect(tc_sb, QOverload<int>::of(&QSpinBox::valueChanged),
|
||||
this, [this, row](int) { plcIOCellChanged(row, 5); });
|
||||
}
|
||||
bool ok;
|
||||
int v = val.toInt(&ok);
|
||||
if (ok && v >= 1 && v <= 4)
|
||||
tc_sb->setValue(v);
|
||||
}
|
||||
else if (col == 6) {
|
||||
// CrossRef - read-only
|
||||
auto *item = new QTableWidgetItem(val);
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
m_plc_table->setItem(row, col, item);
|
||||
}
|
||||
else {
|
||||
// Text columns: Address, Function, Comment
|
||||
m_plc_table->setItem(row, col, new QTableWidgetItem(val));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::plcAddRow
|
||||
* Add a new empty row to the PLC IO table
|
||||
*/
|
||||
void MasterPropertiesWidget::plcAddRow()
|
||||
{
|
||||
if (!m_plc_table)
|
||||
return;
|
||||
|
||||
int row = m_plc_table->rowCount();
|
||||
m_plc_table->insertRow(row);
|
||||
|
||||
// Type combo
|
||||
auto *type_cb = new QComboBox(m_plc_table);
|
||||
QStringList plc_types = ElementData::plcIOTypeList();
|
||||
for (int t = 0; t < plc_types.size(); ++t) {
|
||||
type_cb->addItem(plc_types.at(t), t);
|
||||
}
|
||||
m_plc_table->setCellWidget(row, 0, type_cb);
|
||||
connect(type_cb, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, [this, row](int) { plcIOCellChanged(row, 0); });
|
||||
|
||||
m_plc_table->setItem(row, 1, new QTableWidgetItem());
|
||||
m_plc_table->setItem(row, 2, new QTableWidgetItem());
|
||||
m_plc_table->setItem(row, 3, new QTableWidgetItem());
|
||||
m_plc_table->setItem(row, 4, new QTableWidgetItem());
|
||||
|
||||
auto *tc_sb = new QSpinBox(m_plc_table);
|
||||
tc_sb->setMinimum(1);
|
||||
tc_sb->setMaximum(4);
|
||||
tc_sb->setValue(1);
|
||||
m_plc_table->setCellWidget(row, 5, tc_sb);
|
||||
connect(tc_sb, QOverload<int>::of(&QSpinBox::valueChanged),
|
||||
this, [this, row](int) { plcIOCellChanged(row, 5); });
|
||||
|
||||
auto *crossref_item = new QTableWidgetItem();
|
||||
crossref_item->setFlags(crossref_item->flags() & ~Qt::ItemIsEditable);
|
||||
m_plc_table->setItem(row, 6, crossref_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::plcRemoveRow
|
||||
* Remove selected rows from the PLC IO table
|
||||
*/
|
||||
void MasterPropertiesWidget::plcRemoveRow()
|
||||
{
|
||||
if (!m_plc_table)
|
||||
return;
|
||||
|
||||
QModelIndexList selected = m_plc_table->selectionModel()->selectedRows();
|
||||
if (selected.isEmpty())
|
||||
return;
|
||||
|
||||
// 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(); });
|
||||
|
||||
for (const QModelIndex &idx : selected) {
|
||||
m_plc_table->removeRow(idx.row());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::plcMoveRowUp
|
||||
* Move selected row up
|
||||
*/
|
||||
void MasterPropertiesWidget::plcMoveRowUp()
|
||||
{
|
||||
if (!m_plc_table)
|
||||
return;
|
||||
|
||||
int row = m_plc_table->currentRow();
|
||||
if (row <= 0)
|
||||
return;
|
||||
|
||||
// Swap with row above
|
||||
for (int col = 0; col < m_plc_table->columnCount(); ++col) {
|
||||
QWidget *w1 = m_plc_table->cellWidget(row, col);
|
||||
QWidget *w2 = m_plc_table->cellWidget(row - 1, col);
|
||||
m_plc_table->setCellWidget(row, col, w2);
|
||||
m_plc_table->setCellWidget(row - 1, col, w1);
|
||||
|
||||
QTableWidgetItem *i1 = m_plc_table->item(row, col);
|
||||
QTableWidgetItem *i2 = m_plc_table->item(row - 1, col);
|
||||
m_plc_table->setItem(row, col, i2);
|
||||
m_plc_table->setItem(row - 1, col, i1);
|
||||
}
|
||||
|
||||
m_plc_table->setCurrentCell(row - 1, m_plc_table->currentColumn());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::plcMoveRowDown
|
||||
* Move selected row down
|
||||
*/
|
||||
void MasterPropertiesWidget::plcMoveRowDown()
|
||||
{
|
||||
if (!m_plc_table)
|
||||
return;
|
||||
|
||||
int row = m_plc_table->currentRow();
|
||||
if (row < 0 || row >= m_plc_table->rowCount() - 1)
|
||||
return;
|
||||
|
||||
// Swap with row below
|
||||
for (int col = 0; col < m_plc_table->columnCount(); ++col) {
|
||||
QWidget *w1 = m_plc_table->cellWidget(row, col);
|
||||
QWidget *w2 = m_plc_table->cellWidget(row + 1, col);
|
||||
m_plc_table->setCellWidget(row, col, w2);
|
||||
m_plc_table->setCellWidget(row + 1, col, w1);
|
||||
|
||||
QTableWidgetItem *i1 = m_plc_table->item(row, col);
|
||||
QTableWidgetItem *i2 = m_plc_table->item(row + 1, col);
|
||||
m_plc_table->setItem(row, col, i2);
|
||||
m_plc_table->setItem(row + 1, col, i1);
|
||||
}
|
||||
|
||||
m_plc_table->setCurrentCell(row + 1, m_plc_table->currentColumn());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::plcIOCellChanged
|
||||
* Called when a cell in the PLC IO table changes
|
||||
*/
|
||||
void MasterPropertiesWidget::plcIOCellChanged(int row, int column)
|
||||
{
|
||||
Q_UNUSED(row)
|
||||
Q_UNUSED(column)
|
||||
if (m_plc_updating || !m_element || !m_plc_table)
|
||||
return;
|
||||
|
||||
// Save immediately
|
||||
plcUpdateDisplaySettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::plcUpdateDisplaySettings
|
||||
* Update the IO data from the table (display settings are managed by the editor only)
|
||||
*/
|
||||
void MasterPropertiesWidget::plcUpdateDisplaySettings()
|
||||
{
|
||||
if (m_plc_updating || !m_element || !m_plc_table)
|
||||
return;
|
||||
|
||||
m_plc_updating = true;
|
||||
|
||||
// Preserve existing display settings
|
||||
ElementData ed = m_element->elementData();
|
||||
ElementData::PlcMasterData plc_data = ed.plcMasterData();
|
||||
plc_data.ios.clear();
|
||||
|
||||
// Read IOs from table
|
||||
for (int row = 0; row < m_plc_table->rowCount(); ++row) {
|
||||
ElementData::PlcIO io;
|
||||
|
||||
auto *type_cb = qobject_cast<QComboBox *>(m_plc_table->cellWidget(row, 0));
|
||||
if (type_cb)
|
||||
io.type = static_cast<ElementData::PlcIOType>(type_cb->currentData().toInt());
|
||||
|
||||
auto *addr_item = m_plc_table->item(row, 1);
|
||||
if (addr_item)
|
||||
io.address = addr_item->text();
|
||||
|
||||
auto *func_item = m_plc_table->item(row, 2);
|
||||
if (func_item)
|
||||
io.functionText = func_item->text();
|
||||
|
||||
auto *comment_item = m_plc_table->item(row, 3);
|
||||
if (comment_item)
|
||||
io.comment = comment_item->text();
|
||||
|
||||
auto *crossref_item = m_plc_table->item(row, 4);
|
||||
if (crossref_item)
|
||||
io.crossRef = crossref_item->text();
|
||||
|
||||
plc_data.ios.append(io);
|
||||
}
|
||||
|
||||
ed.setPlcMasterData(plc_data);
|
||||
m_element->setElementData(ed);
|
||||
|
||||
// Trigger update of the cross ref item
|
||||
if (m_element->scene())
|
||||
m_element->update();
|
||||
|
||||
m_plc_updating = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MasterPropertiesWidget::plcShowTableContextMenu
|
||||
* Show context menu for the PLC IO table
|
||||
*/
|
||||
void MasterPropertiesWidget::plcShowTableContextMenu(const QPoint &pos)
|
||||
{
|
||||
Q_UNUSED(pos)
|
||||
if (!m_plc_table)
|
||||
return;
|
||||
|
||||
QMenu menu;
|
||||
menu.addAction(tr("Coller depuis le presse-papiers"), this, &MasterPropertiesWidget::plcPasteFromClipboard);
|
||||
|
||||
menu.exec(m_plc_table->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user