1408 lines
37 KiB
C++
1408 lines
37 KiB
C++
#include "tablewidgetfactory.h"
|
||
|
||
#include <QAbstractItemView>
|
||
#include <QCheckBox>
|
||
#include <QComboBox>
|
||
#include <QDateTime>
|
||
#include <QDateTimeEdit>
|
||
#include <QDebug>
|
||
#include <QDoubleSpinBox>
|
||
#include <QGridLayout>
|
||
#include <QHeaderView>
|
||
#include <QLabel>
|
||
#include <QLineEdit>
|
||
#include <QRegularExpression>
|
||
#include <QRegularExpressionValidator>
|
||
#include <QStringList>
|
||
#include <QTableWidget>
|
||
#include <QTableWidgetItem>
|
||
#include <QVBoxLayout>
|
||
#include <QWidget>
|
||
|
||
#include <algorithm>
|
||
|
||
TableWidgetFactory::TableWidgetFactory(QObject *parent)
|
||
: QObject(parent)
|
||
{
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createTableWidget(const TableDefinition &tableDef)
|
||
{
|
||
QWidget *widget = nullptr;
|
||
|
||
if (tableDef.tableType == "grid")
|
||
{
|
||
widget = createGridTable(tableDef);
|
||
}
|
||
else if (tableDef.tableType == "form")
|
||
{
|
||
widget = createFormTable(tableDef);
|
||
}
|
||
else if (tableDef.tableType == "series")
|
||
{
|
||
widget = createSeriesTable(tableDef);
|
||
}
|
||
else
|
||
{
|
||
qWarning() << "Unknown table type:" << tableDef.tableType;
|
||
widget = new QWidget();
|
||
}
|
||
|
||
if (widget)
|
||
{
|
||
tableData[widget].definition = tableDef;
|
||
qDebug() << "Created table widget for:" << tableDef.id << "name:" << tableDef.name;
|
||
}
|
||
|
||
return widget;
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createGridTable(const TableDefinition &tableDef)
|
||
{
|
||
QWidget *container = new QWidget();
|
||
QVBoxLayout *layout = new QVBoxLayout(container);
|
||
layout->setContentsMargins(12, 12, 12, 12);
|
||
|
||
// 標題 - Material Design 風格
|
||
if (!tableDef.name.isEmpty())
|
||
{
|
||
QLabel *titleLabel = new QLabel(tableDef.name, container);
|
||
titleLabel->setStyleSheet("font-weight: 500; font-size: 14px; color: #212121; margin-bottom: 4px;");
|
||
layout->addWidget(titleLabel);
|
||
}
|
||
|
||
// 描述
|
||
if (!tableDef.description.isEmpty())
|
||
{
|
||
QLabel *descLabel = new QLabel(tableDef.description, container);
|
||
descLabel->setWordWrap(true);
|
||
descLabel->setStyleSheet("color: #757575; font-size: 13px; margin-bottom: 8px;");
|
||
layout->addWidget(descLabel);
|
||
}
|
||
|
||
// 創建表格
|
||
QTableWidget *table = new QTableWidget(container);
|
||
table->setRowCount(tableDef.rowHeaders.size());
|
||
table->setColumnCount(tableDef.columnHeaders.size());
|
||
|
||
// Material Design 樣式
|
||
table->setStyleSheet(
|
||
"QTableWidget {"
|
||
" border: 1px solid #E0E0E0;"
|
||
" border-radius: 8px;"
|
||
" gridline-color: #EEEEEE;"
|
||
" background-color: white;"
|
||
"}"
|
||
"QTableWidget::item {"
|
||
" padding: 8px 12px;"
|
||
" color: #212121;"
|
||
"}"
|
||
"QTableWidget::item:selected {"
|
||
" background-color: #E3F2FD;"
|
||
" color: #1976D2;"
|
||
"}"
|
||
"QHeaderView::section {"
|
||
" background-color: #2196F3;"
|
||
" color: white;"
|
||
" padding: 10px 12px;"
|
||
" border: none;"
|
||
" font-weight: 500;"
|
||
" font-size: 13px;"
|
||
"}");
|
||
|
||
// 列標題
|
||
QStringList columnLabels;
|
||
for (const FieldDefinition &colDef : tableDef.columnHeaders)
|
||
{
|
||
QString label = colDef.name;
|
||
if (!colDef.unit.isEmpty())
|
||
{
|
||
label += QString("\n(%1)").arg(colDef.unit);
|
||
}
|
||
if (colDef.isRequired)
|
||
{
|
||
label += " *";
|
||
}
|
||
columnLabels << label;
|
||
}
|
||
table->setHorizontalHeaderLabels(columnLabels);
|
||
|
||
// 行標題
|
||
QStringList rowLabels;
|
||
for (const auto &rowHeader : tableDef.rowHeaders)
|
||
{
|
||
rowLabels << rowHeader.second;
|
||
}
|
||
table->setVerticalHeaderLabels(rowLabels);
|
||
|
||
// 調整列寬
|
||
table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||
table->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||
|
||
// 初始化單元格
|
||
for (int row = 0; row < table->rowCount(); ++row)
|
||
{
|
||
for (int col = 0; col < table->columnCount(); ++col)
|
||
{
|
||
QTableWidgetItem *item = new QTableWidgetItem("");
|
||
table->setItem(row, col, item);
|
||
|
||
// 設置只讀(計算字段)
|
||
if (col < tableDef.columnHeaders.size())
|
||
{
|
||
const FieldDefinition &colDef = tableDef.columnHeaders[col];
|
||
if (colDef.isReadOnly || colDef.type == "calculated")
|
||
{
|
||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||
item->setBackground(QColor("#FAFAFA"));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 連接信號
|
||
connect(table, &QTableWidget::itemChanged, this,
|
||
[this, container, tableDef](QTableWidgetItem *item)
|
||
{
|
||
if (!item)
|
||
return;
|
||
|
||
int row = item->row();
|
||
int col = item->column();
|
||
|
||
if (row < tableDef.rowHeaders.size() && col < tableDef.columnHeaders.size())
|
||
{
|
||
QString rowId = tableDef.rowHeaders[row].first;
|
||
QString colId = tableDef.columnHeaders[col].id;
|
||
emit cellValueChanged(container, rowId, colId, item->text());
|
||
}
|
||
});
|
||
|
||
layout->addWidget(table);
|
||
|
||
// 存儲 grid widget
|
||
tableData[container].gridWidget = table;
|
||
|
||
// 應用靜態單元格
|
||
applyStaticCells(container, tableDef);
|
||
|
||
return container;
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createSeriesTable(const TableDefinition &tableDef)
|
||
{
|
||
QWidget *container = new QWidget();
|
||
QVBoxLayout *layout = new QVBoxLayout(container);
|
||
layout->setContentsMargins(12, 12, 12, 12);
|
||
|
||
// 標題 - Material Design 風格
|
||
if (!tableDef.name.isEmpty())
|
||
{
|
||
QLabel *titleLabel = new QLabel(tableDef.name, container);
|
||
titleLabel->setStyleSheet("font-weight: 500; font-size: 14px; color: #212121; margin-bottom: 4px;");
|
||
layout->addWidget(titleLabel);
|
||
}
|
||
|
||
// 描述
|
||
if (!tableDef.description.isEmpty())
|
||
{
|
||
QLabel *descLabel = new QLabel(tableDef.description, container);
|
||
descLabel->setWordWrap(true);
|
||
descLabel->setStyleSheet("color: #757575; font-size: 13px; margin-bottom: 8px;");
|
||
layout->addWidget(descLabel);
|
||
}
|
||
|
||
// 創建時序數據表格
|
||
QTableWidget *table = new QTableWidget(container);
|
||
|
||
// 設置列:時間戳 + 字段
|
||
int columnCount = 1 + tableDef.fields.size();
|
||
table->setColumnCount(columnCount);
|
||
|
||
// 列標題
|
||
QStringList columnLabels;
|
||
columnLabels << "時間戳";
|
||
|
||
for (const FieldDefinition &fieldDef : tableDef.fields)
|
||
{
|
||
QString label = fieldDef.name;
|
||
if (!fieldDef.unit.isEmpty())
|
||
{
|
||
label += QString(" (%1)").arg(fieldDef.unit);
|
||
}
|
||
columnLabels << label;
|
||
}
|
||
table->setHorizontalHeaderLabels(columnLabels);
|
||
|
||
// Material Design 樣式 - 使用綠色強調時序數據
|
||
table->setStyleSheet(
|
||
"QTableWidget {"
|
||
" border: 1px solid #E0E0E0;"
|
||
" border-radius: 8px;"
|
||
" gridline-color: #EEEEEE;"
|
||
" background-color: white;"
|
||
"}"
|
||
"QTableWidget::item {"
|
||
" padding: 8px 12px;"
|
||
" color: #212121;"
|
||
"}"
|
||
"QTableWidget::item:selected {"
|
||
" background-color: #E8F5E9;"
|
||
" color: #388E3C;"
|
||
"}"
|
||
"QHeaderView::section {"
|
||
" background-color: #4CAF50;"
|
||
" color: white;"
|
||
" padding: 10px 12px;"
|
||
" border: none;"
|
||
" font-weight: 500;"
|
||
" font-size: 13px;"
|
||
"}");
|
||
|
||
// 配置表格
|
||
table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||
table->verticalHeader()->setVisible(false);
|
||
table->setAlternatingRowColors(true);
|
||
table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
table->setSortingEnabled(true);
|
||
|
||
layout->addWidget(table);
|
||
|
||
// 存儲 series widget
|
||
tableData[container].seriesWidget = table;
|
||
|
||
// 應用靜態單元格
|
||
applyStaticCells(container, tableDef);
|
||
|
||
return container;
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createFormTable(const TableDefinition &tableDef)
|
||
{
|
||
QWidget *container = new QWidget();
|
||
QVBoxLayout *mainLayout = new QVBoxLayout(container);
|
||
mainLayout->setContentsMargins(12, 12, 12, 12);
|
||
|
||
// 標題 - Material Design 風格
|
||
if (!tableDef.name.isEmpty())
|
||
{
|
||
QLabel *titleLabel = new QLabel(tableDef.name, container);
|
||
titleLabel->setStyleSheet("font-weight: 500; font-size: 14px; color: #212121; margin-bottom: 4px;");
|
||
mainLayout->addWidget(titleLabel);
|
||
}
|
||
|
||
// 描述
|
||
if (!tableDef.description.isEmpty())
|
||
{
|
||
QLabel *descLabel = new QLabel(tableDef.description, container);
|
||
descLabel->setWordWrap(true);
|
||
descLabel->setStyleSheet("color: #757575; font-size: 13px; margin-bottom: 12px;");
|
||
mainLayout->addWidget(descLabel);
|
||
}
|
||
|
||
// 創建網格佈局(12列系統)
|
||
int totalColumns = 12;
|
||
if (tableDef.layoutConfig.contains("totalColumns"))
|
||
{
|
||
totalColumns = tableDef.layoutConfig["totalColumns"].toInt();
|
||
}
|
||
Q_UNUSED(totalColumns);
|
||
|
||
QGridLayout *gridLayout = new QGridLayout();
|
||
gridLayout->setSpacing(10);
|
||
|
||
// 按順序排序字段
|
||
QList<FieldDefinition> sortedFields = tableDef.fields;
|
||
std::sort(sortedFields.begin(), sortedFields.end(),
|
||
[](const FieldDefinition &a, const FieldDefinition &b)
|
||
{
|
||
int orderA = a.layoutConfig.value("order", 999).toInt();
|
||
int orderB = b.layoutConfig.value("order", 999).toInt();
|
||
return orderA < orderB;
|
||
});
|
||
|
||
// 創建字段組件
|
||
int currentRow = 0;
|
||
QMap<QString, QWidget *> fields;
|
||
|
||
for (const FieldDefinition &fieldDef : sortedFields)
|
||
{
|
||
// 獲取佈局配置
|
||
int labelSpan = fieldDef.layoutConfig.value("labelSpan", 4).toInt();
|
||
int valueSpan = fieldDef.layoutConfig.value("valueSpan", 8).toInt();
|
||
int rowSpan = fieldDef.layoutConfig.value("rowSpan", 1).toInt();
|
||
|
||
// 創建標籤
|
||
QString labelText = fieldDef.name;
|
||
if (!fieldDef.unit.isEmpty())
|
||
{
|
||
labelText += QString(" (%1)").arg(fieldDef.unit);
|
||
}
|
||
if (fieldDef.isRequired)
|
||
{
|
||
labelText += " <span style='color: red;'>*</span>";
|
||
}
|
||
|
||
QLabel *label = new QLabel(labelText, container);
|
||
label->setTextFormat(Qt::RichText);
|
||
label->setStyleSheet("font-weight: 500; color: #455A64; font-size: 14px;");
|
||
label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||
|
||
// 創建字段組件
|
||
QWidget *fieldWidget = createFieldWidget(fieldDef, container);
|
||
|
||
if (fieldWidget)
|
||
{
|
||
// 添加到網格
|
||
gridLayout->addWidget(label, currentRow, 0, rowSpan, labelSpan);
|
||
gridLayout->addWidget(fieldWidget, currentRow, labelSpan, rowSpan, valueSpan);
|
||
|
||
fields[fieldDef.id] = fieldWidget;
|
||
connectFieldSignals(fieldWidget, fieldDef.id, container);
|
||
}
|
||
|
||
currentRow += rowSpan;
|
||
}
|
||
|
||
mainLayout->addLayout(gridLayout);
|
||
mainLayout->addStretch();
|
||
|
||
// 存儲字段組件
|
||
tableData[container].fieldWidgets = fields;
|
||
|
||
// 應用靜態單元格
|
||
applyStaticCells(container, tableDef);
|
||
|
||
return container;
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createFieldWidget(const FieldDefinition &fieldDef, QWidget *parent)
|
||
{
|
||
QWidget *widget = nullptr;
|
||
|
||
if (fieldDef.type == "numeric")
|
||
{
|
||
widget = createNumericField(fieldDef, parent);
|
||
}
|
||
else if (fieldDef.type == "text")
|
||
{
|
||
widget = createTextField(fieldDef, parent);
|
||
}
|
||
else if (fieldDef.type == "selection")
|
||
{
|
||
widget = createSelectionField(fieldDef, parent);
|
||
}
|
||
else if (fieldDef.type == "boolean")
|
||
{
|
||
widget = createBooleanField(fieldDef, parent);
|
||
}
|
||
else if (fieldDef.type == "datetime")
|
||
{
|
||
widget = createDateTimeField(fieldDef, parent);
|
||
}
|
||
else if (fieldDef.type == "calculated")
|
||
{
|
||
widget = createCalculatedField(fieldDef, parent);
|
||
}
|
||
|
||
if (widget)
|
||
{
|
||
widget->setEnabled(!fieldDef.isReadOnly);
|
||
}
|
||
|
||
return widget;
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createNumericField(const FieldDefinition &fieldDef, QWidget *parent)
|
||
{
|
||
QDoubleSpinBox *spinBox = new QDoubleSpinBox(parent);
|
||
|
||
// 範圍
|
||
double minVal = -999999.0;
|
||
double maxVal = 999999.0;
|
||
|
||
if (fieldDef.validationRules.contains("min"))
|
||
{
|
||
minVal = fieldDef.validationRules["min"].toDouble();
|
||
}
|
||
if (fieldDef.validationRules.contains("max"))
|
||
{
|
||
maxVal = fieldDef.validationRules["max"].toDouble();
|
||
}
|
||
|
||
spinBox->setRange(minVal, maxVal);
|
||
|
||
// 精度
|
||
int decimals = 3;
|
||
if (fieldDef.validationRules.contains("precision"))
|
||
{
|
||
decimals = fieldDef.validationRules["precision"].toInt();
|
||
}
|
||
spinBox->setDecimals(decimals);
|
||
|
||
// 默認值
|
||
if (fieldDef.defaultValue.isValid())
|
||
{
|
||
spinBox->setValue(fieldDef.defaultValue.toDouble());
|
||
}
|
||
|
||
// Material Design 樣式
|
||
spinBox->setStyleSheet(
|
||
"QDoubleSpinBox {"
|
||
" padding: 10px 12px;"
|
||
" border: 1px solid #E0E0E0;"
|
||
" border-radius: 8px;"
|
||
" background-color: white;"
|
||
" font-size: 14px;"
|
||
" color: #212121;"
|
||
"}"
|
||
"QDoubleSpinBox:focus {"
|
||
" border: 2px solid #2196F3;"
|
||
"}"
|
||
"QDoubleSpinBox:disabled {"
|
||
" background-color: #FAFAFA;"
|
||
" color: #9E9E9E;"
|
||
"}");
|
||
|
||
return spinBox;
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createTextField(const FieldDefinition &fieldDef, QWidget *parent)
|
||
{
|
||
QLineEdit *lineEdit = new QLineEdit(parent);
|
||
|
||
// 默認值
|
||
if (fieldDef.defaultValue.isValid())
|
||
{
|
||
lineEdit->setText(fieldDef.defaultValue.toString());
|
||
}
|
||
|
||
// 最大長度
|
||
if (fieldDef.validationRules.contains("maxLength"))
|
||
{
|
||
lineEdit->setMaxLength(fieldDef.validationRules["maxLength"].toInt());
|
||
}
|
||
|
||
// 正則驗證
|
||
if (fieldDef.validationRules.contains("pattern"))
|
||
{
|
||
QString pattern = fieldDef.validationRules["pattern"].toString();
|
||
QRegularExpression regex(pattern);
|
||
QRegularExpressionValidator *validator = new QRegularExpressionValidator(regex, lineEdit);
|
||
lineEdit->setValidator(validator);
|
||
}
|
||
|
||
// Material Design 樣式
|
||
lineEdit->setStyleSheet(
|
||
"QLineEdit {"
|
||
" padding: 10px 12px;"
|
||
" border: 1px solid #E0E0E0;"
|
||
" border-radius: 8px;"
|
||
" background-color: white;"
|
||
" font-size: 14px;"
|
||
" color: #212121;"
|
||
"}"
|
||
"QLineEdit:focus {"
|
||
" border: 2px solid #2196F3;"
|
||
"}"
|
||
"QLineEdit:disabled {"
|
||
" background-color: #FAFAFA;"
|
||
" color: #9E9E9E;"
|
||
"}");
|
||
|
||
return lineEdit;
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createSelectionField(const FieldDefinition &fieldDef, QWidget *parent)
|
||
{
|
||
QComboBox *comboBox = new QComboBox(parent);
|
||
|
||
// 添加選項
|
||
for (const QString &option : fieldDef.options)
|
||
{
|
||
comboBox->addItem(option);
|
||
}
|
||
|
||
// 默認值
|
||
if (fieldDef.defaultValue.isValid())
|
||
{
|
||
int index = comboBox->findText(fieldDef.defaultValue.toString());
|
||
if (index >= 0)
|
||
{
|
||
comboBox->setCurrentIndex(index);
|
||
}
|
||
}
|
||
|
||
// Material Design 樣式
|
||
comboBox->setStyleSheet(
|
||
"QComboBox {"
|
||
" padding: 10px 12px;"
|
||
" border: 1px solid #E0E0E0;"
|
||
" border-radius: 8px;"
|
||
" background-color: white;"
|
||
" font-size: 14px;"
|
||
" color: #212121;"
|
||
"}"
|
||
"QComboBox:hover {"
|
||
" border-color: #BDBDBD;"
|
||
"}"
|
||
"QComboBox:focus {"
|
||
" border: 2px solid #2196F3;"
|
||
"}");
|
||
|
||
return comboBox;
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createBooleanField(const FieldDefinition &fieldDef, QWidget *parent)
|
||
{
|
||
QCheckBox *checkBox = new QCheckBox(parent);
|
||
|
||
// 默認值
|
||
if (fieldDef.defaultValue.isValid())
|
||
{
|
||
checkBox->setChecked(fieldDef.defaultValue.toBool());
|
||
}
|
||
|
||
// Material Design 樣式
|
||
checkBox->setStyleSheet(
|
||
"QCheckBox { font-size: 14px; color: #455A64; spacing: 8px; }"
|
||
"QCheckBox::indicator { width: 22px; height: 22px; border: 2px solid #BDBDBD; border-radius: 4px; background: white; }"
|
||
"QCheckBox::indicator:checked { background: #2196F3; border-color: #2196F3; }"
|
||
"QCheckBox::indicator:hover { border-color: #2196F3; }");
|
||
|
||
return checkBox;
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createDateTimeField(const FieldDefinition &fieldDef, QWidget *parent)
|
||
{
|
||
QDateTimeEdit *dateTimeEdit = new QDateTimeEdit(parent);
|
||
dateTimeEdit->setCalendarPopup(true);
|
||
dateTimeEdit->setDisplayFormat("yyyy-MM-dd HH:mm:ss");
|
||
|
||
// 默認值
|
||
if (fieldDef.defaultValue.isValid())
|
||
{
|
||
dateTimeEdit->setDateTime(fieldDef.defaultValue.toDateTime());
|
||
}
|
||
else
|
||
{
|
||
dateTimeEdit->setDateTime(QDateTime::currentDateTime());
|
||
}
|
||
|
||
// Material Design 樣式
|
||
dateTimeEdit->setStyleSheet(
|
||
"QDateTimeEdit {"
|
||
" padding: 10px 12px;"
|
||
" border: 1px solid #E0E0E0;"
|
||
" border-radius: 8px;"
|
||
" background-color: white;"
|
||
" font-size: 14px;"
|
||
" color: #212121;"
|
||
"}"
|
||
"QDateTimeEdit:focus {"
|
||
" border: 2px solid #2196F3;"
|
||
"}");
|
||
|
||
return dateTimeEdit;
|
||
}
|
||
|
||
QWidget *TableWidgetFactory::createCalculatedField(const FieldDefinition &fieldDef, QWidget *parent)
|
||
{
|
||
Q_UNUSED(fieldDef);
|
||
QLineEdit *lineEdit = new QLineEdit(parent);
|
||
lineEdit->setReadOnly(true);
|
||
lineEdit->setPlaceholderText("計算字段");
|
||
|
||
// Material Design 只讀樣式
|
||
lineEdit->setStyleSheet(
|
||
"QLineEdit {"
|
||
" padding: 10px 12px;"
|
||
" border: 1px solid #E0E0E0;"
|
||
" border-radius: 8px;"
|
||
" background-color: #FAFAFA;"
|
||
" color: #9E9E9E;"
|
||
" font-size: 14px;"
|
||
"}");
|
||
|
||
return lineEdit;
|
||
}
|
||
|
||
void TableWidgetFactory::connectFieldSignals(QWidget *fieldWidget, const QString &fieldId,
|
||
QWidget *tableWidget)
|
||
{
|
||
if (QDoubleSpinBox *spinBox = qobject_cast<QDoubleSpinBox *>(fieldWidget))
|
||
{
|
||
connect(spinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
|
||
[this, fieldId, tableWidget](double value)
|
||
{
|
||
emit fieldValueChanged(tableWidget, fieldId, value);
|
||
});
|
||
}
|
||
else if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(fieldWidget))
|
||
{
|
||
connect(lineEdit, &QLineEdit::textChanged, this,
|
||
[this, fieldId, tableWidget](const QString &text)
|
||
{
|
||
emit fieldValueChanged(tableWidget, fieldId, text);
|
||
});
|
||
}
|
||
else if (QComboBox *comboBox = qobject_cast<QComboBox *>(fieldWidget))
|
||
{
|
||
connect(comboBox, &QComboBox::currentTextChanged, this,
|
||
[this, fieldId, tableWidget](const QString &text)
|
||
{
|
||
emit fieldValueChanged(tableWidget, fieldId, text);
|
||
});
|
||
}
|
||
else if (QCheckBox *checkBox = qobject_cast<QCheckBox *>(fieldWidget))
|
||
{
|
||
connect(checkBox, &QCheckBox::toggled, this,
|
||
[this, fieldId, tableWidget](bool checked)
|
||
{
|
||
emit fieldValueChanged(tableWidget, fieldId, checked);
|
||
});
|
||
}
|
||
else if (QDateTimeEdit *dateTimeEdit = qobject_cast<QDateTimeEdit *>(fieldWidget))
|
||
{
|
||
connect(dateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
|
||
[this, fieldId, tableWidget](const QDateTime &dateTime)
|
||
{
|
||
emit fieldValueChanged(tableWidget, fieldId, dateTime);
|
||
});
|
||
}
|
||
}
|
||
|
||
void TableWidgetFactory::applyStaticCells(QWidget *tableWidget, const TableDefinition &tableDef)
|
||
{
|
||
if (!tableData.contains(tableWidget) || tableDef.staticCells.isEmpty())
|
||
{
|
||
return;
|
||
}
|
||
|
||
const TableWidgetData &data = tableData[tableWidget];
|
||
|
||
for (const StaticCell &staticCell : tableDef.staticCells)
|
||
{
|
||
if (tableDef.tableType == "form" || tableDef.tableType == "series")
|
||
{
|
||
// 處理 form/series 類型表格
|
||
if (!staticCell.field.isEmpty() && data.fieldWidgets.contains(staticCell.field))
|
||
{
|
||
QWidget *fieldWidget = data.fieldWidgets[staticCell.field];
|
||
|
||
// 設置靜態內容並設為只讀 - Material Design 風格
|
||
if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(fieldWidget))
|
||
{
|
||
lineEdit->setText(staticCell.content);
|
||
lineEdit->setReadOnly(true);
|
||
lineEdit->setStyleSheet(
|
||
"QLineEdit {"
|
||
" padding: 10px 12px;"
|
||
" border: 1px solid #E0E0E0;"
|
||
" border-radius: 8px;"
|
||
" background-color: #FAFAFA;"
|
||
" color: #616161;"
|
||
" font-size: 14px;"
|
||
"}");
|
||
}
|
||
else if (QDoubleSpinBox *spinBox = qobject_cast<QDoubleSpinBox *>(fieldWidget))
|
||
{
|
||
spinBox->setValue(staticCell.content.toDouble());
|
||
spinBox->setReadOnly(true);
|
||
spinBox->setStyleSheet(
|
||
"QDoubleSpinBox {"
|
||
" padding: 10px 12px;"
|
||
" border: 1px solid #E0E0E0;"
|
||
" border-radius: 8px;"
|
||
" background-color: #FAFAFA;"
|
||
" color: #616161;"
|
||
" font-size: 14px;"
|
||
"}");
|
||
}
|
||
else if (QComboBox *comboBox = qobject_cast<QComboBox *>(fieldWidget))
|
||
{
|
||
int index = comboBox->findText(staticCell.content);
|
||
if (index >= 0)
|
||
{
|
||
comboBox->setCurrentIndex(index);
|
||
}
|
||
comboBox->setEnabled(false);
|
||
}
|
||
else if (QCheckBox *checkBox = qobject_cast<QCheckBox *>(fieldWidget))
|
||
{
|
||
checkBox->setChecked(staticCell.content.toLower() == "true");
|
||
checkBox->setEnabled(false);
|
||
}
|
||
}
|
||
}
|
||
else if (tableDef.tableType == "grid")
|
||
{
|
||
// 處理 grid 類型表格
|
||
if (data.gridWidget)
|
||
{
|
||
QTableWidget *table = data.gridWidget;
|
||
int rowIndex = staticCell.row;
|
||
int colIndex = staticCell.column;
|
||
|
||
if (rowIndex >= 0 && colIndex >= 0 &&
|
||
rowIndex < table->rowCount() && colIndex < table->columnCount())
|
||
{
|
||
QTableWidgetItem *item = table->item(rowIndex, colIndex);
|
||
if (!item)
|
||
{
|
||
item = new QTableWidgetItem();
|
||
table->setItem(rowIndex, colIndex, item);
|
||
}
|
||
|
||
// 設置靜態內容並設為只讀 - Material Design 風格
|
||
item->setText(staticCell.content);
|
||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||
item->setBackground(QColor("#FAFAFA"));
|
||
item->setForeground(QColor("#616161"));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// =====================================================
|
||
// Form 表格數據訪問
|
||
// =====================================================
|
||
|
||
bool TableWidgetFactory::setFieldValue(QWidget *tableWidget, const QString &fieldId,
|
||
const QVariant &value)
|
||
{
|
||
if (!tableData.contains(tableWidget))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
const TableWidgetData &data = tableData[tableWidget];
|
||
if (!data.fieldWidgets.contains(fieldId))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
QWidget *fieldWidget = data.fieldWidgets[fieldId];
|
||
|
||
if (QDoubleSpinBox *spinBox = qobject_cast<QDoubleSpinBox *>(fieldWidget))
|
||
{
|
||
spinBox->setValue(value.toDouble());
|
||
}
|
||
else if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(fieldWidget))
|
||
{
|
||
lineEdit->setText(value.toString());
|
||
}
|
||
else if (QComboBox *comboBox = qobject_cast<QComboBox *>(fieldWidget))
|
||
{
|
||
int index = comboBox->findText(value.toString());
|
||
if (index >= 0)
|
||
{
|
||
comboBox->setCurrentIndex(index);
|
||
}
|
||
}
|
||
else if (QCheckBox *checkBox = qobject_cast<QCheckBox *>(fieldWidget))
|
||
{
|
||
checkBox->setChecked(value.toBool());
|
||
}
|
||
else if (QDateTimeEdit *dateTimeEdit = qobject_cast<QDateTimeEdit *>(fieldWidget))
|
||
{
|
||
dateTimeEdit->setDateTime(value.toDateTime());
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
QVariant TableWidgetFactory::getFieldValue(QWidget *tableWidget, const QString &fieldId) const
|
||
{
|
||
if (!tableData.contains(tableWidget))
|
||
{
|
||
return QVariant();
|
||
}
|
||
|
||
const TableWidgetData &data = tableData[tableWidget];
|
||
if (!data.fieldWidgets.contains(fieldId))
|
||
{
|
||
return QVariant();
|
||
}
|
||
|
||
QWidget *fieldWidget = data.fieldWidgets[fieldId];
|
||
|
||
if (QDoubleSpinBox *spinBox = qobject_cast<QDoubleSpinBox *>(fieldWidget))
|
||
{
|
||
return spinBox->value();
|
||
}
|
||
else if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(fieldWidget))
|
||
{
|
||
return lineEdit->text();
|
||
}
|
||
else if (QComboBox *comboBox = qobject_cast<QComboBox *>(fieldWidget))
|
||
{
|
||
return comboBox->currentText();
|
||
}
|
||
else if (QCheckBox *checkBox = qobject_cast<QCheckBox *>(fieldWidget))
|
||
{
|
||
return checkBox->isChecked();
|
||
}
|
||
else if (QDateTimeEdit *dateTimeEdit = qobject_cast<QDateTimeEdit *>(fieldWidget))
|
||
{
|
||
return dateTimeEdit->dateTime();
|
||
}
|
||
|
||
return QVariant();
|
||
}
|
||
|
||
// =====================================================
|
||
// Grid 表格數據訪問
|
||
// =====================================================
|
||
|
||
bool TableWidgetFactory::setCellValue(QWidget *tableWidget, const QString &rowId,
|
||
const QString &columnId, const QVariant &value)
|
||
{
|
||
if (!tableData.contains(tableWidget))
|
||
{
|
||
qWarning() << "setCellValue: tableWidget not found in tableData";
|
||
return false;
|
||
}
|
||
|
||
const TableWidgetData &data = tableData[tableWidget];
|
||
if (!data.gridWidget)
|
||
{
|
||
qWarning() << "setCellValue: gridWidget is null";
|
||
return false;
|
||
}
|
||
|
||
QTableWidget *table = data.gridWidget;
|
||
const TableDefinition &tableDef = data.definition;
|
||
|
||
// 查找行索引
|
||
int rowIndex = -1;
|
||
for (int i = 0; i < tableDef.rowHeaders.size(); ++i)
|
||
{
|
||
if (tableDef.rowHeaders[i].first == rowId)
|
||
{
|
||
rowIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 查找列索引
|
||
int colIndex = -1;
|
||
for (int i = 0; i < tableDef.columnHeaders.size(); ++i)
|
||
{
|
||
if (tableDef.columnHeaders[i].id == columnId)
|
||
{
|
||
colIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (rowIndex >= 0 && colIndex >= 0 &&
|
||
rowIndex < table->rowCount() && colIndex < table->columnCount())
|
||
{
|
||
QTableWidgetItem *item = table->item(rowIndex, colIndex);
|
||
if (!item)
|
||
{
|
||
item = new QTableWidgetItem();
|
||
table->setItem(rowIndex, colIndex, item);
|
||
}
|
||
item->setText(value.toString());
|
||
return true;
|
||
}
|
||
|
||
qWarning() << "setCellValue: Invalid row/column index or out of bounds";
|
||
return false;
|
||
}
|
||
|
||
QVariant TableWidgetFactory::getCellValue(QWidget *tableWidget, const QString &rowId,
|
||
const QString &columnId) const
|
||
{
|
||
if (!tableData.contains(tableWidget))
|
||
{
|
||
return QVariant();
|
||
}
|
||
|
||
const TableWidgetData &data = tableData[tableWidget];
|
||
if (!data.gridWidget)
|
||
{
|
||
return QVariant();
|
||
}
|
||
|
||
QTableWidget *table = data.gridWidget;
|
||
const TableDefinition &tableDef = data.definition;
|
||
|
||
// 查找索引
|
||
int rowIndex = -1;
|
||
for (int i = 0; i < tableDef.rowHeaders.size(); ++i)
|
||
{
|
||
if (tableDef.rowHeaders[i].first == rowId)
|
||
{
|
||
rowIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
int colIndex = -1;
|
||
for (int i = 0; i < tableDef.columnHeaders.size(); ++i)
|
||
{
|
||
if (tableDef.columnHeaders[i].id == columnId)
|
||
{
|
||
colIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (rowIndex >= 0 && colIndex >= 0)
|
||
{
|
||
QTableWidgetItem *item = table->item(rowIndex, colIndex);
|
||
if (item)
|
||
{
|
||
return item->text();
|
||
}
|
||
}
|
||
|
||
return QVariant();
|
||
}
|
||
|
||
// =====================================================
|
||
// 通用方法
|
||
// =====================================================
|
||
|
||
TableDefinition TableWidgetFactory::getTableDefinition(QWidget *tableWidget) const
|
||
{
|
||
if (tableData.contains(tableWidget))
|
||
{
|
||
return tableData[tableWidget].definition;
|
||
}
|
||
return TableDefinition();
|
||
}
|
||
|
||
bool TableWidgetFactory::loadDataFromEngine(QWidget *tableWidget, const QString &tableRef,
|
||
const QVariantMap &engineData)
|
||
{
|
||
if (!tableData.contains(tableWidget))
|
||
{
|
||
qWarning() << "loadDataFromEngine: tableWidget not found in tableData";
|
||
return false;
|
||
}
|
||
|
||
const TableWidgetData &data = tableData[tableWidget];
|
||
const TableDefinition &tableDef = data.definition;
|
||
|
||
qDebug() << "Loading engine data into table widget - tableRef:" << tableRef
|
||
<< "entries:" << engineData.size() << "tableType:" << tableDef.tableType;
|
||
|
||
if (engineData.isEmpty())
|
||
{
|
||
qDebug() << " No data to load";
|
||
return true;
|
||
}
|
||
|
||
// 批量加載時阻斷信號
|
||
bool containerSignalsBlocked = tableWidget->signalsBlocked();
|
||
tableWidget->blockSignals(true);
|
||
|
||
bool gridSignalsBlocked = false;
|
||
if (data.gridWidget)
|
||
{
|
||
gridSignalsBlocked = data.gridWidget->signalsBlocked();
|
||
data.gridWidget->blockSignals(true);
|
||
}
|
||
|
||
// 阻斷字段組件信號
|
||
QList<bool> fieldSignalStates;
|
||
for (auto it = data.fieldWidgets.begin(); it != data.fieldWidgets.end(); ++it)
|
||
{
|
||
fieldSignalStates.append(it.value()->signalsBlocked());
|
||
it.value()->blockSignals(true);
|
||
}
|
||
|
||
bool success = true;
|
||
|
||
if (tableDef.tableType == "form")
|
||
{
|
||
// 加載表單字段
|
||
for (auto it = engineData.begin(); it != engineData.end(); ++it)
|
||
{
|
||
qDebug() << " Loading form field:" << it.key() << "=" << it.value();
|
||
if (!setFieldValue(tableWidget, it.key(), it.value()))
|
||
{
|
||
qWarning() << " Failed to set form field:" << it.key();
|
||
success = false;
|
||
}
|
||
}
|
||
}
|
||
else if (tableDef.tableType == "grid")
|
||
{
|
||
// 加載網格單元格
|
||
for (auto it = engineData.begin(); it != engineData.end(); ++it)
|
||
{
|
||
QStringList parts = it.key().split("_");
|
||
if (parts.size() == 2)
|
||
{
|
||
QString rowId = parts[0];
|
||
QString colId = parts[1];
|
||
qDebug() << " Loading grid cell:" << rowId << "," << colId << "=" << it.value();
|
||
if (!setCellValue(tableWidget, rowId, colId, it.value()))
|
||
{
|
||
qWarning() << " Failed to set grid cell:" << rowId << "," << colId;
|
||
success = false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
qWarning() << " Invalid cell key format:" << it.key();
|
||
}
|
||
}
|
||
}
|
||
else if (tableDef.tableType == "series")
|
||
{
|
||
// 時序數據格式:timestamp_fieldId -> value
|
||
QMap<QString, QVariantMap> timePoints;
|
||
|
||
for (auto it = engineData.begin(); it != engineData.end(); ++it)
|
||
{
|
||
QStringList parts = it.key().split("_", Qt::KeepEmptyParts);
|
||
if (parts.size() >= 2)
|
||
{
|
||
QString timestamp = parts[0];
|
||
QString fieldId = parts.mid(1).join("_");
|
||
|
||
if (!timePoints.contains(timestamp))
|
||
{
|
||
timePoints[timestamp] = QVariantMap();
|
||
}
|
||
timePoints[timestamp][fieldId] = it.value();
|
||
}
|
||
}
|
||
|
||
// 按時間順序添加數據點
|
||
QStringList timestamps = timePoints.keys();
|
||
timestamps.sort();
|
||
|
||
for (const QString ×tampStr : timestamps)
|
||
{
|
||
QDateTime timestamp = QDateTime::fromString(timestampStr, Qt::ISODate);
|
||
if (!timestamp.isValid())
|
||
{
|
||
timestamp = QDateTime::fromString(timestampStr, "yyyy-MM-dd hh:mm:ss.zzz");
|
||
}
|
||
if (!timestamp.isValid())
|
||
{
|
||
qWarning() << " Invalid timestamp format:" << timestampStr;
|
||
continue;
|
||
}
|
||
|
||
if (!addSeriesDataPoint(tableWidget, timestamp, timePoints[timestampStr]))
|
||
{
|
||
qWarning() << " Failed to add series data point for timestamp:" << timestampStr;
|
||
success = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 恢復信號狀態
|
||
tableWidget->blockSignals(containerSignalsBlocked);
|
||
|
||
if (data.gridWidget)
|
||
{
|
||
data.gridWidget->blockSignals(gridSignalsBlocked);
|
||
}
|
||
|
||
int i = 0;
|
||
for (auto it = data.fieldWidgets.begin(); it != data.fieldWidgets.end(); ++it)
|
||
{
|
||
if (i < fieldSignalStates.size())
|
||
{
|
||
it.value()->blockSignals(fieldSignalStates[i]);
|
||
}
|
||
i++;
|
||
}
|
||
|
||
qDebug() << "Finished loading engine data - success:" << success;
|
||
return success;
|
||
}
|
||
|
||
// =====================================================
|
||
// Series 表格特有方法
|
||
// =====================================================
|
||
|
||
bool TableWidgetFactory::addSeriesDataPoint(QWidget *tableWidget, const QDateTime ×tamp,
|
||
const QVariantMap &fieldValues)
|
||
{
|
||
if (!tableData.contains(tableWidget))
|
||
{
|
||
qWarning() << "addSeriesDataPoint: tableWidget not found in tableData";
|
||
return false;
|
||
}
|
||
|
||
const TableWidgetData &data = tableData[tableWidget];
|
||
if (!data.seriesWidget || data.definition.tableType != "series")
|
||
{
|
||
qWarning() << "addSeriesDataPoint: not a series table or seriesWidget is null";
|
||
return false;
|
||
}
|
||
|
||
QTableWidget *table = data.seriesWidget;
|
||
const TableDefinition &tableDef = data.definition;
|
||
|
||
// 添加新行
|
||
int newRow = table->rowCount();
|
||
table->insertRow(newRow);
|
||
|
||
// 設置時間戳(第一列)- Material Design 風格
|
||
QTableWidgetItem *timestampItem =
|
||
new QTableWidgetItem(timestamp.toString("yyyy-MM-dd hh:mm:ss.zzz"));
|
||
timestampItem->setFlags(timestampItem->flags() & ~Qt::ItemIsEditable);
|
||
timestampItem->setBackground(QColor("#FAFAFA"));
|
||
table->setItem(newRow, 0, timestampItem);
|
||
|
||
// 設置字段值
|
||
for (int i = 0; i < tableDef.fields.size(); ++i)
|
||
{
|
||
const FieldDefinition &fieldDef = tableDef.fields[i];
|
||
QVariant value = fieldValues.value(fieldDef.id);
|
||
|
||
QTableWidgetItem *item = new QTableWidgetItem();
|
||
if (value.isValid())
|
||
{
|
||
if (fieldDef.type == "numeric")
|
||
{
|
||
item->setText(QString::number(value.toDouble(), 'f', 3));
|
||
item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||
}
|
||
else
|
||
{
|
||
item->setText(value.toString());
|
||
}
|
||
}
|
||
|
||
// 設為只讀
|
||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||
table->setItem(newRow, i + 1, item);
|
||
}
|
||
|
||
// 自動滾動到底部
|
||
table->scrollToBottom();
|
||
|
||
return true;
|
||
}
|
||
|
||
void TableWidgetFactory::clearSeriesData(QWidget *tableWidget)
|
||
{
|
||
if (!tableData.contains(tableWidget))
|
||
{
|
||
return;
|
||
}
|
||
|
||
const TableWidgetData &data = tableData[tableWidget];
|
||
if (!data.seriesWidget || data.definition.tableType != "series")
|
||
{
|
||
return;
|
||
}
|
||
|
||
data.seriesWidget->setRowCount(0);
|
||
}
|
||
|
||
// =====================================================
|
||
// 驗證與控制
|
||
// =====================================================
|
||
|
||
bool TableWidgetFactory::validateField(QWidget *tableWidget, const QString &fieldId) const
|
||
{
|
||
if (!tableData.contains(tableWidget))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
const TableWidgetData &data = tableData[tableWidget];
|
||
const TableDefinition &tableDef = data.definition;
|
||
|
||
// 查找字段定義
|
||
FieldDefinition fieldDef;
|
||
bool found = false;
|
||
|
||
if (tableDef.tableType == "form")
|
||
{
|
||
for (const FieldDefinition &def : tableDef.fields)
|
||
{
|
||
if (def.id == fieldId)
|
||
{
|
||
fieldDef = def;
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else if (tableDef.tableType == "grid")
|
||
{
|
||
for (const FieldDefinition &def : tableDef.columnHeaders)
|
||
{
|
||
if (def.id == fieldId)
|
||
{
|
||
fieldDef = def;
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!found)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// 獲取當前值
|
||
QVariant value;
|
||
if (tableDef.tableType == "form")
|
||
{
|
||
value = getFieldValue(tableWidget, fieldId);
|
||
}
|
||
else
|
||
{
|
||
return true; // Grid 驗證需要更多上下文
|
||
}
|
||
|
||
// 基本驗證規則
|
||
if (fieldDef.isRequired && (!value.isValid() || value.toString().isEmpty()))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// 類型特定驗證
|
||
if (fieldDef.type == "numeric")
|
||
{
|
||
bool ok;
|
||
double numValue = value.toDouble(&ok);
|
||
if (!ok)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// 範圍驗證
|
||
if (fieldDef.validationRules.contains("min"))
|
||
{
|
||
double minVal = fieldDef.validationRules["min"].toDouble();
|
||
if (numValue < minVal)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
if (fieldDef.validationRules.contains("max"))
|
||
{
|
||
double maxVal = fieldDef.validationRules["max"].toDouble();
|
||
if (numValue > maxVal)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
else if (fieldDef.type == "text")
|
||
{
|
||
QString textValue = value.toString();
|
||
|
||
// 最大長度驗證
|
||
if (fieldDef.validationRules.contains("maxLength"))
|
||
{
|
||
int maxLen = fieldDef.validationRules["maxLength"].toInt();
|
||
if (textValue.length() > maxLen)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 正則驗證
|
||
if (fieldDef.validationRules.contains("pattern"))
|
||
{
|
||
QString pattern = fieldDef.validationRules["pattern"].toString();
|
||
QRegularExpression regex(pattern);
|
||
if (!regex.match(textValue).hasMatch())
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
void TableWidgetFactory::enableManualInput(QWidget *tableWidget, bool enabled)
|
||
{
|
||
if (!tableData.contains(tableWidget))
|
||
{
|
||
return;
|
||
}
|
||
|
||
const TableWidgetData &data = tableData[tableWidget];
|
||
const TableDefinition &tableDef = data.definition;
|
||
|
||
if (tableDef.tableType == "form")
|
||
{
|
||
// 啟用/禁用所有字段組件
|
||
for (auto it = data.fieldWidgets.begin(); it != data.fieldWidgets.end(); ++it)
|
||
{
|
||
QWidget *fieldWidget = it.value();
|
||
if (fieldWidget)
|
||
{
|
||
QString fieldId = it.key();
|
||
bool isReadOnly = false;
|
||
|
||
for (const FieldDefinition &fieldDef : tableDef.fields)
|
||
{
|
||
if (fieldDef.id == fieldId)
|
||
{
|
||
isReadOnly = fieldDef.isReadOnly || fieldDef.type == "calculated";
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 只對非只讀字段操作
|
||
if (!isReadOnly)
|
||
{
|
||
fieldWidget->setEnabled(enabled);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (tableDef.tableType == "grid" && data.gridWidget)
|
||
{
|
||
QTableWidget *table = data.gridWidget;
|
||
|
||
// 啟用/禁用非只讀單元格
|
||
for (int row = 0; row < table->rowCount(); ++row)
|
||
{
|
||
for (int col = 0; col < table->columnCount(); ++col)
|
||
{
|
||
QTableWidgetItem *item = table->item(row, col);
|
||
if (item)
|
||
{
|
||
bool isReadOnly = false;
|
||
if (col < tableDef.columnHeaders.size())
|
||
{
|
||
const FieldDefinition &colDef = tableDef.columnHeaders[col];
|
||
isReadOnly = colDef.isReadOnly || colDef.type == "calculated";
|
||
}
|
||
|
||
if (enabled && !isReadOnly)
|
||
{
|
||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||
}
|
||
else
|
||
{
|
||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (tableDef.tableType == "series" && data.seriesWidget)
|
||
{
|
||
// Series 表格通常是只讀的
|
||
data.seriesWidget->setEnabled(enabled);
|
||
}
|
||
}
|