mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-13 10:04:13 +02:00
Merge pull request #669 from Kellermorph/fix-plc-editor
PLC Fix scroll sync, data persistence, font defaults, copy/paste, and layout fixes
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
#include "partplctable.h"
|
#include "partplctable.h"
|
||||||
|
|
||||||
#include "../../QPropertyUndoCommand/qpropertyundocommand.h"
|
#include "../../QPropertyUndoCommand/qpropertyundocommand.h"
|
||||||
|
#include "../../qetapp.h"
|
||||||
#include "../../QetGraphicsItemModeler/qetgraphicshandleritem.h"
|
#include "../../QetGraphicsItemModeler/qetgraphicshandleritem.h"
|
||||||
#include "../../QetGraphicsItemModeler/qetgraphicshandlerutility.h"
|
#include "../../QetGraphicsItemModeler/qetgraphicshandlerutility.h"
|
||||||
#include "../../properties/elementdata.h"
|
#include "../../properties/elementdata.h"
|
||||||
@@ -302,7 +303,7 @@ void PartPlcTable::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
|
|||||||
|
|
||||||
// Draw column headers
|
// Draw column headers
|
||||||
QFont header_font = plc_data.headerFont.family().isEmpty()
|
QFont header_font = plc_data.headerFont.family().isEmpty()
|
||||||
? painter->font() : plc_data.headerFont;
|
? QETApp::diagramTextsFont() : plc_data.headerFont;
|
||||||
header_font.setBold(true);
|
header_font.setBold(true);
|
||||||
painter->setFont(header_font);
|
painter->setFont(header_font);
|
||||||
|
|
||||||
@@ -318,7 +319,7 @@ void PartPlcTable::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
|
|||||||
|
|
||||||
// Draw IO rows
|
// Draw IO rows
|
||||||
QFont cell_font = plc_data.cellFont.family().isEmpty()
|
QFont cell_font = plc_data.cellFont.family().isEmpty()
|
||||||
? painter->font() : plc_data.cellFont;
|
? QETApp::diagramTextsFont() : plc_data.cellFont;
|
||||||
painter->setFont(cell_font);
|
painter->setFont(cell_font);
|
||||||
int start_idx = block_starts.at(block);
|
int start_idx = block_starts.at(block);
|
||||||
int end_idx = (block + 1 < block_starts.size())
|
int end_idx = (block + 1 < block_starts.size())
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
#include <QSignalBlocker>
|
#include <QSignalBlocker>
|
||||||
#include <QTableWidgetItem>
|
#include <QTableWidgetItem>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QWheelEvent>
|
||||||
#include <QTableWidget>
|
#include <QTableWidget>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
@@ -41,6 +43,8 @@
|
|||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
|
#include <QShortcut>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief The EditorDelegate class
|
@brief The EditorDelegate class
|
||||||
@@ -666,10 +670,15 @@ void ElementPropertiesEditorWidget::createPlcConfigWidgets()
|
|||||||
plc_layout->addLayout(toolbar);
|
plc_layout->addLayout(toolbar);
|
||||||
|
|
||||||
// Tables side by side: IO table (left) + Terminal table (right)
|
// Tables side by side: IO table (left) + Terminal table (right)
|
||||||
auto *tables_splitter = new QSplitter(Qt::Horizontal, m_plc_gb);
|
// Both share a single vertical scrollbar on the right
|
||||||
|
auto *tables_container = new QWidget(m_plc_gb);
|
||||||
|
auto *tables_layout = new QHBoxLayout(tables_container);
|
||||||
|
tables_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
auto *splitter = new QSplitter(Qt::Horizontal, tables_container);
|
||||||
|
|
||||||
// IO Table
|
// IO Table
|
||||||
m_plc_table = new QTableWidget(tables_splitter);
|
m_plc_table = new QTableWidget(splitter);
|
||||||
m_plc_table->setColumnCount(5);
|
m_plc_table->setColumnCount(5);
|
||||||
m_plc_table->setHorizontalHeaderLabels({
|
m_plc_table->setHorizontalHeaderLabels({
|
||||||
tr("Type"), tr("Adresse"), tr("Fonction"),
|
tr("Type"), tr("Adresse"), tr("Fonction"),
|
||||||
@@ -686,10 +695,10 @@ void ElementPropertiesEditorWidget::createPlcConfigWidgets()
|
|||||||
m_plc_table->setSelectionBehavior(QAbstractItemView::SelectItems);
|
m_plc_table->setSelectionBehavior(QAbstractItemView::SelectItems);
|
||||||
m_plc_table->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
m_plc_table->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
m_plc_table->setMinimumHeight(200);
|
m_plc_table->setMinimumHeight(200);
|
||||||
tables_splitter->addWidget(m_plc_table);
|
m_plc_table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
|
||||||
// Terminal table (per IO: Nb + T1-T4)
|
// Terminal table (per IO: Nb + T1-T4)
|
||||||
m_plc_terminal_table = new QTableWidget(tables_splitter);
|
m_plc_terminal_table = new QTableWidget(splitter);
|
||||||
m_plc_terminal_table->setColumnCount(2);
|
m_plc_terminal_table->setColumnCount(2);
|
||||||
m_plc_terminal_table->setHorizontalHeaderLabels({
|
m_plc_terminal_table->setHorizontalHeaderLabels({
|
||||||
tr("Nb."), tr("T1")
|
tr("Nb."), tr("T1")
|
||||||
@@ -700,13 +709,61 @@ void ElementPropertiesEditorWidget::createPlcConfigWidgets()
|
|||||||
m_plc_terminal_table->setSelectionBehavior(QAbstractItemView::SelectItems);
|
m_plc_terminal_table->setSelectionBehavior(QAbstractItemView::SelectItems);
|
||||||
m_plc_terminal_table->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
m_plc_terminal_table->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
m_plc_terminal_table->setMinimumHeight(200);
|
m_plc_terminal_table->setMinimumHeight(200);
|
||||||
tables_splitter->addWidget(m_plc_terminal_table);
|
m_plc_terminal_table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
|
||||||
tables_splitter->setStretchFactor(0, 3);
|
// Shared vertical scrollbar
|
||||||
tables_splitter->setStretchFactor(1, 1);
|
m_plc_shared_scrollbar = new QScrollBar(Qt::Vertical, tables_container);
|
||||||
tables_splitter->setSizes({500, 150});
|
m_plc_shared_scrollbar->setMinimum(0);
|
||||||
|
|
||||||
plc_layout->addWidget(tables_splitter);
|
splitter->addWidget(m_plc_table);
|
||||||
|
splitter->addWidget(m_plc_terminal_table);
|
||||||
|
splitter->setStretchFactor(0, 3);
|
||||||
|
splitter->setStretchFactor(1, 1);
|
||||||
|
|
||||||
|
tables_layout->addWidget(splitter);
|
||||||
|
tables_layout->addWidget(m_plc_shared_scrollbar);
|
||||||
|
|
||||||
|
// Bidirectional sync between tables and shared scrollbar
|
||||||
|
// Use flag to prevent infinite loops
|
||||||
|
connect(m_plc_shared_scrollbar, &QScrollBar::valueChanged,
|
||||||
|
this, [this](int value) {
|
||||||
|
if (m_plc_scroll_sync) return;
|
||||||
|
m_plc_scroll_sync = true;
|
||||||
|
m_plc_table->verticalScrollBar()->setValue(value);
|
||||||
|
m_plc_terminal_table->verticalScrollBar()->setValue(value);
|
||||||
|
m_plc_scroll_sync = false;
|
||||||
|
});
|
||||||
|
connect(m_plc_table->verticalScrollBar(), &QScrollBar::valueChanged,
|
||||||
|
this, [this](int value) {
|
||||||
|
if (m_plc_scroll_sync) return;
|
||||||
|
m_plc_scroll_sync = true;
|
||||||
|
m_plc_shared_scrollbar->setValue(value);
|
||||||
|
m_plc_terminal_table->verticalScrollBar()->setValue(value);
|
||||||
|
m_plc_scroll_sync = false;
|
||||||
|
});
|
||||||
|
connect(m_plc_terminal_table->verticalScrollBar(), &QScrollBar::valueChanged,
|
||||||
|
this, [this](int value) {
|
||||||
|
if (m_plc_scroll_sync) return;
|
||||||
|
m_plc_scroll_sync = true;
|
||||||
|
m_plc_shared_scrollbar->setValue(value);
|
||||||
|
m_plc_table->verticalScrollBar()->setValue(value);
|
||||||
|
m_plc_scroll_sync = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update shared scrollbar range from both tables
|
||||||
|
auto syncRange = [this]() {
|
||||||
|
int max = qMax(m_plc_table->verticalScrollBar()->maximum(),
|
||||||
|
m_plc_terminal_table->verticalScrollBar()->maximum());
|
||||||
|
m_plc_shared_scrollbar->blockSignals(true);
|
||||||
|
m_plc_shared_scrollbar->setMaximum(max);
|
||||||
|
m_plc_shared_scrollbar->blockSignals(false);
|
||||||
|
};
|
||||||
|
connect(m_plc_table->verticalScrollBar(), &QScrollBar::rangeChanged,
|
||||||
|
this, syncRange);
|
||||||
|
connect(m_plc_terminal_table->verticalScrollBar(), &QScrollBar::rangeChanged,
|
||||||
|
this, syncRange);
|
||||||
|
|
||||||
|
plc_layout->addWidget(tables_container);
|
||||||
|
|
||||||
// Font settings
|
// Font settings
|
||||||
auto *font_layout = new QHBoxLayout();
|
auto *font_layout = new QHBoxLayout();
|
||||||
@@ -790,12 +847,25 @@ void ElementPropertiesEditorWidget::createPlcConfigWidgets()
|
|||||||
}
|
}
|
||||||
plc_layout->addLayout(col_layout);
|
plc_layout->addLayout(col_layout);
|
||||||
|
|
||||||
// Add to master group box
|
// Add to master group box - row 4, full width
|
||||||
ui->m_master_gb->layout()->addWidget(m_plc_gb);
|
auto *gl = qobject_cast<QGridLayout*>(ui->m_master_gb->layout());
|
||||||
|
gl->addWidget(m_plc_gb, 4, 0, 1, 2);
|
||||||
|
|
||||||
// Connect signals
|
// Connect signals
|
||||||
connect(add_btn, &QPushButton::clicked, this, &ElementPropertiesEditorWidget::plcAddRow);
|
connect(add_btn, &QPushButton::clicked, this, &ElementPropertiesEditorWidget::plcAddRow);
|
||||||
connect(remove_btn, &QPushButton::clicked, this, &ElementPropertiesEditorWidget::plcRemoveRow);
|
connect(remove_btn, &QPushButton::clicked, this, &ElementPropertiesEditorWidget::plcRemoveRow);
|
||||||
|
|
||||||
|
// Ctrl+V shortcut for paste
|
||||||
|
auto *paste_shortcut = new QShortcut(QKeySequence::Paste, m_plc_table);
|
||||||
|
connect(paste_shortcut, &QShortcut::activated, this, &ElementPropertiesEditorWidget::plcPasteFromClipboard);
|
||||||
|
|
||||||
|
// Context menu for the PLC table
|
||||||
|
m_plc_table->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect(m_plc_table, &QTableWidget::customContextMenuRequested, this, [this](const QPoint &pos) {
|
||||||
|
QMenu menu;
|
||||||
|
menu.addAction(tr("Coller depuis le presse-papiers"), this, &ElementPropertiesEditorWidget::plcPasteFromClipboard);
|
||||||
|
menu.exec(m_plc_table->mapToGlobal(pos));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -877,12 +947,12 @@ void ElementPropertiesEditorWidget::populatePlcTable()
|
|||||||
m_plc_header_font = plc_data.headerFont;
|
m_plc_header_font = plc_data.headerFont;
|
||||||
m_plc_cell_font = plc_data.cellFont;
|
m_plc_cell_font = plc_data.cellFont;
|
||||||
if (m_plc_header_font.family().isEmpty()) {
|
if (m_plc_header_font.family().isEmpty()) {
|
||||||
m_plc_header_font = QFont(m_plc_table->font());
|
m_plc_header_font = QETApp::diagramTextsFont();
|
||||||
m_plc_header_font.setBold(true);
|
m_plc_header_font.setBold(true);
|
||||||
m_plc_header_font.setPointSize(8);
|
m_plc_header_font.setPointSize(8);
|
||||||
}
|
}
|
||||||
if (m_plc_cell_font.family().isEmpty()) {
|
if (m_plc_cell_font.family().isEmpty()) {
|
||||||
m_plc_cell_font = QFont(m_plc_table->font());
|
m_plc_cell_font = QETApp::diagramTextsFont();
|
||||||
m_plc_cell_font.setPointSize(8);
|
m_plc_cell_font.setPointSize(8);
|
||||||
}
|
}
|
||||||
m_plc_header_font_btn->setText(tr("Police des en-têtes: %1 %2pt")
|
m_plc_header_font_btn->setText(tr("Police des en-têtes: %1 %2pt")
|
||||||
@@ -920,6 +990,21 @@ void ElementPropertiesEditorWidget::populatePlcTable()
|
|||||||
hdr->moveSection(hdr->visualIndex(logical), visual);
|
hdr->moveSection(hdr->visualIndex(logical), visual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure scrollbars stay hidden and sync shared scrollbar range
|
||||||
|
if (m_plc_table) {
|
||||||
|
m_plc_table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
m_plc_table->verticalScrollBar()->setVisible(false);
|
||||||
|
}
|
||||||
|
if (m_plc_terminal_table) {
|
||||||
|
m_plc_terminal_table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
m_plc_terminal_table->verticalScrollBar()->setVisible(false);
|
||||||
|
}
|
||||||
|
if (m_plc_shared_scrollbar && m_plc_table) {
|
||||||
|
int max = qMax(m_plc_table->verticalScrollBar()->maximum(),
|
||||||
|
m_plc_terminal_table->verticalScrollBar()->maximum());
|
||||||
|
m_plc_shared_scrollbar->setMaximum(max);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1158,53 +1243,80 @@ void ElementPropertiesEditorWidget::plcPasteFromClipboard()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
QStringList lines = clipboard_text.split('\n', Qt::SkipEmptyParts);
|
QStringList lines = clipboard_text.split('\n', Qt::SkipEmptyParts);
|
||||||
|
if (lines.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
int start_row = m_plc_table->rowCount();
|
bool has_tabs = false;
|
||||||
m_plc_table->setRowCount(start_row + lines.size());
|
for (const QString &line : lines) {
|
||||||
|
if (line.contains('\t')) {
|
||||||
for (int i = 0; i < lines.size(); ++i) {
|
has_tabs = true;
|
||||||
QStringList cells = lines.at(i).split('\t');
|
break;
|
||||||
int row = start_row + i;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to match type from clipboard
|
if (!has_tabs) {
|
||||||
if (!cells.isEmpty()) {
|
// Vertical paste: values go down the same column
|
||||||
QString type_str = cells.at(0).trimmed();
|
int target_col = m_plc_table->currentColumn();
|
||||||
int type_idx = -1;
|
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;
|
||||||
|
plcSetCellFromValue(row, target_col, lines.at(i).trimmed());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Horizontal paste: each line is a separate IO row
|
||||||
|
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 > 4) break;
|
||||||
|
plcSetCellFromValue(row, c, cells.at(c).trimmed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ElementPropertiesEditorWidget::plcSetCellFromValue
|
||||||
|
* Set a single table cell value, respecting the column widget type.
|
||||||
|
*/
|
||||||
|
void ElementPropertiesEditorWidget::plcSetCellFromValue(int row, int col, const QString &val)
|
||||||
|
{
|
||||||
|
if (!m_plc_table || row < 0 || col < 0 || col > 4)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (col == 0) {
|
||||||
|
auto *type_cb = qobject_cast<QComboBox*>(m_plc_table->cellWidget(row, col));
|
||||||
|
if (!type_cb)
|
||||||
|
return;
|
||||||
|
if (!val.isEmpty()) {
|
||||||
|
QStringList plc_types = ElementData::plcIOTypeList();
|
||||||
for (int t = 0; t < plc_types.size(); ++t) {
|
for (int t = 0; t < plc_types.size(); ++t) {
|
||||||
if (plc_types.at(t).compare(type_str, Qt::CaseInsensitive) == 0) {
|
if (plc_types.at(t).compare(val, Qt::CaseInsensitive) == 0) {
|
||||||
type_idx = t;
|
type_cb->setCurrentIndex(t);
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (type_idx >= 0)
|
|
||||||
type_cb->setCurrentIndex(type_idx);
|
|
||||||
}
|
}
|
||||||
m_plc_table->setCellWidget(row, 0, type_cb);
|
}
|
||||||
|
else if (col == 4) {
|
||||||
// Address
|
// CrossRef - read-only
|
||||||
m_plc_table->setItem(row, 1, new QTableWidgetItem(
|
auto *item = new QTableWidgetItem(val);
|
||||||
cells.size() > 1 ? cells.at(1).trimmed() : QString()));
|
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||||
|
m_plc_table->setItem(row, col, item);
|
||||||
// Function text
|
}
|
||||||
m_plc_table->setItem(row, 2, new QTableWidgetItem(
|
else {
|
||||||
cells.size() > 2 ? cells.at(2).trimmed() : QString()));
|
// Text columns: Address, Function, Comment
|
||||||
|
m_plc_table->setItem(row, col, new QTableWidgetItem(val));
|
||||||
// Comment
|
|
||||||
m_plc_table->setItem(row, 3, new QTableWidgetItem(
|
|
||||||
cells.size() > 3 ? cells.at(3).trimmed() : QString()));
|
|
||||||
|
|
||||||
// CrossRef (read-only)
|
|
||||||
auto *crossref_item = new QTableWidgetItem(
|
|
||||||
cells.size() > 4 ? cells.at(4).trimmed() : QString());
|
|
||||||
crossref_item->setFlags(crossref_item->flags() & ~Qt::ItemIsEditable);
|
|
||||||
m_plc_table->setItem(row, 4, crossref_item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class QCheckBox;
|
|||||||
class QGroupBox;
|
class QGroupBox;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
class QLineEdit;
|
class QLineEdit;
|
||||||
|
class QScrollBar;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ElementPropertiesEditorWidget;
|
class ElementPropertiesEditorWidget;
|
||||||
@@ -74,6 +75,7 @@ class ElementPropertiesEditorWidget : public QDialog
|
|||||||
void plcTerminalCountChanged(int row, int count);
|
void plcTerminalCountChanged(int row, int count);
|
||||||
void plcSelectHeaderFont();
|
void plcSelectHeaderFont();
|
||||||
void plcSelectCellFont();
|
void plcSelectCellFont();
|
||||||
|
void plcSetCellFromValue(int row, int col, const QString &val);
|
||||||
|
|
||||||
//ATTRIBUTES
|
//ATTRIBUTES
|
||||||
private:
|
private:
|
||||||
@@ -92,9 +94,11 @@ class ElementPropertiesEditorWidget : public QDialog
|
|||||||
QCheckBox *m_plc_show_headers_cb = nullptr;
|
QCheckBox *m_plc_show_headers_cb = nullptr;
|
||||||
QFont m_plc_header_font;
|
QFont m_plc_header_font;
|
||||||
QFont m_plc_cell_font;
|
QFont m_plc_cell_font;
|
||||||
QList<QCheckBox *> m_plc_col_visibility_checkboxes;
|
QList<QCheckBox *> m_plc_col_visibility_checkboxes;
|
||||||
QList<QSpinBox *> m_plc_col_width_spinboxes;
|
QList<QSpinBox *> m_plc_col_width_spinboxes;
|
||||||
QList<QLineEdit *> m_plc_col_name_edits;
|
QList<QLineEdit *> m_plc_col_name_edits;
|
||||||
|
QScrollBar *m_plc_shared_scrollbar = nullptr;
|
||||||
|
bool m_plc_scroll_sync = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ELEMENTPROPERTIESEDITORWIDGET_H
|
#endif // ELEMENTPROPERTIESEDITORWIDGET_H
|
||||||
|
|||||||
@@ -39,7 +39,20 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QComboBox" name="m_base_type_cb"/>
|
<widget class="QComboBox" name="m_base_type_cb"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="m_slave_gb">
|
<widget class="QGroupBox" name="m_slave_gb">
|
||||||
@@ -93,17 +106,34 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Élément maître</string>
|
<string>Élément maître</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_5">
|
<layout class="QHBoxLayout" name="type_concret_layout">
|
||||||
<property name="text">
|
<item>
|
||||||
<string>Type concret</string>
|
<widget class="QLabel" name="label_5">
|
||||||
</property>
|
<property name="text">
|
||||||
</widget>
|
<string>Type concret</string>
|
||||||
</item>
|
</property>
|
||||||
<item row="0" column="1">
|
</widget>
|
||||||
<widget class="QComboBox" name="m_master_type_cb"/>
|
</item>
|
||||||
</item>
|
<item>
|
||||||
|
<spacer name="type_concret_spacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="m_master_type_cb"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="max_slaves_checkbox">
|
<widget class="QCheckBox" name="max_slaves_checkbox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -202,20 +232,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
</layout>
|
||||||
<spacer name="verticalSpacer_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="Informations">
|
<widget class="QWidget" name="Informations">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
|||||||
@@ -267,6 +267,211 @@ QDomElement ElementData::kindInfoToXml(QDomDocument &document)
|
|||||||
return returned_elmt;
|
return returned_elmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ElementData::plcMasterDataToXml
|
||||||
|
* Serialize PLC master data to XML (used by Element::toXml for diagram instances)
|
||||||
|
*/
|
||||||
|
QDomElement ElementData::plcMasterDataToXml(QDomDocument &document) const
|
||||||
|
{
|
||||||
|
auto xml_plc = document.createElement(QStringLiteral("plcMasterData"));
|
||||||
|
xml_plc.setAttribute(QStringLiteral("rowHeight"),
|
||||||
|
QString::number(m_plc_master_data.rowHeight, 'f', 2));
|
||||||
|
|
||||||
|
// Save break positions
|
||||||
|
{
|
||||||
|
auto xml_breaks = document.createElement(QStringLiteral("breakPositions"));
|
||||||
|
for (int bp : m_plc_master_data.breakPositions) {
|
||||||
|
auto xml_bp = document.createElement(QStringLiteral("break"));
|
||||||
|
xml_bp.appendChild(document.createTextNode(QString::number(bp)));
|
||||||
|
xml_breaks.appendChild(xml_bp);
|
||||||
|
}
|
||||||
|
xml_plc.appendChild(xml_breaks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save column widths
|
||||||
|
auto xml_col_widths = document.createElement(QStringLiteral("columnWidths"));
|
||||||
|
for (auto it = m_plc_master_data.colWidths.constBegin();
|
||||||
|
it != m_plc_master_data.colWidths.constEnd(); ++it) {
|
||||||
|
auto xml_col = document.createElement(QStringLiteral("column"));
|
||||||
|
xml_col.setAttribute(QStringLiteral("index"), it.key());
|
||||||
|
xml_col.setAttribute(QStringLiteral("width"), QString::number(it.value(), 'f', 2));
|
||||||
|
xml_col_widths.appendChild(xml_col);
|
||||||
|
}
|
||||||
|
xml_plc.appendChild(xml_col_widths);
|
||||||
|
|
||||||
|
// Save column visibility
|
||||||
|
auto xml_col_vis = document.createElement(QStringLiteral("columnVisibility"));
|
||||||
|
for (auto it = m_plc_master_data.colVisible.constBegin();
|
||||||
|
it != m_plc_master_data.colVisible.constEnd(); ++it) {
|
||||||
|
auto xml_col = document.createElement(QStringLiteral("column"));
|
||||||
|
xml_col.setAttribute(QStringLiteral("index"), it.key());
|
||||||
|
xml_col.setAttribute(QStringLiteral("visible"), it.value() ? "true" : "false");
|
||||||
|
xml_col_vis.appendChild(xml_col);
|
||||||
|
}
|
||||||
|
xml_plc.appendChild(xml_col_vis);
|
||||||
|
|
||||||
|
// Save fonts
|
||||||
|
if (!m_plc_master_data.headerFont.family().isEmpty()) {
|
||||||
|
auto xml_hfont = document.createElement(QStringLiteral("headerFont"));
|
||||||
|
xml_hfont.setAttribute(QStringLiteral("family"), m_plc_master_data.headerFont.family());
|
||||||
|
xml_hfont.setAttribute(QStringLiteral("size"), m_plc_master_data.headerFont.pointSize());
|
||||||
|
xml_hfont.setAttribute(QStringLiteral("bold"), m_plc_master_data.headerFont.bold() ? "true" : "false");
|
||||||
|
xml_plc.appendChild(xml_hfont);
|
||||||
|
}
|
||||||
|
if (!m_plc_master_data.cellFont.family().isEmpty()) {
|
||||||
|
auto xml_cfont = document.createElement(QStringLiteral("cellFont"));
|
||||||
|
xml_cfont.setAttribute(QStringLiteral("family"), m_plc_master_data.cellFont.family());
|
||||||
|
xml_cfont.setAttribute(QStringLiteral("size"), m_plc_master_data.cellFont.pointSize());
|
||||||
|
xml_cfont.setAttribute(QStringLiteral("bold"), m_plc_master_data.cellFont.bold() ? "true" : "false");
|
||||||
|
xml_plc.appendChild(xml_cfont);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save custom column names
|
||||||
|
if (!m_plc_master_data.columnNames.isEmpty()) {
|
||||||
|
auto xml_names = document.createElement(QStringLiteral("columnNames"));
|
||||||
|
for (int i = 0; i < m_plc_master_data.columnNames.size(); ++i) {
|
||||||
|
auto xml_name = document.createElement(QStringLiteral("column"));
|
||||||
|
xml_name.setAttribute(QStringLiteral("index"), i);
|
||||||
|
xml_name.appendChild(document.createTextNode(m_plc_master_data.columnNames.at(i)));
|
||||||
|
xml_names.appendChild(xml_name);
|
||||||
|
}
|
||||||
|
xml_plc.appendChild(xml_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save column order
|
||||||
|
if (!m_plc_master_data.columnOrder.isEmpty()) {
|
||||||
|
auto xml_order = document.createElement(QStringLiteral("columnOrder"));
|
||||||
|
QString order_str;
|
||||||
|
for (int i = 0; i < m_plc_master_data.columnOrder.size(); ++i) {
|
||||||
|
if (i > 0) order_str += QStringLiteral(",");
|
||||||
|
order_str += QString::number(m_plc_master_data.columnOrder.at(i));
|
||||||
|
}
|
||||||
|
xml_order.appendChild(document.createTextNode(order_str));
|
||||||
|
xml_plc.appendChild(xml_order);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save showHeaders
|
||||||
|
{
|
||||||
|
auto xml_sh = document.createElement(QStringLiteral("showHeaders"));
|
||||||
|
xml_sh.appendChild(document.createTextNode(
|
||||||
|
m_plc_master_data.showHeaders ? QStringLiteral("1") : QStringLiteral("0")));
|
||||||
|
xml_plc.appendChild(xml_sh);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save IO entries
|
||||||
|
auto xml_ios = document.createElement(QStringLiteral("plcIOs"));
|
||||||
|
for (const auto &io : m_plc_master_data.ios) {
|
||||||
|
auto xml_io = document.createElement(QStringLiteral("plcIO"));
|
||||||
|
xml_io.setAttribute(QStringLiteral("type"), plcIOTypeToString(io.type));
|
||||||
|
xml_io.setAttribute(QStringLiteral("address"), io.address);
|
||||||
|
xml_io.setAttribute(QStringLiteral("functionText"), io.functionText);
|
||||||
|
xml_io.setAttribute(QStringLiteral("comment"), io.comment);
|
||||||
|
xml_io.setAttribute(QStringLiteral("crossRef"), io.crossRef);
|
||||||
|
xml_io.setAttribute(QStringLiteral("terminalCount"), io.terminalCount);
|
||||||
|
for (const auto &t : io.terminals) {
|
||||||
|
auto xml_term = document.createElement(QStringLiteral("terminal"));
|
||||||
|
xml_term.appendChild(document.createTextNode(t));
|
||||||
|
xml_io.appendChild(xml_term);
|
||||||
|
}
|
||||||
|
xml_ios.appendChild(xml_io);
|
||||||
|
}
|
||||||
|
xml_plc.appendChild(xml_ios);
|
||||||
|
|
||||||
|
return xml_plc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ElementData::plcMasterDataFromXml
|
||||||
|
* Deserialize PLC master data from XML
|
||||||
|
*/
|
||||||
|
void ElementData::plcMasterDataFromXml(const QDomElement &xml_plc)
|
||||||
|
{
|
||||||
|
if (xml_plc.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Reset PLC data before loading to avoid appending to existing data
|
||||||
|
m_plc_master_data = PlcMasterData();
|
||||||
|
|
||||||
|
m_plc_master_data.rowHeight = xml_plc.attribute(
|
||||||
|
QStringLiteral("rowHeight"), QStringLiteral("8.0")).toDouble();
|
||||||
|
|
||||||
|
// Load break positions
|
||||||
|
auto xml_breaks = xml_plc.firstChildElement(QStringLiteral("breakPositions"));
|
||||||
|
for (const auto &xml_bp : QETXML::findInDomElement(xml_breaks, QStringLiteral("break"))) {
|
||||||
|
m_plc_master_data.breakPositions.append(xml_bp.text().toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load column widths
|
||||||
|
auto xml_col_widths = xml_plc.firstChildElement(QStringLiteral("columnWidths"));
|
||||||
|
for (const auto &xml_col : QETXML::findInDomElement(xml_col_widths, QStringLiteral("column"))) {
|
||||||
|
int idx = xml_col.attribute(QStringLiteral("index")).toInt();
|
||||||
|
qreal w = xml_col.attribute(QStringLiteral("width")).toDouble();
|
||||||
|
m_plc_master_data.colWidths.insert(idx, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load column visibility
|
||||||
|
auto xml_col_vis = xml_plc.firstChildElement(QStringLiteral("columnVisibility"));
|
||||||
|
for (const auto &xml_col : QETXML::findInDomElement(xml_col_vis, QStringLiteral("column"))) {
|
||||||
|
int idx = xml_col.attribute(QStringLiteral("index")).toInt();
|
||||||
|
bool vis = xml_col.attribute(QStringLiteral("visible")) == QLatin1String("true");
|
||||||
|
m_plc_master_data.colVisible.insert(idx, vis);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load fonts
|
||||||
|
auto xml_hfont = xml_plc.firstChildElement(QStringLiteral("headerFont"));
|
||||||
|
if (!xml_hfont.isNull()) {
|
||||||
|
m_plc_master_data.headerFont.setFamily(xml_hfont.attribute(QStringLiteral("family")));
|
||||||
|
m_plc_master_data.headerFont.setPointSize(xml_hfont.attribute(QStringLiteral("size")).toInt());
|
||||||
|
m_plc_master_data.headerFont.setBold(xml_hfont.attribute(QStringLiteral("bold")) == QLatin1String("true"));
|
||||||
|
}
|
||||||
|
auto xml_cfont = xml_plc.firstChildElement(QStringLiteral("cellFont"));
|
||||||
|
if (!xml_cfont.isNull()) {
|
||||||
|
m_plc_master_data.cellFont.setFamily(xml_cfont.attribute(QStringLiteral("family")));
|
||||||
|
m_plc_master_data.cellFont.setPointSize(xml_cfont.attribute(QStringLiteral("size")).toInt());
|
||||||
|
m_plc_master_data.cellFont.setBold(xml_cfont.attribute(QStringLiteral("bold")) == QLatin1String("true"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load custom column names
|
||||||
|
auto xml_names = xml_plc.firstChildElement(QStringLiteral("columnNames"));
|
||||||
|
for (const auto &xml_col : QETXML::findInDomElement(xml_names, QStringLiteral("column"))) {
|
||||||
|
int idx = xml_col.attribute(QStringLiteral("index")).toInt();
|
||||||
|
while (m_plc_master_data.columnNames.size() <= idx)
|
||||||
|
m_plc_master_data.columnNames.append(QString());
|
||||||
|
m_plc_master_data.columnNames.replace(idx, xml_col.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load column order
|
||||||
|
auto xml_order = xml_plc.firstChildElement(QStringLiteral("columnOrder"));
|
||||||
|
if (!xml_order.isNull()) {
|
||||||
|
QStringList order_str_list = xml_order.text().split(',');
|
||||||
|
for (const auto &s : order_str_list) {
|
||||||
|
m_plc_master_data.columnOrder.append(s.trimmed().toInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load showHeaders
|
||||||
|
auto xml_sh = xml_plc.firstChildElement(QStringLiteral("showHeaders"));
|
||||||
|
if (!xml_sh.isNull()) {
|
||||||
|
m_plc_master_data.showHeaders = xml_sh.text() == QLatin1String("1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load IO entries
|
||||||
|
auto xml_ios = xml_plc.firstChildElement(QStringLiteral("plcIOs"));
|
||||||
|
for (const auto &xml_io : QETXML::findInDomElement(xml_ios, QStringLiteral("plcIO"))) {
|
||||||
|
PlcIO io;
|
||||||
|
io.type = plcIOTypeFromString(xml_io.attribute(QStringLiteral("type")));
|
||||||
|
io.address = xml_io.attribute(QStringLiteral("address"));
|
||||||
|
io.functionText = xml_io.attribute(QStringLiteral("functionText"));
|
||||||
|
io.comment = xml_io.attribute(QStringLiteral("comment"));
|
||||||
|
io.crossRef = xml_io.attribute(QStringLiteral("crossRef"));
|
||||||
|
io.terminalCount = xml_io.attribute(QStringLiteral("terminalCount")).toInt();
|
||||||
|
for (const auto &xml_term : QETXML::findInDomElement(xml_io, QStringLiteral("terminal"))) {
|
||||||
|
io.terminals.append(xml_term.text());
|
||||||
|
}
|
||||||
|
m_plc_master_data.ios.append(io);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief ElementData::setTerminalType
|
* @brief ElementData::setTerminalType
|
||||||
* Override the terminal type by \p t_type
|
* Override the terminal type by \p t_type
|
||||||
|
|||||||
@@ -130,6 +130,11 @@ class ElementData : public PropertiesInterface
|
|||||||
QList<int> columnOrder; ///< Column display order (logical indices)
|
QList<int> columnOrder; ///< Column display order (logical indices)
|
||||||
bool showHeaders = true; ///< Show column headers on sheet
|
bool showHeaders = true; ///< Show column headers on sheet
|
||||||
|
|
||||||
|
PlcMasterData() {
|
||||||
|
headerFont.setFamily(QString());
|
||||||
|
cellFont.setFamily(QString());
|
||||||
|
}
|
||||||
|
|
||||||
bool operator==(const PlcMasterData &other) const {
|
bool operator==(const PlcMasterData &other) const {
|
||||||
return ios == other.ios
|
return ios == other.ios
|
||||||
&& breakPositions == other.breakPositions
|
&& breakPositions == other.breakPositions
|
||||||
@@ -201,6 +206,8 @@ class ElementData : public PropertiesInterface
|
|||||||
QDomElement toXml(QDomDocument &xml_element) const override;
|
QDomElement toXml(QDomDocument &xml_element) const override;
|
||||||
bool fromXml(const QDomElement &xml_element) override;
|
bool fromXml(const QDomElement &xml_element) override;
|
||||||
QDomElement kindInfoToXml(QDomDocument &document);
|
QDomElement kindInfoToXml(QDomDocument &document);
|
||||||
|
QDomElement plcMasterDataToXml(QDomDocument &document) const;
|
||||||
|
void plcMasterDataFromXml(const QDomElement &xml_plc);
|
||||||
|
|
||||||
void setTerminalType(ElementData::TerminalType t_type);
|
void setTerminalType(ElementData::TerminalType t_type);
|
||||||
ElementData::TerminalType terminalType() const;
|
ElementData::TerminalType terminalType() const;
|
||||||
|
|||||||
@@ -16,6 +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 "element.h"
|
#include "element.h"
|
||||||
|
#include "../qetapp.h"
|
||||||
#include "../qetproject.h"
|
#include "../qetproject.h"
|
||||||
#include "../PropertiesEditor/propertieseditordialog.h"
|
#include "../PropertiesEditor/propertieseditordialog.h"
|
||||||
#include "../autoNum/assignvariables.h"
|
#include "../autoNum/assignvariables.h"
|
||||||
@@ -860,6 +861,15 @@ bool Element::fromXml(QDomElement &e,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Load PLC master data override from diagram XML
|
||||||
|
if (m_data.m_type == ElementData::Master &&
|
||||||
|
m_data.m_master_type == ElementData::PLC)
|
||||||
|
{
|
||||||
|
auto xml_plc = e.firstChildElement(QStringLiteral("plcMasterData"));
|
||||||
|
if (!xml_plc.isNull())
|
||||||
|
m_data.plcMasterDataFromXml(xml_plc);
|
||||||
|
}
|
||||||
|
|
||||||
//We must block the update of the alignment when loading the information
|
//We must block the update of the alignment when loading the information
|
||||||
//otherwise the pos of the text will not be the same as it was at save time.
|
//otherwise the pos of the text will not be the same as it was at save time.
|
||||||
for(DynamicElementTextItem *deti : m_dynamic_text_list)
|
for(DynamicElementTextItem *deti : m_dynamic_text_list)
|
||||||
@@ -993,6 +1003,15 @@ QDomElement Element::toXml(
|
|||||||
element.appendChild(properties);
|
element.appendChild(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Save PLC master data override for elements on diagram
|
||||||
|
if (m_data.m_type == ElementData::Master &&
|
||||||
|
m_data.m_master_type == ElementData::PLC)
|
||||||
|
{
|
||||||
|
auto xml_plc = m_data.plcMasterDataToXml(document);
|
||||||
|
if (!xml_plc.isNull())
|
||||||
|
element.appendChild(xml_plc);
|
||||||
|
}
|
||||||
|
|
||||||
//Dynamic texts
|
//Dynamic texts
|
||||||
QDomElement dyn_text = document.createElement(QStringLiteral("dynamic_texts"));
|
QDomElement dyn_text = document.createElement(QStringLiteral("dynamic_texts"));
|
||||||
for (DynamicElementTextItem *deti : m_dynamic_text_list)
|
for (DynamicElementTextItem *deti : m_dynamic_text_list)
|
||||||
@@ -1876,12 +1895,12 @@ void Element::drawPlcTable(QPainter *painter)
|
|||||||
// Fonts
|
// Fonts
|
||||||
QFont header_font = plc_data.headerFont;
|
QFont header_font = plc_data.headerFont;
|
||||||
if (header_font.family().isEmpty()) {
|
if (header_font.family().isEmpty()) {
|
||||||
header_font = painter->font();
|
header_font = QETApp::diagramTextsFont();
|
||||||
header_font.setBold(true);
|
header_font.setBold(true);
|
||||||
}
|
}
|
||||||
QFont cell_font = plc_data.cellFont;
|
QFont cell_font = plc_data.cellFont;
|
||||||
if (cell_font.family().isEmpty()) {
|
if (cell_font.family().isEmpty()) {
|
||||||
cell_font = painter->font();
|
cell_font = QETApp::diagramTextsFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const QPointF &pos : positions) {
|
for (const QPointF &pos : positions) {
|
||||||
|
|||||||
@@ -535,7 +535,7 @@ void MasterPropertiesWidget::updateUi()
|
|||||||
tr("Commentaire"), tr("Réf. croisée")
|
tr("Commentaire"), tr("Réf. croisée")
|
||||||
});
|
});
|
||||||
m_plc_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
m_plc_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||||||
m_plc_table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
m_plc_table->setSelectionBehavior(QAbstractItemView::SelectItems);
|
||||||
m_plc_table->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
m_plc_table->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
m_plc_table->setMinimumHeight(200);
|
m_plc_table->setMinimumHeight(200);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user