Files
CalibratorLauncher/widgets/tablewidgetfactory.cpp
2026-01-02 19:20:35 +09:00

1408 lines
37 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 &timestampStr : 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 &timestamp,
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);
}
}