diff --git a/src/translations/Dyt_zh_CN.ts b/src/translations/Dyt_zh_CN.ts index 13a9e92e..d0ff280b 100644 --- a/src/translations/Dyt_zh_CN.ts +++ b/src/translations/Dyt_zh_CN.ts @@ -54,6 +54,11 @@ Chart Name: + + + Chart 1 + + Enter chart name... @@ -65,157 +70,142 @@ - - Wave - - - - - Report - - - - + X Axis Title: - + Enter X axis title... - + Y Axis Title: - + Enter Y axis title... - - Time Parameter: + + Time: - + Axis Range Settings - - X Min: - - - - - X Max: - - - - - Y Min: - - - - - Y Max: - - - - + X Tick Count: - - Curve Name: + + X Min: - - Chart 1 + + Y Max: - + + Y Min: + + + + + X Max: + + + + Y Tick Count: - + Curve Management - + Curves: - + Add Curve - + Remove - + Selected Curve Properties - + + Curve Name: + + + + Enter curve name... - + Curve Color: - + Select Color - + background-color: rgb(255, 0, 0); border: 1px solid black; - + Data Start: - + Data Stop: - + X Value: - + Y Value: - + Add File - + Cancel @@ -405,6 +395,11 @@ AddLightFileDlg + + + Add Light Data File + + File Selection @@ -415,11 +410,6 @@ File Path: - - - Add Light Data File - - Select light data file... @@ -438,6 +428,7 @@ + - @@ -489,7 +480,7 @@ - Add Light + Add Light Row @@ -503,63 +494,78 @@ - - Light Name: + + Light Names: - - Enter light name... + + Enter light names (comma separated)... - - Light Index: + + Light Data: - + + Enter data values (comma separated integers)... + + + + + Edit Data + + + + + Row Index: + + + + Add File - + Cancel - - + + Error - + Unable to get current workspace - + Curve file count has reached the limit (9 files) - + File already exists - + File copy failed - + Invalid file - + Failed to add file @@ -652,6 +658,11 @@ AddSurfaceFileDlg + + + Add Surface File + + File Selection @@ -662,11 +673,6 @@ Select surface data file... - - - Add Surface File - - Browse... @@ -809,11 +815,6 @@ , - - - Has Header Row - - X Grid Size: @@ -825,13 +826,8 @@ - - Select Surface Data File - - - - - Please add at least one surface. + + Has Header Row @@ -839,12 +835,22 @@ Surface Data Files (*.txt *.dat *.csv);;All Files (*.*) + + + Select Surface Data File + + Warning + + + Please add at least one surface. + + Please fill in all axis titles. @@ -906,190 +912,281 @@ - + + File Selection + + + + + File Path: + + + + + Select table data file... + + + + + ... + + + + + File Name: + + + + + + - + + + + + File Size: + + + + Basic Information - Table Name: - - - - Enter table name... + Table Name: + + + + Time Parameter: - - File Selection - - - - - File Path: - - - - - Select table data file... - - - - - ... - - - - - File Name: - - - - - - - - - - - - File Size: - - - - - Encoding: - - - - - UTF-8 - - - - - GBK - - - - - ASCII - - - - - ISO-8859-1 - - - - - Skip Rows: - - - - - File has header row - - - - + Table Headers Configuration - - Headers (comma-separated): + + File has header row + Headers (comma-separated): + + + + e.g., Time, Value1, Value2, Value3... - + Tip: Headers will be auto-detected if file has header row - + Parsing Parameters + Skip Rows: + + + + Auto-detect parameters - - Preview + + Encoding: - + + UTF-8 + + + + + GBK + + + + + ASCII + + + + + ISO-8859-1 + + + + + Curves Configuration + + + + + Add Curve + + + + + Remove Curve + + + + + Curve Properties + + + + + Name: + + + + + Enter curve name... + + + + + Color: + + + + + Select Color + + + + + Data: + + + + + Enter data values (comma-separated)... + + + + + Data values must match the number of headers + + + + Add File - + Cancel - + + + + + + + Warning - - Please enter a table name. - - - - - - - Error - - - - - Failed to create table file entry. - - - - - Unable to get current workspace - - - - - Table file count has reached the limit - - - - - File already exists - - - - - File copy failed + + Please configure table headers first. + Select Curve Color + + + + + Please configure table headers. + + + + + Please add at least one curve. + + + + + Curve %1 name cannot be empty. + + + + + Curve '%1' data cannot be empty. + + + + + Curve '%1' data count (%2) doesn't match headers count (%3). + + + + + Please enter a table name. + + + + + + + Error + + + + + Failed to create table file entry. + + + + + Unable to get current workspace + + + + + Table file count has reached the limit + + + + + File already exists + + + + + File copy failed + + + + Invalid file - + Failed to add file @@ -1409,14 +1506,6 @@ - - FitCurve - - - FitCurveDialog - - - FitCurveChartView @@ -1879,30 +1968,6 @@ altitude: - - - - Curve[%1] - - - - - - Surface[%1] - - - - - - Table[%1] - - - - - - Light[%1] - - Open Workspace @@ -1969,14 +2034,6 @@ Failed to create file entry - - - - - - invalid file - - @@ -2001,6 +2058,38 @@ copy file failed + + + + + + invalid file + + + + + + Curve[%1] + + + + + + Surface[%1] + + + + + + Table[%1] + + + + + + Light[%1] + + QtBoolEdit diff --git a/src/ui/Panel/DataPanelManager.cpp b/src/ui/Panel/DataPanelManager.cpp index c7bf4f7d..b5fa55db 100644 --- a/src/ui/Panel/DataPanelManager.cpp +++ b/src/ui/Panel/DataPanelManager.cpp @@ -102,7 +102,7 @@ void DataPanelManager::OnWorkspaceChanged(WorkSpace* workspace) SetWorkspace(workspace); } -void DataPanelManager::OnFilesChanged(FileEntryType type) +void DataPanelManager::OnFilesChanged(FileEntryType type, std::shared_ptr fileEntry) { // Only respond to supported file types if (!DataPanelFactory::IsTypeSupported(type)) { diff --git a/src/ui/Panel/DataPanelManager.h b/src/ui/Panel/DataPanelManager.h index f4d64cbd..3b585b4b 100644 --- a/src/ui/Panel/DataPanelManager.h +++ b/src/ui/Panel/DataPanelManager.h @@ -61,7 +61,7 @@ public slots: * @brief Handle file changes * @param type File type */ - void OnFilesChanged(FileEntryType type); + void OnFilesChanged(FileEntryType type, std::shared_ptr fileEntry); /** * @brief Handle panel close event diff --git a/src/ui/PropertyBrowser.cpp b/src/ui/PropertyBrowser.cpp index 2c53dbd0..5a39f84b 100644 --- a/src/ui/PropertyBrowser.cpp +++ b/src/ui/PropertyBrowser.cpp @@ -175,7 +175,7 @@ void PropertyBrowser::InitPropertyManager() { doubleSpinBoxFactory); } -void PropertyBrowser::OnWorkspaceFilesChanged(FileEntryType /*type*/) { +void PropertyBrowser::OnWorkspaceFilesChanged(FileEntryType /*type*/, std::shared_ptr /*fileEntry*/) { if (!currentWorkspace_) return; auto it = idToProperty_.find(tr("WorkSpace")); if (it == idToProperty_.end()) return; diff --git a/src/ui/PropertyBrowser.h b/src/ui/PropertyBrowser.h index 9effc06c..9d345df4 100644 --- a/src/ui/PropertyBrowser.h +++ b/src/ui/PropertyBrowser.h @@ -18,7 +18,7 @@ public: void OnWorkSpaceChange(const QVariant& value); void OnEntityChange(const QVariant& value); - void OnWorkspaceFilesChanged(enum class FileEntryType type); + void OnWorkspaceFilesChanged(enum class FileEntryType type, std::shared_ptr fileEntry); void Test(); diff --git a/src/ui/WorkSpace/AddLightFileDlg.cpp b/src/ui/WorkSpace/AddLightFileDlg.cpp index 4acd76b7..ece774a6 100644 --- a/src/ui/WorkSpace/AddLightFileDlg.cpp +++ b/src/ui/WorkSpace/AddLightFileDlg.cpp @@ -51,7 +51,8 @@ void AddLightFileDlg::setupConnections() { // Light properties connections connect(ui->lightNameEdit, &QLineEdit::textChanged, this, &AddLightFileDlg::onLightNameChanged); - connect(ui->lightIndexSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &AddLightFileDlg::onLightIndexChanged); + connect(ui->lightDataEdit, &QLineEdit::textChanged, this, &AddLightFileDlg::onLightDataChanged); + connect(ui->editDataBtn, &QPushButton::clicked, this, &AddLightFileDlg::onEditDataClicked); // Dialog buttons connect(ui->addFileBtn, &QPushButton::clicked, this, &AddLightFileDlg::onSure); @@ -107,17 +108,16 @@ void AddLightFileDlg::onAddLightClicked() { } // Create new light with default properties - FileEntryLight::LightProperty newLight; - newLight.name = generateLightName(); - newLight.index = lights_.size(); + FileEntryLight::LightRowProperty newLight; + newLight.name = QStringList() << generateLightName(); // Add to lights list lights_.append(newLight); // Add to UI list widget - QListWidgetItem* item = new QListWidgetItem(QString("%1 [Index: %2]") - .arg(newLight.name) - .arg(newLight.index)); + QListWidgetItem* item = new QListWidgetItem(QString("%1 [Row: %2]") + .arg(newLight.name.join(", ")) + .arg(lights_.size() - 1)); ui->lightListWidget->addItem(item); // Select the new item @@ -151,14 +151,13 @@ void AddLightFileDlg::onRemoveLightClicked() { updateLightProperties(); } - // Update list display with new indices + // Update list display with new row indices for (int i = 0; i < lights_.size(); ++i) { - lights_[i].index = i; QListWidgetItem* item = ui->lightListWidget->item(i); if (item) { - item->setText(QString("%1 [Index: %2]") - .arg(lights_[i].name) - .arg(lights_[i].index)); + item->setText(QString("%1 [Row: %2]") + .arg(lights_[i].name.join(", ")) + .arg(i)); } } } @@ -189,30 +188,19 @@ void AddLightFileDlg::onLightSelectionChanged() { void AddLightFileDlg::onLightNameChanged() { if (currentLightIndex_ >= 0 && currentLightIndex_ < lights_.size()) { - QString newName = ui->lightNameEdit->text(); - lights_[currentLightIndex_].name = newName; - - // Update list widget item text - QListWidgetItem* item = ui->lightListWidget->item(currentLightIndex_); - if (item) { - item->setText(QString("%1 [Index: %2]") - .arg(newName) - .arg(lights_[currentLightIndex_].index)); + QString namesText = ui->lightNameEdit->text(); + QStringList names = namesText.split(",", Qt::SkipEmptyParts); + for (int i = 0; i < names.size(); ++i) { + names[i] = names[i].trimmed(); } - } -} - -void AddLightFileDlg::onLightIndexChanged() { - if (currentLightIndex_ >= 0 && currentLightIndex_ < lights_.size()) { - int newIndex = ui->lightIndexSpinBox->value(); - lights_[currentLightIndex_].index = newIndex; + lights_[currentLightIndex_].name = names; // Update list widget item text QListWidgetItem* item = ui->lightListWidget->item(currentLightIndex_); if (item) { - item->setText(QString("%1 [Index: %2]") - .arg(lights_[currentLightIndex_].name) - .arg(newIndex)); + item->setText(QString("%1 [Row: %2]") + .arg(names.join(", ")) + .arg(currentLightIndex_)); } } } @@ -233,31 +221,63 @@ void AddLightFileDlg::updateCloseColorPreview(const QColor& color) { ui->closeColorPreview->setStyleSheet(styleSheet); } -void AddLightFileDlg::addLightToList(const FileEntryLight::LightProperty& light) { - QListWidgetItem* item = new QListWidgetItem(QString("%1 [Index: %2]") - .arg(light.name) - .arg(light.index)); +void AddLightFileDlg::addLightToList(const FileEntryLight::LightRowProperty& light) { + QListWidgetItem* item = new QListWidgetItem(QString("%1 [Row: %2]") + .arg(light.name.join(", ")) + .arg(lights_.size())); ui->lightListWidget->addItem(item); } void AddLightFileDlg::updateLightProperties() { if (currentLightIndex_ >= 0 && currentLightIndex_ < lights_.size()) { - const FileEntryLight::LightProperty& light = lights_[currentLightIndex_]; - ui->lightNameEdit->setText(light.name); - ui->lightIndexSpinBox->setValue(light.index); + const FileEntryLight::LightRowProperty& light = lights_[currentLightIndex_]; + ui->lightNameEdit->setText(light.name.join(", ")); + + // Update data edit + QStringList dataStrings; + for (int dataValue : light.data) { + dataStrings.append(QString::number(dataValue)); + } + ui->lightDataEdit->setText(dataStrings.join(", ")); + + // Update row index display + ui->rowIndexValue->setText(QString::number(currentLightIndex_)); + + // Update data display + updateDataDisplay(); } } void AddLightFileDlg::saveLightProperties() { if (currentLightIndex_ >= 0 && currentLightIndex_ < lights_.size()) { - lights_[currentLightIndex_].name = ui->lightNameEdit->text(); - lights_[currentLightIndex_].index = ui->lightIndexSpinBox->value(); + // Save names + QString namesText = ui->lightNameEdit->text(); + QStringList names = namesText.split(",", Qt::SkipEmptyParts); + for (int i = 0; i < names.size(); ++i) { + names[i] = names[i].trimmed(); + } + lights_[currentLightIndex_].name = names; + + // Save data + QString dataText = ui->lightDataEdit->text(); + QStringList dataStrings = dataText.split(",", Qt::SkipEmptyParts); + QList dataValues; + + for (const QString& str : dataStrings) { + bool ok; + int dataValue = str.trimmed().toInt(&ok); + if (ok) { + dataValues.append(dataValue); + } + } + lights_[currentLightIndex_].data = dataValues; } } void AddLightFileDlg::clearLightProperties() { ui->lightNameEdit->clear(); - ui->lightIndexSpinBox->setValue(0); + ui->lightDataEdit->clear(); + ui->rowIndexValue->setText("-"); } void AddLightFileDlg::enableLightProperties(bool enabled) { @@ -265,7 +285,44 @@ void AddLightFileDlg::enableLightProperties(bool enabled) { } QString AddLightFileDlg::generateLightName() { - return QString("Light %1").arg(lights_.size() + 1); + return QString("Light_%1").arg(lights_.size() + 1); +} + +void AddLightFileDlg::updateDataDisplay() { + if (currentLightIndex_ >= 0 && currentLightIndex_ < lights_.size()) { + const FileEntryLight::LightRowProperty& light = lights_[currentLightIndex_]; + QStringList dataStrings; + for (int dataValue : light.data) { + dataStrings.append(QString::number(dataValue)); + } + ui->lightDataEdit->setText(dataStrings.join(", ")); + } else { + ui->lightDataEdit->clear(); + } +} + +void AddLightFileDlg::onLightDataChanged() { + // Parse comma-separated data values and store them + if (currentLightIndex_ >= 0 && currentLightIndex_ < lights_.size()) { + QString dataText = ui->lightDataEdit->text(); + QStringList dataStrings = dataText.split(',', Qt::SkipEmptyParts); + + QList dataValues; + for (const QString& str : dataStrings) { + bool ok; + int dataValue = str.trimmed().toInt(&ok); + if (ok) { + dataValues.append(dataValue); + } + } + lights_[currentLightIndex_].data = dataValues; + } +} + +void AddLightFileDlg::onEditDataClicked() { + // Future implementation: could open a detailed data editor dialog + // For now, just focus on the data edit field + ui->lightDataEdit->setFocus(); } bool AddLightFileDlg::validateSpecificParams() { diff --git a/src/ui/WorkSpace/AddLightFileDlg.h b/src/ui/WorkSpace/AddLightFileDlg.h index d0cd3d44..82e31d55 100644 --- a/src/ui/WorkSpace/AddLightFileDlg.h +++ b/src/ui/WorkSpace/AddLightFileDlg.h @@ -8,7 +8,6 @@ class QLineEdit; class QCheckBox; -class QSpinBox; class QDoubleSpinBox; class QTextEdit; class QToolButton; @@ -42,19 +41,23 @@ private slots: void onLightListWidgetItemClicked(QListWidgetItem* item); void onLightSelectionChanged(); void onLightNameChanged(); - void onLightIndexChanged(); void onSure(); private: void setupConnections(); void updateOpenColorPreview(const QColor& color); void updateCloseColorPreview(const QColor& color); - void addLightToList(const FileEntryLight::LightProperty& light); + void addLightToList(const FileEntryLight::LightRowProperty& light); void updateLightProperties(); void saveLightProperties(); void clearLightProperties(); void enableLightProperties(bool enabled); QString generateLightName(); + + // New methods for data handling + void updateDataDisplay(); + void onLightDataChanged(); + void onEditDataClicked(); Ui::AddLightFileDlg* ui; int currentLightIndex_; @@ -62,5 +65,5 @@ private: QColor closeColor_; FileEntryLight::ColorProperties colorProperties_; - FileEntryLight::LightProperties lights_; + FileEntryLight::LightRowProperties lights_; }; \ No newline at end of file diff --git a/src/ui/WorkSpace/AddLightFileDlg.ui b/src/ui/WorkSpace/AddLightFileDlg.ui index c302c410..26d8e86c 100644 --- a/src/ui/WorkSpace/AddLightFileDlg.ui +++ b/src/ui/WorkSpace/AddLightFileDlg.ui @@ -7,7 +7,7 @@ 0 0 600 - 487 + 516 @@ -222,12 +222,12 @@ - 80 + 180 16777215 - Add Light + Add Light Row @@ -267,40 +267,69 @@ Selected Light Properties - - - - - Light Name: - - - - - - - Enter light name... - - - - - - - Light Index: - - - - - - - 0 - - - 999999 - - - 0 - - + + + + + + + Light Names: + + + + + + + Enter light names (comma separated)... + + + + + + + Light Data: + + + + + + + + + Enter data values (comma separated integers)... + + + + + + + + 80 + 16777215 + + + + Edit Data + + + + + + + + + Row Index: + + + + + + + - + + + + diff --git a/src/ui/WorkSpace/AddTableFileDlg.cpp b/src/ui/WorkSpace/AddTableFileDlg.cpp index 734718be..5ed650de 100644 --- a/src/ui/WorkSpace/AddTableFileDlg.cpp +++ b/src/ui/WorkSpace/AddTableFileDlg.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "app/Application.h" #include "common/SpdLogger.h" @@ -14,7 +15,8 @@ AddTableFileDlg::AddTableFileDlg(QWidget* parent) : BaseAddFileDlg(FileEntryType::Table, parent) - , ui(new Ui::AddTableFileDlg) { + , ui(new Ui::AddTableFileDlg) + , m_currentCurveIndex(-1) { SetupUI(ui); SetTitle(getDialogTitle()); @@ -27,28 +29,26 @@ AddTableFileDlg::~AddTableFileDlg() { void AddTableFileDlg::setupConnections() { // Connect file selection - connect(ui->selectFileBtn, &QToolButton::clicked, this, &AddTableFileDlg::onSelectFileClicked); + connect(ui->selectFileBtn, &QToolButton::clicked, this, &AddTableFileDlg::OnSelectFile); connect(ui->filePathEdit, &QLineEdit::textChanged, this, &AddTableFileDlg::onFilePathChanged); connect(ui->hasHeaderCheckBox, &QCheckBox::toggled, this, &AddTableFileDlg::onHeaderToggled); + connect(ui->headersEdit, &QLineEdit::textChanged, this, &AddTableFileDlg::onHeadersChanged); + + // Connect curve management + connect(ui->addCurveBtn, &QPushButton::clicked, this, &AddTableFileDlg::onAddCurveClicked); + connect(ui->removeCurveBtn, &QPushButton::clicked, this, &AddTableFileDlg::onRemoveCurveClicked); + connect(ui->curvesListWidget, &QListWidget::currentRowChanged, this, &AddTableFileDlg::onCurveSelectionChanged); + + // Connect curve properties + connect(ui->curveNameEdit, &QLineEdit::textChanged, this, &AddTableFileDlg::onCurveNameChanged); + connect(ui->colorBtn, &QPushButton::clicked, this, &AddTableFileDlg::onCurveColorClicked); + connect(ui->curveDataEdit, &QLineEdit::textChanged, this, &AddTableFileDlg::onCurveDataChanged); + connect(ui->addBtn, &QPushButton::clicked, this, [this]() { OnSure(); }); connect(ui->cancelBtn, &QPushButton::clicked, this, &QDialog::reject); } -void AddTableFileDlg::onSelectFileClicked() { - const QString workspacePath = Application::GetWorkSpacePath(); - QString filePath = QFileDialog::getOpenFileName( - this, - QStringLiteral("Select Table File"), - workspacePath, - getFileFilter() - ); - - if (!filePath.isEmpty()) { - ui->filePathEdit->setText(filePath); - } -} - void AddTableFileDlg::onFilePathChanged(const QString& filePath) { updateFileInfo(filePath); } @@ -56,6 +56,7 @@ void AddTableFileDlg::onFilePathChanged(const QString& filePath) { void AddTableFileDlg::updateFileInfo(const QString& filePath) { QFileInfo fileInfo(filePath); ui->fileNameValue->setText(fileInfo.fileName()); + ui->filePathEdit->setText(filePath); qint64 size = fileInfo.size(); QString sizeText; @@ -77,6 +78,203 @@ void AddTableFileDlg::onHeaderToggled(bool hasHeader) { // Handle header toggle if needed } +void AddTableFileDlg::onHeadersChanged() { + // Update data validation when headers change + if (m_currentCurveIndex >= 0 && m_currentCurveIndex < m_curves.size()) { + QString data = ui->curveDataEdit->text(); + QStringList headers = getHeadersList(); + + if (!data.isEmpty() && !validateCurveData(data, headers.size())) { + ui->dataValidationLabel->setText(QString("Data values must match the number of headers (%1)").arg(headers.size())); + ui->dataValidationLabel->setStyleSheet("color: red; font-size: 11px;"); + } else { + ui->dataValidationLabel->setText(QString("Data values must match the number of headers (%1)").arg(headers.size())); + ui->dataValidationLabel->setStyleSheet("color: gray; font-size: 11px;"); + } + } +} + +void AddTableFileDlg::onAddCurveClicked() { + QStringList headers = getHeadersList(); + if (headers.isEmpty()) { + QMessageBox::warning(this, tr("Warning"), tr("Please configure table headers first.")); + return; + } + + // Save current curve properties if any + saveCurveProperties(); + + // Create new curve with default values + CurveData newCurve; + newCurve.name = QString("Curve %1").arg(m_curves.size() + 1); + newCurve.color = QColor::fromHsv((m_curves.size() * 60) % 360, 200, 200); // Different colors + + m_curves.append(newCurve); + updateCurvesList(); + + // Select the new curve + ui->curvesListWidget->setCurrentRow(m_curves.size() - 1); +} + +void AddTableFileDlg::onRemoveCurveClicked() { + int currentRow = ui->curvesListWidget->currentRow(); + if (currentRow >= 0 && currentRow < m_curves.size()) { + m_curves.removeAt(currentRow); + updateCurvesList(); + + if (m_curves.isEmpty()) { + m_currentCurveIndex = -1; + ui->curvePropertiesGroupBox->setEnabled(false); + ui->removeCurveBtn->setEnabled(false); + } else { + // Select previous curve or first curve + int newIndex = qMin(currentRow, m_curves.size() - 1); + ui->curvesListWidget->setCurrentRow(newIndex); + } + } +} + +void AddTableFileDlg::onCurveSelectionChanged() { + // Save current curve properties before switching + saveCurveProperties(); + + int currentRow = ui->curvesListWidget->currentRow(); + m_currentCurveIndex = currentRow; + + if (currentRow >= 0 && currentRow < m_curves.size()) { + ui->curvePropertiesGroupBox->setEnabled(true); + ui->removeCurveBtn->setEnabled(true); + updateCurveProperties(); + } else { + ui->curvePropertiesGroupBox->setEnabled(false); + ui->removeCurveBtn->setEnabled(false); + } +} + +void AddTableFileDlg::onCurveNameChanged() { + if (m_currentCurveIndex >= 0 && m_currentCurveIndex < m_curves.size()) { + QString newName = ui->curveNameEdit->text(); + m_curves[m_currentCurveIndex].name = newName; + + // Update list item text + QListWidgetItem* item = ui->curvesListWidget->item(m_currentCurveIndex); + if (item) { + item->setText(newName); + } + } +} + +void AddTableFileDlg::onCurveColorClicked() { + if (m_currentCurveIndex >= 0 && m_currentCurveIndex < m_curves.size()) { + QColor currentColor = m_curves[m_currentCurveIndex].color; + QColor newColor = QColorDialog::getColor(currentColor, this, tr("Select Curve Color")); + + if (newColor.isValid()) { + m_curves[m_currentCurveIndex].color = newColor; + + // Update color preview + QString colorStyle = QString("background-color: rgb(%1, %2, %3); border: 1px solid gray;") + .arg(newColor.red()).arg(newColor.green()).arg(newColor.blue()); + ui->colorPreviewLabel->setStyleSheet(colorStyle); + } + } +} + +void AddTableFileDlg::onCurveDataChanged() { + if (m_currentCurveIndex >= 0 && m_currentCurveIndex < m_curves.size()) { + QString dataText = ui->curveDataEdit->text(); + QStringList dataList = dataText.split(',', Qt::SkipEmptyParts); + + // Trim whitespace from each data item + for (QString& data : dataList) { + data = data.trimmed(); + } + + m_curves[m_currentCurveIndex].data = dataList; + + // Validate data count + QStringList headers = getHeadersList(); + if (!dataText.isEmpty() && !validateCurveData(dataText, headers.size())) { + ui->dataValidationLabel->setText(QString("Data count (%1) doesn't match headers count (%2)") + .arg(dataList.size()).arg(headers.size())); + ui->dataValidationLabel->setStyleSheet("color: red; font-size: 11px;"); + } else { + ui->dataValidationLabel->setText(QString("Data values must match the number of headers (%1)").arg(headers.size())); + ui->dataValidationLabel->setStyleSheet("color: gray; font-size: 11px;"); + } + } +} + +void AddTableFileDlg::updateCurvesList() { + ui->curvesListWidget->clear(); + + for (const CurveData& curve : m_curves) { + ui->curvesListWidget->addItem(curve.name); + } +} + +void AddTableFileDlg::updateCurveProperties() { + if (m_currentCurveIndex >= 0 && m_currentCurveIndex < m_curves.size()) { + const CurveData& curve = m_curves[m_currentCurveIndex]; + + // Block signals to prevent recursive updates + ui->curveNameEdit->blockSignals(true); + ui->curveDataEdit->blockSignals(true); + + ui->curveNameEdit->setText(curve.name); + ui->curveDataEdit->setText(curve.data.join(", ")); + + // Update color preview + QString colorStyle = QString("background-color: rgb(%1, %2, %3); border: 1px solid gray;") + .arg(curve.color.red()).arg(curve.color.green()).arg(curve.color.blue()); + ui->colorPreviewLabel->setStyleSheet(colorStyle); + + ui->curveNameEdit->blockSignals(false); + ui->curveDataEdit->blockSignals(false); + + // Update validation + QStringList headers = getHeadersList(); + if (!curve.data.isEmpty() && !validateCurveData(curve.data.join(", "), headers.size())) { + ui->dataValidationLabel->setText(QString("Data count (%1) doesn't match headers count (%2)") + .arg(curve.data.size()).arg(headers.size())); + ui->dataValidationLabel->setStyleSheet("color: red; font-size: 11px;"); + } else { + ui->dataValidationLabel->setText(QString("Data values must match the number of headers (%1)").arg(headers.size())); + ui->dataValidationLabel->setStyleSheet("color: gray; font-size: 11px;"); + } + } +} + +void AddTableFileDlg::saveCurveProperties() { + if (m_currentCurveIndex >= 0 && m_currentCurveIndex < m_curves.size()) { + // Properties are already saved in real-time through signal handlers + // This method is kept for consistency and future extensions + } +} + +bool AddTableFileDlg::validateCurveData(const QString& data, int expectedCount) { + if (data.isEmpty()) { + return true; // Empty data is allowed + } + + QStringList dataList = data.split(',', Qt::SkipEmptyParts); + return dataList.size() == expectedCount; +} + +QStringList AddTableFileDlg::getHeadersList() const { + QString headersText = ui->headersEdit->text().trimmed(); + if (headersText.isEmpty()) { + return QStringList(); + } + + QStringList headers = headersText.split(',', Qt::SkipEmptyParts); + for (QString& header : headers) { + header = header.trimmed(); + } + + return headers; +} + bool AddTableFileDlg::validateSpecificParams() { if (ui->filePathEdit->text().isEmpty()) { QMessageBox::warning(this, QStringLiteral("Warning"), @@ -91,6 +289,45 @@ bool AddTableFileDlg::validateSpecificParams() { return false; } + // Validate headers + QStringList headers = getHeadersList(); + if (headers.isEmpty()) { + QMessageBox::warning(this, tr("Warning"), + tr("Please configure table headers.")); + return false; + } + + // Validate curves + if (m_curves.isEmpty()) { + QMessageBox::warning(this, tr("Warning"), + tr("Please add at least one curve.")); + return false; + } + + // Validate each curve's data + for (int i = 0; i < m_curves.size(); ++i) { + const CurveData& curve = m_curves[i]; + + if (curve.name.isEmpty()) { + QMessageBox::warning(this, tr("Warning"), + tr("Curve %1 name cannot be empty.").arg(i + 1)); + return false; + } + + if (curve.data.isEmpty()) { + QMessageBox::warning(this, tr("Warning"), + tr("Curve '%1' data cannot be empty.").arg(curve.name)); + return false; + } + + if (curve.data.size() != headers.size()) { + QMessageBox::warning(this, tr("Warning"), + tr("Curve '%1' data count (%2) doesn't match headers count (%3).") + .arg(curve.name).arg(curve.data.size()).arg(headers.size())); + return false; + } + } + return true; } @@ -108,11 +345,6 @@ QString AddTableFileDlg::getSelectedFilePath() const { void AddTableFileDlg::accept() { - // Validate parameters - if (!validateSpecificParams()) { - return; - } - // Validate table-specific parameters if (ui->tableNameEdit->text().isEmpty()) { QMessageBox::warning(this, tr("Warning"), tr("Please enter a table name.")); diff --git a/src/ui/WorkSpace/AddTableFileDlg.h b/src/ui/WorkSpace/AddTableFileDlg.h index 8bc6b34b..9e32356d 100644 --- a/src/ui/WorkSpace/AddTableFileDlg.h +++ b/src/ui/WorkSpace/AddTableFileDlg.h @@ -1,11 +1,23 @@ #pragma once +#include + namespace Ui { class AddTableFileDlg; } #include "BaseAddFileDlg.h" +struct CurveData { + QString name; + QColor color; + QStringList data; + + CurveData() : color(Qt::blue) {} + CurveData(const QString& n, const QColor& c, const QStringList& d) + : name(n), color(c), data(d) {} +}; + class AddTableFileDlg : public BaseAddFileDlg { Q_OBJECT @@ -29,10 +41,18 @@ protected: bool validateSpecificParams() override; private slots: - void onSelectFileClicked(); void onFilePathChanged(const QString& filePath); void onDelimiterChanged(); void onHeaderToggled(bool hasHeader); + void onHeadersChanged(); + + // Curve management slots + void onAddCurveClicked(); + void onRemoveCurveClicked(); + void onCurveSelectionChanged(); + void onCurveNameChanged(); + void onCurveColorClicked(); + void onCurveDataChanged(); public slots: void accept() override; @@ -40,6 +60,13 @@ public slots: private: void setupConnections(); void updateFileInfo(const QString& filePath); + void updateCurvesList(); + void updateCurveProperties(); + void saveCurveProperties(); + bool validateCurveData(const QString& data, int expectedCount); + QStringList getHeadersList() const; Ui::AddTableFileDlg* ui; + QList m_curves; + int m_currentCurveIndex; }; \ No newline at end of file diff --git a/src/ui/WorkSpace/AddTableFileDlg.ui b/src/ui/WorkSpace/AddTableFileDlg.ui index 55192b32..e1db65b6 100644 --- a/src/ui/WorkSpace/AddTableFileDlg.ui +++ b/src/ui/WorkSpace/AddTableFileDlg.ui @@ -7,272 +7,411 @@ 0 0 676 - 683 + 600 Add Table Data File - + - - - File Selection + + + Qt::Horizontal - - - - - File Path: - - - - - - - true - - - Select table data file... - - - - - - - ... - - - - - - - File Name: - - - - - - - - - - - - - - - File Size: - - - - - - - - - - - - - - - - - - Basic Information - - - - - - 3 - - - 0.000000000000000 - - - 999999.000000000000000 - - - 1.000000000000000 - - - - - - - Enter table name... - - - - - - - Table Name: - - - - - - - Time Parameter: - - - - - - - - - - Table Headers Configuration - - - - - - File has header row - - - true - - - - - - - Headers (comma-separated): - - - - - - - e.g., Time, Value1, Value2, Value3... - - - - - - - color: gray; font-size: 11px; - - - Tip: Headers will be auto-detected if file has header row - - - - - - - - - - Parsing Parameters - - - - - - Skip Rows: - - - - - - - Auto-detect parameters - - - true - - - - - - - Encoding: - - - - - - - - UTF-8 + + + + + + File Selection - - - - GBK + + + + + File Path: + + + + + + + true + + + Select table data file... + + + + + + + ... + + + + + + + File Name: + + + + + + + - + + + + + + + File Size: + + + + + + + - + + + + + + + + + + Basic Information - - - - ASCII + + + + + 3 + + + 0.000000000000000 + + + 999999.000000000000000 + + + 1.000000000000000 + + + + + + + Enter table name... + + + + + + + Table Name: + + + + + + + Time Parameter: + + + + + + + + + + Table Headers Configuration - - - - ISO-8859-1 + + + + + File has header row + + + true + + + + + + + Headers (comma-separated): + + + + + + + e.g., Time, Value1, Value2, Value3... + + + + + + + color: gray; font-size: 11px; + + + Tip: Headers will be auto-detected if file has header row + + + + + + + + + + Parsing Parameters - - - - - - - 0 - - - 1000 - - - 0 - - - - - - - - - - Preview - - - - - - - 16777215 - 120 - - - - true - - - QAbstractItemView::NoSelection - - - 5 - - - 5 - - - - - - - - - - - - - - + + + + + Skip Rows: + + + + + + + Auto-detect parameters + + + true + + + + + + + Encoding: + + + + + + + + UTF-8 + + + + + GBK + + + + + ASCII + + + + + ISO-8859-1 + + + + + + + + 0 + + + 1000 + + + 0 + + + + + + + + + + + + + + Curves Configuration + + + + + + + + Add Curve + + + + + + + false + + + Remove Curve + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 16777215 + 150 + + + + true + + + + + + + false + + + Curve Properties + + + + + + Name: + + + + + + + Enter curve name... + + + + + + + Color: + + + + + + + + + + 100 + 16777215 + + + + Select Color + + + + + + + + 30 + 20 + + + + + 30 + 20 + + + + background-color: rgb(0, 0, 255); border: 1px solid gray; + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Data: + + + + + + + Enter data values (comma-separated)... + + + + + + + color: gray; font-size: 11px; + + + Data values must match the number of headers + + + true + + + + + + + + + + + diff --git a/src/ui/WorkSpace/BaseAddFileDlg.cpp b/src/ui/WorkSpace/BaseAddFileDlg.cpp index 1036464d..687a2c66 100644 --- a/src/ui/WorkSpace/BaseAddFileDlg.cpp +++ b/src/ui/WorkSpace/BaseAddFileDlg.cpp @@ -38,7 +38,7 @@ void BaseAddFileDlg::OnSelectFile() { QString filePath = QFileDialog::getOpenFileName( this, getDialogTitle(), - QString(), + workspacePath, getFileFilter() ); diff --git a/src/workspace/FileEntry.cpp b/src/workspace/FileEntry.cpp index 850599e9..3f7229a9 100644 --- a/src/workspace/FileEntry.cpp +++ b/src/workspace/FileEntry.cpp @@ -467,6 +467,109 @@ bool FileEntryCurve::SaveFiles(tinyxml2::XMLElement* scene, tinyxml2::XMLDocumen return true; } +// FileEntryLight SaveFiles implementation +bool FileEntryLight::SaveFiles(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc) { + if (!scene || !doc) { + LOG_ERROR("Invalid XML parameters"); + return false; + } + + // 创建 元素 + tinyxml2::XMLElement* chartElement = doc->NewElement("chart"); + scene->InsertEndChild(chartElement); + + // 设置chart属性 + chartElement->SetAttribute("name", name_.toUtf8().constData()); + chartElement->SetAttribute("path", fileName_.toUtf8().constData()); + + // 保存颜色属性 + chartElement->SetAttribute("openColor", QColorToString(colorProperties_.openColor).toUtf8().constData()); + chartElement->SetAttribute("closeColor", QColorToString(colorProperties_.closeColor).toUtf8().constData()); + + // 为每个LightRowProperty创建元素 + for (const auto& lightRow : lightProperties_) { + tinyxml2::XMLElement* lightElement = doc->NewElement("curve"); + chartElement->InsertEndChild(lightElement); + + // 保存name列表(以逗号分隔) + QString nameStr = lightRow.name.join(","); + lightElement->SetAttribute("name", nameStr.toUtf8().constData()); + + // 保存data列表(以逗号分隔) + QStringList dataStrList; + for (int value : lightRow.data) { + dataStrList.append(QString::number(value)); + } + QString dataStr = dataStrList.join(","); + lightElement->SetAttribute("data", dataStr.toUtf8().constData()); + } + + return true; +} + +// FileEntryLight ParseFiles implementation +bool FileEntryLight::ParseFiles(const tinyxml2::XMLElement* chartElement) { + if (!chartElement) { + LOG_ERROR("Invalid XML element"); + return false; + } + + // 解析chart属性 + const char* nameAttr = chartElement->Attribute("name"); + const char* pathAttr = chartElement->Attribute("path"); + if (nameAttr) name_ = QString::fromUtf8(nameAttr); + if (pathAttr) { + QString fullPath = QString::fromUtf8(pathAttr); + QFileInfo fileInfo(fullPath); + fileName_ = fileInfo.fileName(); + path_ = fileInfo.absolutePath(); + } + + // 解析颜色属性 + const char* openColorAttr = chartElement->Attribute("openColor"); + const char* closeColorAttr = chartElement->Attribute("closeColor"); + if (openColorAttr) { + colorProperties_.openColor = StringToQColor(QString::fromUtf8(openColorAttr)); + } + if (closeColorAttr) { + colorProperties_.closeColor = StringToQColor(QString::fromUtf8(closeColorAttr)); + } + + // 解析所有元素 + lightProperties_.clear(); + for (const tinyxml2::XMLElement* lightElement = chartElement->FirstChildElement("curve"); + lightElement != nullptr; + lightElement = lightElement->NextSiblingElement("curve")) { + + LightRowProperty lightRow; + + // 解析name属性(逗号分隔的字符串列表) + const char* nameAttr = lightElement->Attribute("name"); + if (nameAttr) { + QString nameStr = QString::fromUtf8(nameAttr); + lightRow.name = nameStr.split(",", Qt::SkipEmptyParts); + } + + // 解析data属性(逗号分隔的整数列表) + const char* dataAttr = lightElement->Attribute("data"); + if (dataAttr) { + QString dataStr = QString::fromUtf8(dataAttr); + QStringList dataStrList = dataStr.split(",", Qt::SkipEmptyParts); + for (const QString& str : dataStrList) { + bool ok; + int value = str.trimmed().toInt(&ok); + if (ok) { + lightRow.data.append(value); + } + } + } + + lightProperties_.append(lightRow); + } + + return true; +} + // FileEntryCurve ParseFiles implementation bool FileEntryCurve::ParseFiles(const tinyxml2::XMLElement* chartElement) { if (!chartElement) { diff --git a/src/workspace/FileEntry.h b/src/workspace/FileEntry.h index 4125b279..ea1bc4ec 100644 --- a/src/workspace/FileEntry.h +++ b/src/workspace/FileEntry.h @@ -264,12 +264,13 @@ public: QColor closeColor; }; - struct LightProperty { - QString name; - int index; + struct LightRowProperty { + QStringList name; + QList data; }; - using LightProperties = QList; + + using LightRowProperties = QList; public: FileEntryLight() { type_ = FileEntryType::Light; } @@ -277,14 +278,18 @@ public: void SetColorProperties(const ColorProperties& props) { colorProperties_ = props; } const ColorProperties& GetColorProperties() const { return colorProperties_; } - void AddLightProperty(const LightProperty& prop) { lightProperties_.append(prop); } + void AddLightProperty(const LightRowProperty& prop) { lightProperties_.append(prop); } void RemoveLightProperty(int index) { lightProperties_.removeAt(index); } - const LightProperties& GetLightProperties() const { return lightProperties_; } + const LightRowProperties& GetLightProperties() const { return lightProperties_; } FileEntryLight* AsLight() override { return this; } + // XML处理方法 + bool SaveFiles(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc) override; + bool ParseFiles(const tinyxml2::XMLElement* element) override; + private: ColorProperties colorProperties_; - LightProperties lightProperties_; + LightRowProperties lightProperties_; }; diff --git a/src/workspace/WorkSpace.cpp b/src/workspace/WorkSpace.cpp index f0d32d36..ed65989c 100644 --- a/src/workspace/WorkSpace.cpp +++ b/src/workspace/WorkSpace.cpp @@ -165,8 +165,7 @@ WorkSpace::FileEntryResult WorkSpace::SetFileEntry(std::shared_ptr fi } // Update file entry with workspace-relative path - fileName = fileInfo.fileName(); - fileEntry->SetFileNanme(fileInfo.fileName()); + fileEntry->SetPath(targetPath); } else { QString filePath = QString("%1/%2").arg(GetDir(), fileEntry->GetFileName()); @@ -178,7 +177,7 @@ WorkSpace::FileEntryResult WorkSpace::SetFileEntry(std::shared_ptr fi vec.push_back(fileEntry); ++filesSeq_; - emit FilesChanged(type); + emit FilesChanged(type, fileEntry); LOG_INFO("Successfully added file entry: {} (type: {})", fileName.toUtf8().constData(), FileEntryTypeToString(type)); @@ -231,7 +230,7 @@ bool WorkSpace::SetFileEntryCount(FileEntryType type, int count) { vec.resize(count); } ++filesSeq_; - emit FilesChanged(type); + emit FilesChanged(type, nullptr); // No specific fileEntry for batch operations return true; } diff --git a/src/workspace/WorkSpace.h b/src/workspace/WorkSpace.h index f514c669..0ac701c9 100644 --- a/src/workspace/WorkSpace.h +++ b/src/workspace/WorkSpace.h @@ -149,7 +149,7 @@ Q_SIGNALS: void EntityRemoved(class Entity* entity); void TimestepChanged(class Timestep* timestep); void LampStatusChanged(class LampStatus* lampStatus); - void FilesChanged(FileEntryType type); + void FilesChanged(FileEntryType type, std::shared_ptr fileEntry); protected: const QString& GetSimMatlabName() const {