From 0ad5e599558ebc6d48b30825796ab7ff7f4bbc8e Mon Sep 17 00:00:00 2001 From: pimin <362371171@qq.com> Date: Fri, 31 Oct 2025 23:10:35 +0800 Subject: [PATCH] modified polarPanel --- src/Dyt.qrc | 1 + src/res/default/polar.png | Bin 0 -> 65908 bytes src/ui/DialogTitleBar.ui | 8 +- src/ui/Menu/FileManagerMenu.cpp | 284 ++++--- src/ui/Menu/FileManagerMenu.h | 1 + src/ui/Menu/FileManagerMenu.ui | 10 + src/ui/Panel/DataPanel.cpp | 2 + src/ui/Panel/DataPanelFactory.cpp | 15 +- src/ui/Panel/DataPanelManager.cpp | 1 + src/ui/Panel/PolarPanel.cpp | 344 ++++++++ src/ui/Panel/PolarPanel.h | 87 ++ src/ui/PropertyBrowser/qtpropertymanager.cpp | 25 +- src/ui/WorkSpace/AddCurveFileDlg.ui | 256 +++++- src/ui/WorkSpace/AddFileDialogFactory.cpp | 3 + src/ui/WorkSpace/AddLightFileDlg.cpp | 12 + src/ui/WorkSpace/AddLightFileDlg.ui | 114 ++- src/ui/WorkSpace/AddPolarDlg.ui | 810 +++++++++++++++++++ src/ui/WorkSpace/AddPolarFileDlg.cpp | 225 ++++++ src/ui/WorkSpace/AddPolarFileDlg.h | 42 + src/ui/WorkSpace/AddSurfaceFileDlg.cpp | 63 +- src/ui/WorkSpace/AddSurfaceFileDlg.ui | 563 ++++++++++--- src/ui/WorkSpace/AddTableFileDlg.cpp | 73 +- src/ui/WorkSpace/AddTableFileDlg.ui | 126 ++- src/ui/WorkSpace/WorkSpaceDlg.ui | 68 +- src/workspace/FileEntry.cpp | 155 ++++ src/workspace/FileEntry.h | 58 +- src/workspace/WorkSpace.cpp | 3 + 27 files changed, 3036 insertions(+), 313 deletions(-) create mode 100644 src/res/default/polar.png create mode 100644 src/ui/Panel/PolarPanel.cpp create mode 100644 src/ui/Panel/PolarPanel.h create mode 100644 src/ui/WorkSpace/AddPolarDlg.ui create mode 100644 src/ui/WorkSpace/AddPolarFileDlg.cpp create mode 100644 src/ui/WorkSpace/AddPolarFileDlg.h diff --git a/src/Dyt.qrc b/src/Dyt.qrc index bea57361..eaa97f2e 100644 --- a/src/Dyt.qrc +++ b/src/Dyt.qrc @@ -31,6 +31,7 @@ res/default/menu_window_manager.png res/default/menu_window_setting.png res/default/Command.png + res/default/polar.png diff --git a/src/res/default/polar.png b/src/res/default/polar.png new file mode 100644 index 0000000000000000000000000000000000000000..17a6c55ce4ac64a3a136206161a395d2bf6e7eb4 GIT binary patch literal 65908 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

=z7L5qC`} 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 Y7&0 - 0 + 20 - 0 + 5 - 0 + 5 - 0 + 5 diff --git a/src/ui/Menu/FileManagerMenu.cpp b/src/ui/Menu/FileManagerMenu.cpp index f5c94408..2b7d4e8c 100644 --- a/src/ui/Menu/FileManagerMenu.cpp +++ b/src/ui/Menu/FileManagerMenu.cpp @@ -41,6 +41,7 @@ void FileManagerMenu::InitConnect() { connect(ui->menu_surface_file, &QToolButton::clicked, this, &FileManagerMenu::AddSurfaceFile); connect(ui->menu_table_file, &QToolButton::clicked, this, &FileManagerMenu::AddTableFile); connect(ui->menu_light_file, &QToolButton::clicked, this, &FileManagerMenu::AddLightFile); + connect(ui->menu_polar_file, &QToolButton::clicked, this, &FileManagerMenu::AddPolarFile); } void FileManagerMenu::NewWorkSpace() { @@ -94,38 +95,41 @@ void FileManagerMenu::AddWaveFile() { // Show curve file addition dialog auto dialog = AddFileDialogFactory::createDialog(FileEntryType::Curve, &MainFrame::Get()); if (dialog && dialog->exec() == QDialog::Accepted) { - QString selectedPath = dialog->getSelectedFilePath(); - - // Create file entry using factory function - auto fileEntry = CreateFileEntryCurve(selectedPath); - if (!fileEntry) { - QMessageBox::warning(&MainFrame::Get(), QObject::tr("Error"), - QObject::tr("Failed to create file entry")); - return; - } - - // Add to workspace - switch (current->SetFileEntry(fileEntry)) { - case WorkSpace::FileEntryResult::Ok: - // Success - no action needed - break; - case WorkSpace::FileEntryResult::LimitExceeded: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("up to 9 files allowed for this type")); - break; - case WorkSpace::FileEntryResult::Duplicate: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("file already added for this type")); - break; - case WorkSpace::FileEntryResult::CopyFailed: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("copy file failed")); - break; - case WorkSpace::FileEntryResult::InvalidFile: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("invalid file")); - break; - } + + SaveWorkSpace(); + + //QString selectedPath = dialog->getSelectedFilePath(); + // + //// Create file entry using factory function + //auto fileEntry = CreateFileEntryCurve(selectedPath); + //if (!fileEntry) { + // QMessageBox::warning(&MainFrame::Get(), QObject::tr("Error"), + // QObject::tr("Failed to create file entry")); + // return; + //} + // + //// Add to workspace + //switch (current->SetFileEntry(fileEntry)) { + //case WorkSpace::FileEntryResult::Ok: + // // Success - no action needed + // break; + //case WorkSpace::FileEntryResult::LimitExceeded: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("up to 9 files allowed for this type")); + // break; + //case WorkSpace::FileEntryResult::Duplicate: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("file already added for this type")); + // break; + //case WorkSpace::FileEntryResult::CopyFailed: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("copy file failed")); + // break; + //case WorkSpace::FileEntryResult::InvalidFile: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("invalid file")); + // break; + //} } } @@ -139,38 +143,41 @@ void FileManagerMenu::AddSurfaceFile() { // Show surface file addition dialog auto dialog = AddFileDialogFactory::createDialog(FileEntryType::Surface, &MainFrame::Get()); if (dialog && dialog->exec() == QDialog::Accepted) { - QString selectedPath = dialog->getSelectedFilePath(); - - // Create file entry using factory function - auto fileEntry = CreateFileEntrySurface(selectedPath); - if (!fileEntry) { - QMessageBox::warning(&MainFrame::Get(), QObject::tr("Error"), - QObject::tr("Failed to create file entry")); - return; - } - - // Add to workspace - switch (current->SetFileEntry(fileEntry)) { - case WorkSpace::FileEntryResult::Ok: - // Success - no action needed - break; - case WorkSpace::FileEntryResult::LimitExceeded: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("up to 9 files allowed for this type")); - break; - case WorkSpace::FileEntryResult::Duplicate: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("file already added for this type")); - break; - case WorkSpace::FileEntryResult::CopyFailed: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("copy file failed")); - break; - case WorkSpace::FileEntryResult::InvalidFile: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("invalid file")); - break; - } + + SaveWorkSpace(); + + //QString selectedPath = dialog->getSelectedFilePath(); + // + //// Create file entry using factory function + //auto fileEntry = CreateFileEntrySurface(selectedPath); + //if (!fileEntry) { + // QMessageBox::warning(&MainFrame::Get(), QObject::tr("Error"), + // QObject::tr("Failed to create file entry")); + // return; + //} + // + //// Add to workspace + //switch (current->SetFileEntry(fileEntry)) { + //case WorkSpace::FileEntryResult::Ok: + // // Success - no action needed + // break; + //case WorkSpace::FileEntryResult::LimitExceeded: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("up to 9 files allowed for this type")); + // break; + //case WorkSpace::FileEntryResult::Duplicate: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("file already added for this type")); + // break; + //case WorkSpace::FileEntryResult::CopyFailed: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("copy file failed")); + // break; + //case WorkSpace::FileEntryResult::InvalidFile: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("invalid file")); + // break; + //} } } @@ -184,38 +191,41 @@ void FileManagerMenu::AddTableFile() { // Show table file addition dialog auto dialog = AddFileDialogFactory::createDialog(FileEntryType::Table, &MainFrame::Get()); if (dialog && dialog->exec() == QDialog::Accepted) { - QString selectedPath = dialog->getSelectedFilePath(); - - // Create file entry using factory function - auto fileEntry = CreateFileEntryTable(selectedPath); - if (!fileEntry) { - QMessageBox::warning(&MainFrame::Get(), QObject::tr("Error"), - QObject::tr("Failed to create file entry")); - return; - } - - // Add to workspace - switch (current->SetFileEntry(fileEntry)) { - case WorkSpace::FileEntryResult::Ok: - // Success - no action needed - break; - case WorkSpace::FileEntryResult::LimitExceeded: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("up to 9 files allowed for this type")); - break; - case WorkSpace::FileEntryResult::Duplicate: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("file already added for this type")); - break; - case WorkSpace::FileEntryResult::CopyFailed: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("copy file failed")); - break; - case WorkSpace::FileEntryResult::InvalidFile: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("invalid file")); - break; - } + + SaveWorkSpace(); + + //QString selectedPath = dialog->getSelectedFilePath(); + // + //// Create file entry using factory function + //auto fileEntry = CreateFileEntryTable(selectedPath); + //if (!fileEntry) { + // QMessageBox::warning(&MainFrame::Get(), QObject::tr("Error"), + // QObject::tr("Failed to create file entry")); + // return; + //} + // + //// Add to workspace + //switch (current->SetFileEntry(fileEntry)) { + //case WorkSpace::FileEntryResult::Ok: + // // Success - no action needed + // break; + //case WorkSpace::FileEntryResult::LimitExceeded: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("up to 9 files allowed for this type")); + // break; + //case WorkSpace::FileEntryResult::Duplicate: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("file already added for this type")); + // break; + //case WorkSpace::FileEntryResult::CopyFailed: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("copy file failed")); + // break; + //case WorkSpace::FileEntryResult::InvalidFile: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("invalid file")); + // break; + //} } } @@ -229,37 +239,55 @@ void FileManagerMenu::AddLightFile() { // Show light file addition dialog auto dialog = AddFileDialogFactory::createDialog(FileEntryType::Light, &MainFrame::Get()); if (dialog && dialog->exec() == QDialog::Accepted) { - QString selectedPath = dialog->getSelectedFilePath(); - - // Create file entry using factory function - auto fileEntry = CreateFileEntryLight(selectedPath); - if (!fileEntry) { - QMessageBox::warning(&MainFrame::Get(), QObject::tr("Error"), - QObject::tr("Failed to create file entry")); - return; - } - - // Add to workspace - switch (current->SetFileEntry(fileEntry)) { - case WorkSpace::FileEntryResult::Ok: - // Success - no action needed - break; - case WorkSpace::FileEntryResult::LimitExceeded: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("up to 9 files allowed for this type")); - break; - case WorkSpace::FileEntryResult::Duplicate: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("file already added for this type")); - break; - case WorkSpace::FileEntryResult::CopyFailed: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("copy file failed")); - break; - case WorkSpace::FileEntryResult::InvalidFile: - QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), - QObject::tr("invalid file")); - break; - } + + SaveWorkSpace(); + + //QString selectedPath = dialog->getSelectedFilePath(); + // + //// Create file entry using factory function + //auto fileEntry = CreateFileEntryLight(selectedPath); + //if (!fileEntry) { + // QMessageBox::warning(&MainFrame::Get(), QObject::tr("Error"), + // QObject::tr("Failed to create file entry")); + // return; + //} + // + //// Add to workspace + //switch (current->SetFileEntry(fileEntry)) { + //case WorkSpace::FileEntryResult::Ok: + // // Success - no action needed + // break; + //case WorkSpace::FileEntryResult::LimitExceeded: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("up to 9 files allowed for this type")); + // break; + //case WorkSpace::FileEntryResult::Duplicate: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("file already added for this type")); + // break; + //case WorkSpace::FileEntryResult::CopyFailed: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("copy file failed")); + // break; + //case WorkSpace::FileEntryResult::InvalidFile: + // QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), + // QObject::tr("invalid file")); + // break; + //} } } + +void FileManagerMenu::AddPolarFile() { + auto current = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == current) { + QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), QObject::tr("please create workspace first")); + return; + } + + // Show light file addition dialog + auto dialog = AddFileDialogFactory::createDialog(FileEntryType::Polar, &MainFrame::Get()); + if (dialog && dialog->exec() == QDialog::Accepted) { + + SaveWorkSpace(); + } +} \ No newline at end of file diff --git a/src/ui/Menu/FileManagerMenu.h b/src/ui/Menu/FileManagerMenu.h index da08123b..5162b618 100644 --- a/src/ui/Menu/FileManagerMenu.h +++ b/src/ui/Menu/FileManagerMenu.h @@ -25,6 +25,7 @@ protected: void AddSurfaceFile(); void AddTableFile(); void AddLightFile(); + void AddPolarFile(); signals: void LoadDyt(const QString& path); diff --git a/src/ui/Menu/FileManagerMenu.ui b/src/ui/Menu/FileManagerMenu.ui index 0aa0b80b..c92bbb90 100644 --- a/src/ui/Menu/FileManagerMenu.ui +++ b/src/ui/Menu/FileManagerMenu.ui @@ -94,6 +94,16 @@ + + + + new polar file + + + + + + diff --git a/src/ui/Panel/DataPanel.cpp b/src/ui/Panel/DataPanel.cpp index e24245ed..74229192 100644 --- a/src/ui/Panel/DataPanel.cpp +++ b/src/ui/Panel/DataPanel.cpp @@ -82,6 +82,8 @@ QString DataPanel::GetTypeDisplayName() const return "Table"; case FileEntryType::Light: return "Light"; + case FileEntryType::Polar: + return "Polar"; default: return "Unknown"; } diff --git a/src/ui/Panel/DataPanelFactory.cpp b/src/ui/Panel/DataPanelFactory.cpp index 153c5e78..fc937f56 100644 --- a/src/ui/Panel/DataPanelFactory.cpp +++ b/src/ui/Panel/DataPanelFactory.cpp @@ -4,9 +4,10 @@ #include "common/SpdLogger.h" // Forward declarations for future panel types - #include "SurfacePanel.h" - #include "TablePanel.h" - #include "LightPanel.h" +#include "SurfacePanel.h" +#include "TablePanel.h" +#include "LightPanel.h" +#include "PolarPanel.h" DataPanel* DataPanelFactory::CreatePanel(FileEntryType fileType, int index, const QString& filePath, QWidget* parent) { @@ -30,6 +31,11 @@ DataPanel* DataPanelFactory::CreatePanel(FileEntryType fileType, int index, cons // TODO: Implement LightPanel LOG_WARN("LightPanel not implemented yet, creating base DataPanel"); return new LightPanel(index, filePath, parent); + + case FileEntryType::Polar: + // TODO: Implement LightPanel + LOG_WARN("PolarPanel not implemented yet, creating base DataPanel"); + return new PolarPanel(index, filePath, parent); default: LOG_ERROR("Unsupported file type: {}", static_cast(fileType)); @@ -51,6 +57,7 @@ bool DataPanelFactory::IsTypeSupported(FileEntryType fileType) case FileEntryType::Surface: case FileEntryType::Table: case FileEntryType::Light: + case FileEntryType::Polar: return true; default: return false; @@ -68,6 +75,8 @@ QString DataPanelFactory::GetTypeDisplayName(FileEntryType fileType) return "Table"; case FileEntryType::Light: return "Light"; + case FileEntryType::Polar: + return "Polar"; default: return "Unknown"; } diff --git a/src/ui/Panel/DataPanelManager.cpp b/src/ui/Panel/DataPanelManager.cpp index b5fa55db..636259d1 100644 --- a/src/ui/Panel/DataPanelManager.cpp +++ b/src/ui/Panel/DataPanelManager.cpp @@ -64,6 +64,7 @@ void DataPanelManager::SetWorkspace(WorkSpace* workspace) UpdatePanelsForType(FileEntryType::Surface); UpdatePanelsForType(FileEntryType::Table); UpdatePanelsForType(FileEntryType::Light); + UpdatePanelsForType(FileEntryType::Polar); } else { ClearAllPanels(); } diff --git a/src/ui/Panel/PolarPanel.cpp b/src/ui/Panel/PolarPanel.cpp new file mode 100644 index 00000000..979e6470 --- /dev/null +++ b/src/ui/Panel/PolarPanel.cpp @@ -0,0 +1,344 @@ +#include "ui/Panel/PolarPanel.h" +#include "ui/DockWidget.h" +#include "ui/DockTitleBar.h" +#include "common/SpdLogger.h" +#include +#include +#include + +PolarPanel::PolarPanel(int index, const QString& filePath, QWidget* parent) + : DataPanel(index, FileEntryType::Polar, filePath, parent) +{ + m_pChart = NULL; + m_pThetaAxis = NULL; + m_pRadiusAxis = NULL; + + m_iThetaMax = 0; + m_iThetaMin = 0; + m_iRadiusMax = 0; + m_iRadiusMin = 0; + + m_unitTheta = ""; + m_unitRadius = ""; + + LOG_INFO("Created PolarPanel {} for file: {}", index, filePath.toStdString()); +} + +PolarPanel::PolarPanel(int index, std::shared_ptr fileEntry, QWidget* parent) + : DataPanel(index, fileEntry, parent) +{ + m_pChart = NULL; + m_pThetaAxis = NULL; + m_pRadiusAxis = NULL; + + m_iThetaMax = 0; + m_iThetaMin = 0; + m_iRadiusMax = 0; + m_iRadiusMin = 0; + + m_unitTheta = ""; + m_unitRadius = ""; + + if (fileEntry) { + LOG_INFO("Created PolarPanel {} for chart: {}", index, fileEntry->GetName().toStdString()); + // Override the title with chart name + title_ = QString("Polar Panel %1 - %2").arg(index).arg(fileEntry->GetName()); + } + else { + LOG_WARN("Created PolarPanel {} with null chart data", index); + } +} + +PolarPanel::~PolarPanel() +{ + LOG_INFO("Destroyed PolarPanel {}", GetIndex()); +} + +void PolarPanel::RefreshPanel() +{ + // Implement curve-specific refresh logic here + DataPanel::RefreshPanel(); + + if (auto fileEntry = fileEntry_->AsPolar()) { + OnDataPanelUpdated(fileEntry); + } + + LOG_INFO("Refreshed TablePanel {}", GetIndex()); +} + +void PolarPanel::InitUI() +{ + m_pChart = new QPolarChart(); + //mPolarChart->addSeries(mCurveData); + //m_pChart->legend()->hide(); + m_pChart->layout()->setContentsMargins(0, 0, 0, 0); + m_pChart->setBackgroundRoundness(0); + m_pChart->setTheme(QChart::ChartThemeBlueIcy); + + // + m_pThetaAxis = new QValueAxis(); + m_pThetaAxis->setTickCount(13); + m_pThetaAxis->setLabelFormat("%d"); + m_pThetaAxis->setRange(0, 360); + m_pChart->addAxis(m_pThetaAxis, QPolarChart::PolarOrientationAngular); + + m_pRadiusAxis = new QValueAxis(); + m_pRadiusAxis->setTickCount(6); + m_pRadiusAxis->setLabelFormat("%d"); + m_pRadiusAxis->setRange(0, 30); + m_pChart->addAxis(m_pRadiusAxis, QPolarChart::PolarOrientationRadial); + + QChartView *mChartView = new QChartView(); + mChartView->setChart(m_pChart); + mChartView->setRenderHint(QPainter::Antialiasing); + + QHBoxLayout* mainLayout = new QHBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->addWidget(mChartView); + setLayout(mainLayout); +} + +QString PolarPanel::GetTypeDisplayName() const +{ + return "Polar"; +} + +void PolarPanel::OnDataPanelUpdated(FileEntryPolar* fileEntry) +{ + QString strName = fileEntry->GetName(); + updateTitle(strName); + + FileEntryPolar::ChartProperties propChart = fileEntry->GetChartProperties(); + updateTitleAxis(propChart.AngularTitle, propChart.RadialTitle); + updateMinMaxTheta(propChart.AngularMin, propChart.AngularMax, propChart.AngularCount, propChart.AngularUnit); + updateMinMaxRadius(propChart.RadialMin, propChart.RadialMax, propChart.RadialCount, propChart.RadialUnit); + + QString strFile = fileEntry->GetPath() + "/" + fileEntry->GetFileName(); + FileEntryPolar::LineProperties propCurves = fileEntry->GetLineProperties(); + updateParseFile(strFile, propChart.timeParam, propCurves); +} + +void PolarPanel::OnTimeChanged(double time) +{ + if (m_data.size() > 0) + { + for (QMap::Iterator itSeries = m_seriesIDMap.begin(); itSeries != m_seriesIDMap.end(); itSeries++) + { + QLineSeries * pSeries = itSeries.value(); + if (pSeries) + { + pSeries->clear(); + } + } + + QMap< double, QMap >::const_iterator ite = m_data.lowerBound(time); + if (ite != m_data.end()) + { + ite++; + } + + for (QMap< double, QMap >::Iterator itA = m_data.begin(); itA != ite; itA++) + { + double dTime = itA.key(); + QMap mapData = itA.value(); + for (QMap::Iterator it = mapData.begin(); it != mapData.end(); it++) + { + int nIndex = it.key(); + QPointF data = it.value(); + + QLineSeries * pSeries = m_seriesIDMap.value(nIndex); + if (pSeries) + { + pSeries->append(data); + } + } + } + + m_pChart->update(); + } +} + +void PolarPanel::updateTitle(const QString & title) +{ + if (nullptr != dockWidget_) + { + dockWidget_->setWindowTitle(title); + } +} + +void PolarPanel::updateTitleAxis(const QString& thetaTitle, const QString& radiusTitle) +{ + if (m_pThetaAxis) + { + if (!thetaTitle.isEmpty()) + { + m_pThetaAxis->setTitleText(thetaTitle); + } + } + if (m_pRadiusAxis) + { + if (!radiusTitle.isEmpty()) + { + m_pRadiusAxis->setTitleText(radiusTitle); + } + } +} + +void PolarPanel::updateMinMaxTheta(float min, float max, int count, const QString& unit) +{ + if (m_pThetaAxis) + { + if (max > min) + { + m_iThetaMax = max; + m_iThetaMin = min; + + m_pThetaAxis->setRange(min, max); + } + + if (count > 0) + { + m_pThetaAxis->setTickCount(count); + } + + if (!unit.isEmpty()) + { + m_pThetaAxis->setLabelFormat("%d" + unit); + + m_unitTheta = unit; + } + } +} + +void PolarPanel::updateMinMaxRadius(float min, float max, int count, const QString& unit) +{ + if (m_pRadiusAxis) + { + if (max > min) + { + m_iRadiusMax = max; + m_iRadiusMin = min; + + m_pRadiusAxis->setRange(min, max); + } + + if (count > 0) + { + m_pRadiusAxis->setTickCount(count); + } + + if (!unit.isEmpty()) + { + m_pRadiusAxis->setLabelFormat("%d" + unit); + + m_unitRadius = unit; + } + } +} + +void PolarPanel::updateParseFile(const QString& strFile, int nT, FileEntryPolar::LineProperties listCurve) +{ + if (strFile.isEmpty()) + { + QMessageBox::information(nullptr, QStringLiteral("Error"), QStringLiteral("Please check file path")); + return; + } + + m_seriesIDMap.clear(); + + QFile file(strFile); + if (file.open(QIODevice::ReadOnly)) + { + bool bResetAxisTheta = false; + if (m_iThetaMax == m_iThetaMin) + { + bResetAxisTheta = true; + } + bool bResetAxisRadius = false; + if (m_iRadiusMax == m_iRadiusMin) + { + bResetAxisRadius = true; + } + + float maxTheta = -10000000.0; + float minTheta = 10000000.0; + float maxRadius = -10000000.0; + float minRadius = 10000000.0; + + while (!file.atEnd()) + { + QString strLine = file.readLine().simplified(); + if (!strLine.isEmpty()) + { + QStringList listLine = strLine.split(" "); + double t = listLine.at(nT).toDouble(); + + QMap mapData; + for (int nI = 0; nI < listCurve.size(); nI++) + { + FileEntryPolar::LineProperty propCurve = listCurve.at(nI); + + double Angular = listLine.at(propCurve.Angular).toDouble(); + double Radial = listLine.at(propCurve.Radial).toDouble(); + + QPointF ptData = QPointF(Angular, Radial); + mapData.insert(nI, ptData); + + if (bResetAxisTheta) + { + if (Angular < minTheta) + { + minTheta = Angular; + } + if (Angular > maxTheta) + { + maxTheta = Angular; + } + } + if (bResetAxisRadius) + { + if (Radial < minRadius) + { + minRadius = Radial; + } + if (Radial > maxRadius) + { + maxRadius = Radial; + } + } + } + m_data.insert(t, mapData); + } + } + + if (bResetAxisTheta) + { + updateMinMaxTheta(minTheta * 0.9, maxTheta * 1.1, 0, m_unitTheta); + } + if (bResetAxisRadius) + { + updateMinMaxRadius(minRadius * 0.9, maxRadius * 1.1, 0, m_unitRadius); + } + + for (int nI = 0; nI < listCurve.size(); nI++) + { + FileEntryPolar::LineProperty propCurve = listCurve.at(nI); + + QLineSeries *pSeries = new QLineSeries(this); + pSeries->setName(propCurve.name); + pSeries->setColor(propCurve.color); + pSeries->setUseOpenGL(true); + m_pChart->addSeries(pSeries); + m_seriesIDMap.insert(nI, pSeries); + + QPen pen(propCurve.color); + pen.setWidth(2); + pSeries->setPen(pen); + + pSeries->attachAxis(m_pThetaAxis); + pSeries->attachAxis(m_pRadiusAxis); + } + + file.close(); + } +} \ No newline at end of file diff --git a/src/ui/Panel/PolarPanel.h b/src/ui/Panel/PolarPanel.h new file mode 100644 index 00000000..2eb1653e --- /dev/null +++ b/src/ui/Panel/PolarPanel.h @@ -0,0 +1,87 @@ +#pragma once + +#include "DataPanel.h" +#include "workspace/FileEntry.h" +#include + +#include + +class PolarPanel : public DataPanel +{ + Q_OBJECT + +public: + /** + * @brief Constructor + * @param index Panel index + * @param filePath Associated file path + * @param parent Parent widget + */ + explicit PolarPanel(int index, const QString& filePath, QWidget* parent = nullptr); + + /** + * @brief Constructor with chart data + * @param index Panel index + * @param chartData Chart data containing curve information + * @param parent Parent widget + */ + explicit PolarPanel(int index, std::shared_ptr fileEntry, QWidget* parent = nullptr); + + /** + * @brief Destructor + */ + virtual ~PolarPanel(); + + /** + * @brief Get file type + * @return File type (always Curve for this class) + */ + FileEntryType GetFileType() const override { return FileEntryType::Table; } + + /** + * @brief Refresh panel content + */ + void RefreshPanel() override; + +protected: + /** + * @brief Initialize UI for curve-specific layout + */ + virtual void InitUI(); + + /** + * @brief Get type display name + * @return Display name for curve type + */ + QString GetTypeDisplayName() const override; + + void OnDataPanelUpdated(FileEntryPolar* fileEntry); + + virtual void OnTimeChanged(double time); + +private: + void updateTitle(const QString& title); + void updateTitleAxis(const QString& thetaTitle, const QString& radiusTitle); + void updateMinMaxTheta(float min, float max, int count, const QString& unit); + void updateMinMaxRadius(float min, float max, int count, const QString& unit); + + void updateParseFile(const QString& strFile, int nT, FileEntryPolar::LineProperties listCurve); + +private: + QPolarChart *m_pChart; + QValueAxis *m_pThetaAxis; + QValueAxis *m_pRadiusAxis; + + float m_iThetaMax; + float m_iThetaMin; + float m_iRadiusMax; + float m_iRadiusMin; + + QString m_unitTheta; + QString m_unitRadius; + + QMap m_seriesIDMap; + + QMap< double, QMap > m_data; +}; + diff --git a/src/ui/PropertyBrowser/qtpropertymanager.cpp b/src/ui/PropertyBrowser/qtpropertymanager.cpp index 7d17810f..115408fc 100644 --- a/src/ui/PropertyBrowser/qtpropertymanager.cpp +++ b/src/ui/PropertyBrowser/qtpropertymanager.cpp @@ -7967,6 +7967,15 @@ public: QMap> m_properyToLightPaths; QMap m_lightPathToPropery; QMap m_lightPathIndex; + + // Polar + QMap m_properyToPolarGroup; + QMap m_polarGroupToPropery; + QMap m_properyToPolarCount; + QMap m_polarCountToPropery; + QMap> m_properyToPolarPaths; + QMap m_polarPathToPropery; + QMap m_polarPathIndex; }; void QtWorkspacePropertyManagerPrivate::slotStringChanged(QtProperty* property, QString value) { @@ -8026,7 +8035,12 @@ void QtWorkspacePropertyManagerPrivate::slotStringChanged(QtProperty* property, int idx = m_lightPathIndex.value(property, 0); c.SetFileEntryPath(FileEntryType::Light, idx, value); q_ptr->setValue(prop, c); - } + } else if (QtProperty* prop = m_polarPathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + int idx = m_polarPathIndex.value(property, 0); + c.SetFileEntryPath(FileEntryType::Polar, idx, value); + q_ptr->setValue(prop, c); + } } void QtWorkspacePropertyManagerPrivate::slotIntChanged(QtProperty* property, int value) { @@ -8052,6 +8066,7 @@ void QtWorkspacePropertyManagerPrivate::slotIntChanged(QtProperty* property, int case FileEntryType::Surface: title = QObject::tr("Surface[%1]").arg(i + 1); break; case FileEntryType::Table: title = QObject::tr("Table[%1]").arg(i + 1); break; case FileEntryType::Light: title = QObject::tr("Light[%1]").arg(i + 1); break; + case FileEntryType::Polar: title = QObject::tr("Polar[%1]").arg(i + 1); break; } p->setPropertyName(title); group->addSubProperty(p); @@ -8080,6 +8095,7 @@ void QtWorkspacePropertyManagerPrivate::slotIntChanged(QtProperty* property, int handleType(FileEntryType::Surface, m_surfaceCountToPropery, m_properyToSurfacePaths, m_surfacePathToPropery, m_surfacePathIndex, m_properyToSurfaceGroup); handleType(FileEntryType::Table, m_tableCountToPropery, m_properyToTablePaths, m_tablePathToPropery, m_tablePathIndex, m_properyToTableGroup); handleType(FileEntryType::Light, m_lightCountToPropery, m_properyToLightPaths, m_lightPathToPropery, m_lightPathIndex, m_properyToLightGroup); + handleType(FileEntryType::Polar, m_polarCountToPropery, m_properyToPolarPaths, m_polarPathToPropery, m_polarPathIndex, m_properyToPolarGroup); } void QtWorkspacePropertyManagerPrivate::slotPropertyDestroyed(QtProperty* property) { @@ -8273,6 +8289,7 @@ void QtWorkspacePropertyManager::setValue(QtProperty* property, const QWorkspace syncGroup(FileEntryType::Surface, d_ptr->m_properyToSurfaceGroup, d_ptr->m_properyToSurfaceCount, d_ptr->m_properyToSurfacePaths, d_ptr->m_surfacePathToPropery, d_ptr->m_surfacePathIndex); syncGroup(FileEntryType::Table, d_ptr->m_properyToTableGroup, d_ptr->m_properyToTableCount, d_ptr->m_properyToTablePaths, d_ptr->m_tablePathToPropery, d_ptr->m_tablePathIndex); syncGroup(FileEntryType::Light, d_ptr->m_properyToLightGroup, d_ptr->m_properyToLightCount, d_ptr->m_properyToLightPaths, d_ptr->m_lightPathToPropery, d_ptr->m_lightPathIndex); + syncGroup(FileEntryType::Polar, d_ptr->m_properyToPolarGroup, d_ptr->m_properyToPolarCount, d_ptr->m_properyToPolarPaths, d_ptr->m_polarPathToPropery, d_ptr->m_polarPathIndex); emit propertyChanged(property); emit valueChanged(property, value); @@ -8408,6 +8425,9 @@ void QtWorkspacePropertyManager::initializeProperty(QtProperty* property) { addGroup(FileEntryType::Light, tr("Lights"), d_ptr->m_properyToLightGroup, d_ptr->m_lightGroupToPropery, d_ptr->m_properyToLightCount, d_ptr->m_lightCountToPropery, d_ptr->m_properyToLightPaths, d_ptr->m_lightPathToPropery, d_ptr->m_lightPathIndex); + addGroup(FileEntryType::Polar, tr("Polars"), d_ptr->m_properyToPolarGroup, d_ptr->m_polarGroupToPropery, + d_ptr->m_properyToPolarCount, d_ptr->m_polarCountToPropery, + d_ptr->m_properyToPolarPaths, d_ptr->m_polarPathToPropery, d_ptr->m_polarPathIndex); } /*! @@ -8519,6 +8539,9 @@ void QtWorkspacePropertyManager::uninitializeProperty(QtProperty* property) { cleanupGroup(d_ptr->m_properyToLightGroup, d_ptr->m_lightGroupToPropery, d_ptr->m_properyToLightCount, d_ptr->m_lightCountToPropery, d_ptr->m_properyToLightPaths, d_ptr->m_lightPathToPropery, d_ptr->m_lightPathIndex); + cleanupGroup(d_ptr->m_properyToPolarGroup, d_ptr->m_polarGroupToPropery, + d_ptr->m_properyToPolarCount, d_ptr->m_polarCountToPropery, + d_ptr->m_properyToPolarPaths, d_ptr->m_polarPathToPropery, d_ptr->m_polarPathIndex); } #pragma endregion diff --git a/src/ui/WorkSpace/AddCurveFileDlg.ui b/src/ui/WorkSpace/AddCurveFileDlg.ui index 57d21058..a18c17d3 100644 --- a/src/ui/WorkSpace/AddCurveFileDlg.ui +++ b/src/ui/WorkSpace/AddCurveFileDlg.ui @@ -7,7 +7,7 @@ 0 0 600 - 705 + 789 @@ -22,6 +22,12 @@ + + + 0 + 25 + + File Path: @@ -29,6 +35,12 @@ + + + 0 + 25 + + true @@ -39,6 +51,12 @@ + + + 0 + 25 + + ... @@ -46,6 +64,12 @@ + + + 0 + 25 + + File Name: @@ -53,6 +77,12 @@ + + + 0 + 25 + + - @@ -60,6 +90,12 @@ + + + 0 + 25 + + File Size: @@ -67,6 +103,12 @@ + + + 0 + 25 + + - @@ -83,6 +125,12 @@ + + + 0 + 25 + + Chart Name: @@ -90,6 +138,12 @@ + + + 0 + 25 + + Chart 1 @@ -100,16 +154,35 @@ + + + 0 + 25 + + Chart Type: - + + + + 0 + 25 + + + + + + 0 + 25 + + X Axis Title: @@ -117,6 +190,12 @@ + + + 0 + 25 + + Enter X axis title... @@ -124,6 +203,12 @@ + + + 0 + 25 + + Y Axis Title: @@ -131,6 +216,12 @@ + + + 0 + 25 + + Enter Y axis title... @@ -138,13 +229,26 @@ + + + 0 + 25 + + Time: - + + + + 0 + 25 + + + @@ -157,6 +261,12 @@ + + + 0 + 25 + + -999999.000000000000000 @@ -170,6 +280,12 @@ + + + 0 + 25 + + 2 @@ -183,6 +299,12 @@ + + + 0 + 25 + + X Tick Count: @@ -190,6 +312,12 @@ + + + 0 + 25 + + -999999.000000000000000 @@ -203,6 +331,12 @@ + + + 0 + 25 + + X Min: @@ -210,6 +344,12 @@ + + + 0 + 25 + + Y Max: @@ -217,6 +357,12 @@ + + + 0 + 25 + + Y Min: @@ -224,6 +370,12 @@ + + + 0 + 25 + + 2 @@ -231,12 +383,18 @@ 50 - 6 + 9 + + + 0 + 25 + + -999999.000000000000000 @@ -250,6 +408,12 @@ + + + 0 + 25 + + -999999.000000000000000 @@ -263,6 +427,12 @@ + + + 0 + 25 + + X Max: @@ -270,6 +440,12 @@ + + + 0 + 25 + + Y Tick Count: @@ -308,6 +484,12 @@ + + + 0 + 0 + + 80 @@ -321,6 +503,12 @@ + + + 0 + 0 + + 60 @@ -358,6 +546,12 @@ + + + 0 + 25 + + Curve Name: @@ -365,6 +559,12 @@ + + + 0 + 25 + + Enter curve name... @@ -372,6 +572,12 @@ + + + 0 + 25 + + Curve Color: @@ -381,6 +587,12 @@ + + + 0 + 0 + + 100 @@ -425,6 +637,12 @@ + + + 0 + 25 + + Data Start: @@ -432,6 +650,12 @@ + + + 0 + 25 + + 1 @@ -445,6 +669,12 @@ + + + 0 + 25 + + Data Stop: @@ -452,6 +682,12 @@ + + + 0 + 25 + + 1 @@ -538,6 +774,12 @@ + + + 100 + 30 + + Add File @@ -548,6 +790,12 @@ + + + 100 + 30 + + Cancel diff --git a/src/ui/WorkSpace/AddFileDialogFactory.cpp b/src/ui/WorkSpace/AddFileDialogFactory.cpp index b9bf66bf..ffb9fc9d 100644 --- a/src/ui/WorkSpace/AddFileDialogFactory.cpp +++ b/src/ui/WorkSpace/AddFileDialogFactory.cpp @@ -5,6 +5,7 @@ #include "AddSurfaceFileDlg.h" #include "AddTableFileDlg.h" #include "AddLightFileDlg.h" +#include "AddPolarFileDlg.h" BaseAddFileDlg* AddFileDialogFactory::createDialog(FileEntryType type, QWidget* parent) { switch (type) { @@ -16,6 +17,8 @@ BaseAddFileDlg* AddFileDialogFactory::createDialog(FileEntryType type, QWidget* return new AddTableFileDlg(parent); case FileEntryType::Light: return new AddLightFileDlg(parent); + case FileEntryType::Polar: + return new AddPolarFileDlg(parent); default: return nullptr; } diff --git a/src/ui/WorkSpace/AddLightFileDlg.cpp b/src/ui/WorkSpace/AddLightFileDlg.cpp index ece774a6..85b4fb16 100644 --- a/src/ui/WorkSpace/AddLightFileDlg.cpp +++ b/src/ui/WorkSpace/AddLightFileDlg.cpp @@ -29,6 +29,8 @@ AddLightFileDlg::AddLightFileDlg(QWidget* parent) // Update color previews updateOpenColorPreview(openColor_); updateCloseColorPreview(closeColor_); + + ui->editDataBtn->setVisible(false); } AddLightFileDlg::~AddLightFileDlg() { @@ -344,6 +346,14 @@ QString AddLightFileDlg::getDialogTitle() const { } void AddLightFileDlg::onSure() { + + QString sName = ui->ChartNameEdit->text(); + // Validate table-specific parameters + if (sName.isEmpty()) { + QMessageBox::warning(this, tr("Warning"), tr("Please enter a Chart name.")); + return; + } + // Save current light properties if any light is selected if (currentLightIndex_ >= 0) { saveLightProperties(); @@ -364,6 +374,8 @@ void AddLightFileDlg::onSure() { // Create FileEntryLight and set properties auto lightEntry = CreateFileEntryLight(getSelectedFilePath()); if (lightEntry) { + lightEntry->SetName(sName); + // Set color properties lightEntry->SetColorProperties(colorProperties_); diff --git a/src/ui/WorkSpace/AddLightFileDlg.ui b/src/ui/WorkSpace/AddLightFileDlg.ui index 26d8e86c..c45f4c23 100644 --- a/src/ui/WorkSpace/AddLightFileDlg.ui +++ b/src/ui/WorkSpace/AddLightFileDlg.ui @@ -7,7 +7,7 @@ 0 0 600 - 516 + 607 @@ -22,6 +22,12 @@ + + + 0 + 25 + + File Path: @@ -29,6 +35,12 @@ + + + 0 + 25 + + true @@ -46,6 +58,12 @@ + + + 0 + 25 + + File Name: @@ -53,6 +71,12 @@ + + + 0 + 25 + + - @@ -60,6 +84,12 @@ + + + 0 + 25 + + File Size: @@ -75,6 +105,44 @@ + + + + true + + + Chart Properties + + + + + + + 0 + 25 + + + + Chart Names: + + + + + + + + 0 + 25 + + + + + + + + + + @@ -272,6 +340,12 @@ + + + 0 + 25 + + Light Names: @@ -279,6 +353,12 @@ + + + 0 + 25 + + Enter light names (comma separated)... @@ -286,8 +366,14 @@ + + + 0 + 25 + + - Light Data: + Light Datas: @@ -295,6 +381,12 @@ + + + 0 + 25 + + Enter data values (comma separated integers)... @@ -317,6 +409,12 @@ + + + 0 + 25 + + Row Index: @@ -354,6 +452,12 @@ + + + 100 + 30 + + Add File @@ -361,6 +465,12 @@ + + + 100 + 30 + + Cancel diff --git a/src/ui/WorkSpace/AddPolarDlg.ui b/src/ui/WorkSpace/AddPolarDlg.ui new file mode 100644 index 00000000..062e002d --- /dev/null +++ b/src/ui/WorkSpace/AddPolarDlg.ui @@ -0,0 +1,810 @@ + + + AddPolarDlg + + + + 0 + 0 + 600 + 789 + + + + Add Polar + + + + + + File Selection + + + + + + + 0 + 25 + + + + File Path: + + + + + + + + 0 + 25 + + + + true + + + Select curve data file... + + + + + + + + 0 + 25 + + + + ... + + + + + + + + 0 + 25 + + + + File Name: + + + + + + + + 0 + 25 + + + + - + + + + + + + + 0 + 25 + + + + File Size: + + + + + + + + 0 + 25 + + + + - + + + + + + + + + + Chart Properties + + + + + + + 0 + 25 + + + + Chart Name: + + + + + + + + 0 + 25 + + + + Chart 1 + + + Enter chart name... + + + + + + + + 0 + 25 + + + + Chart Type: + + + + + + + + 0 + 25 + + + + + + + + + 0 + 25 + + + + X Axis Title: + + + + + + + + 0 + 25 + + + + Enter X axis title... + + + + + + + + 0 + 25 + + + + Y Axis Title: + + + + + + + + 0 + 25 + + + + Enter Y axis title... + + + + + + + + 0 + 25 + + + + Time: + + + + + + + + 0 + 25 + + + + + + + + + + + Axis Range Settings + + + + + + + 0 + 25 + + + + -999999.000000000000000 + + + 999999.000000000000000 + + + 800.000000000000000 + + + + + + + + 0 + 25 + + + + 2 + + + 50 + + + 6 + + + + + + + + 0 + 25 + + + + X Tick Count: + + + + + + + + 0 + 25 + + + + -999999.000000000000000 + + + 999999.000000000000000 + + + 0.000000000000000 + + + + + + + + 0 + 25 + + + + X Min: + + + + + + + + 0 + 25 + + + + Y Max: + + + + + + + + 0 + 25 + + + + Y Min: + + + + + + + + 0 + 25 + + + + 2 + + + 50 + + + 9 + + + + + + + + 0 + 25 + + + + -999999.000000000000000 + + + 999999.000000000000000 + + + -800.000000000000000 + + + + + + + + 0 + 25 + + + + -999999.000000000000000 + + + 999999.000000000000000 + + + 250.000000000000000 + + + + + + + + 0 + 25 + + + + X Max: + + + + + + + + 0 + 25 + + + + Y Tick Count: + + + + + + + + + + Curve Management + + + + + + + + Curves: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 80 + 16777215 + + + + Add Curve + + + + + + + + 0 + 0 + + + + + 60 + 16777215 + + + + Remove + + + + + + + + + + 16777215 + 120 + + + + true + + + + + + + false + + + Selected Curve Properties + + + + + + + 0 + 25 + + + + Curve Name: + + + + + + + + 0 + 25 + + + + Enter curve name... + + + + + + + + 0 + 25 + + + + Curve Color: + + + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + Select Color + + + + + + + + 50 + 25 + + + + background-color: rgb(255, 0, 0); border: 1px solid black; + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 25 + + + + Data Start: + + + + + + + + 0 + 25 + + + + 1 + + + 999999 + + + 1 + + + + + + + + 0 + 25 + + + + Data Stop: + + + + + + + + 0 + 25 + + + + 1 + + + 999999 + + + 241 + + + + + + + false + + + X Value: + + + + + + + false + + + -999999.000000000000000 + + + 999999.000000000000000 + + + 0.000000000000000 + + + + + + + false + + + Y Value: + + + + + + + false + + + -999999.000000000000000 + + + 999999.000000000000000 + + + 0.000000000000000 + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 100 + 30 + + + + Add File + + + true + + + + + + + + 100 + 30 + + + + Cancel + + + + + + + + + + diff --git a/src/ui/WorkSpace/AddPolarFileDlg.cpp b/src/ui/WorkSpace/AddPolarFileDlg.cpp new file mode 100644 index 00000000..477f46a7 --- /dev/null +++ b/src/ui/WorkSpace/AddPolarFileDlg.cpp @@ -0,0 +1,225 @@ +#include "AddPolarFileDlg.h" + +#include +#include +#include +#include +#include + +#include "workspace/WorkSpace.h" +#include "workspace/WorkSpaceManager.h" + +#include "ui_AddPolarDlg.h" + +AddPolarFileDlg::AddPolarFileDlg(QWidget* parent) + : BaseAddFileDlg(FileEntryType::Polar, parent) + , ui(new Ui::AddPolarDlg) + , currentCurveIndex_(-1) + , selectedColor_(255, 0, 0) { // Default to red color + + SetupUI(ui); + SetTitle(getDialogTitle()); + + //setupConnections(); + //updateCurvePropertiesUI(); // Initialize UI based on default chart type +} + +AddPolarFileDlg::~AddPolarFileDlg() { + delete ui; +} + + +QString AddPolarFileDlg::getFileFilter() const { + return "Data Files (*.txt *.csv *.dat);;All Files (*.*)"; +} + +QString AddPolarFileDlg::getDialogTitle() const { + return "Add Polar Data File"; +} + +bool AddPolarFileDlg::validateSpecificParams() { + // File path validation + const QString& selectFilePath = getSelectedFilePath(); + if (selectFilePath.isEmpty()) { + QMessageBox::warning(this, tr("Validation Error"), tr("Please select a data file.")); + return false; + } + + // File existence validation + QFileInfo fileInfo(selectFilePath); + if (!fileInfo.exists()) { + QMessageBox::warning(this, tr("Validation Error"), tr("Selected file does not exist.")); + return false; + } + + // File readability validation + if (!fileInfo.isReadable()) { + QMessageBox::warning(this, tr("Validation Error"), tr("Selected file is not readable. Please check file permissions.")); + return false; + } + + // File size validation (avoid memory issues with large files) + if (fileInfo.size() > 100 * 1024 * 1024) { // 100MB limit + QMessageBox::warning(this, tr("Validation Error"), tr("File is too large (over 100MB). Please select a smaller file.")); + return false; + } + + // Curve count validation + if (curves_.isEmpty()) { + QMessageBox::warning(this, tr("Validation Error"), tr("At least one curve must be defined.")); + return false; + } + + //// Save current curve properties + //if (currentCurveIndex_ >= 0) { + // saveCurveProperties(); + //} + + //// Curve name uniqueness validation + //QStringList curveNames; + //for (int i = 0; i < curves_.size(); ++i) { + // const FileEntryCurve::CurveProperty& curve = curves_[i]; + + // if (curve.name.isEmpty()) { + // QMessageBox::warning(this, tr("Validation Error"), + // tr("Curve %1 name cannot be empty.").arg(i + 1)); + // return false; + // } + + // if (curveNames.contains(curve.name)) { + // QMessageBox::warning(this, tr("Validation Error"), + // tr("Curve name '%1' is duplicated. Please use different names.").arg(curve.name)); + // return false; + // } + // curveNames.append(curve.name); + + // // Curve name length validation + // if (curve.name.length() > 50) { + // QMessageBox::warning(this, tr("Validation Error"), + // tr("Curve name '%1' is too long. Please limit to 50 characters.").arg(curve.name)); + // return false; + // } + + // // Data range validation based on chart type + // if (chartProperties_.chartType == ChartType::Wave) { + // if (curve.data.wave.start < 1 || curve.data.wave.stop < 1) { + // QMessageBox::warning(this, tr("Validation Error"), + // tr("Curve '%1' start and stop values must be greater than 0.").arg(curve.name)); + // return false; + // } + + // if (curve.data.wave.start > curve.data.wave.stop) { + // QMessageBox::warning(this, tr("Validation Error"), + // tr("Curve '%1' start value cannot be greater than stop value.").arg(curve.name)); + // return false; + // } + + // // Data range reasonableness validation + // if (curve.data.wave.stop - curve.data.wave.start < 1) { + // QMessageBox::warning(this, tr("Validation Error"), + // tr("Curve '%1' data range is too small. At least 2 data points are required.").arg(curve.name)); + // return false; + // } + + // if (curve.data.wave.stop > 1000000) { + // QMessageBox::warning(this, tr("Validation Error"), + // tr("Curve '%1' stop value is too large. Please ensure it does not exceed 1000000.").arg(curve.name)); + // return false; + // } + // } + // else { + // // Report type validation - ensure x and y values are reasonable + // if (curve.data.report.x < -1000000 || curve.data.report.x > 1000000) { + // QMessageBox::warning(this, tr("Validation Error"), + // tr("Curve '%1' X value is out of range. Please ensure it is between -1000000 and 1000000.").arg(curve.name)); + // return false; + // } + + // if (curve.data.report.y < -1000000 || curve.data.report.y > 1000000) { + // QMessageBox::warning(this, tr("Validation Error"), + // tr("Curve '%1' Y value is out of range. Please ensure it is between -1000000 and 1000000.").arg(curve.name)); + // return false; + // } + // } + //} + + //// Chart properties validation + //if (ui->chartNameEdit->text().isEmpty()) { + // QMessageBox::warning(this, tr("Validation Error"), tr("Chart name cannot be empty.")); + // return false; + //} + + //if (ui->chartNameEdit->text().length() > 100) { + // QMessageBox::warning(this, tr("Validation Error"), tr("Chart name is too long. Please limit to 100 characters.")); + // return false; + //} + + //// Axis title validation + //if (ui->xTitleEdit->text().length() > 50) { + // QMessageBox::warning(this, tr("Validation Error"), tr("X axis title is too long. Please limit to 50 characters.")); + // return false; + //} + + //if (ui->yTitleEdit->text().length() > 50) { + // QMessageBox::warning(this, tr("Validation Error"), tr("Y axis title is too long. Please limit to 50 characters.")); + // return false; + //} + + //// Axis range validation + //double xMin = ui->xMinSpinBox->value(); + //double xMax = ui->xMaxSpinBox->value(); + //double yMin = ui->yMinSpinBox->value(); + //double yMax = ui->yMaxSpinBox->value(); + + //if (xMin >= xMax) { + // QMessageBox::warning(this, tr("Validation Error"), tr("X axis minimum value must be less than maximum value.")); + // return false; + //} + + //if (yMin >= yMax) { + // QMessageBox::warning(this, tr("Validation Error"), tr("Y axis minimum value must be less than maximum value.")); + // return false; + //} + + + //// Time parameter validation + //double timeParam = ui->timeParamSpinBox->value(); + //if (timeParam < 0) { + // QMessageBox::warning(this, tr("Validation Error"), tr("Time parameter cannot be negative.")); + // return false; + //} + + //// X axis tick count validation + //int xTickCount = ui->xCountSpinBox->value(); + //if (xTickCount < 2) { + // QMessageBox::warning(this, tr("Validation Error"), tr("X axis tick count must be at least 2.")); + // return false; + //} + + return true; +} + +void AddPolarFileDlg::updateFileInfo(const QString& filePath) { + QFileInfo fileInfo(filePath); + if (fileInfo.exists()) { + ui->fileNameValue->setText(fileInfo.fileName()); + qint64 size = fileInfo.size(); + QString sizeText; + if (size < 1024) { + sizeText = QString("%1 B").arg(size); + } + else if (size < 1024 * 1024) { + sizeText = QString("%1 KB").arg(size / 1024.0, 0, 'f', 1); + } + else { + sizeText = QString("%1 MB").arg(size / (1024.0 * 1024.0), 0, 'f', 1); + } + ui->fileSizeValue->setText(sizeText); + } + else { + ui->fileNameValue->setText("-"); + ui->fileSizeValue->setText("-"); + } + + ui->filePathEdit->setText(filePath); +} \ No newline at end of file diff --git a/src/ui/WorkSpace/AddPolarFileDlg.h b/src/ui/WorkSpace/AddPolarFileDlg.h new file mode 100644 index 00000000..e12fa42a --- /dev/null +++ b/src/ui/WorkSpace/AddPolarFileDlg.h @@ -0,0 +1,42 @@ +#pragma once + +#include "BaseAddFileDlg.h" +#include "workspace/FileEntry.h" + +QT_BEGIN_NAMESPACE +class QComboBox; +class QLineEdit; +class QSpinBox; +class QDoubleSpinBox; +class QListWidget; +class QPushButton; +class QLabel; +QT_END_NAMESPACE + +namespace Ui { + class AddPolarDlg; +} + +class AddPolarFileDlg : public BaseAddFileDlg +{ + Q_OBJECT + +public: + explicit AddPolarFileDlg(QWidget* parent = nullptr); + ~AddPolarFileDlg() override; + +protected: + QString getFileFilter() const override; + QString getDialogTitle() const override; + bool validateSpecificParams() override; + void updateFileInfo(const QString& filePath) override; + +private: + Ui::AddPolarDlg* ui; + int currentCurveIndex_; + QColor selectedColor_; + + FileEntryPolar::ChartProperties chartProperties_; + FileEntryPolar::LineProperties curves_; +}; + diff --git a/src/ui/WorkSpace/AddSurfaceFileDlg.cpp b/src/ui/WorkSpace/AddSurfaceFileDlg.cpp index f86100cd..b62842e6 100644 --- a/src/ui/WorkSpace/AddSurfaceFileDlg.cpp +++ b/src/ui/WorkSpace/AddSurfaceFileDlg.cpp @@ -23,19 +23,19 @@ AddSurfaceFileDlg::AddSurfaceFileDlg(QWidget *parent) setupConnections(); // Initialize chart properties with default values - chartProperties_.xCount = 100; - chartProperties_.yCount = 100; - chartProperties_.zCount = 100; + chartProperties_.xCount = 7; + chartProperties_.yCount = 0; + chartProperties_.zCount = 7; chartProperties_.xMin = 0.0; - chartProperties_.xMax = 1.0; + chartProperties_.xMax = 14000; chartProperties_.yMin = 0.0; - chartProperties_.yMax = 1.0; + chartProperties_.yMax = 0.0; chartProperties_.zMin = 0.0; - chartProperties_.zMax = 1.0; + chartProperties_.zMax = 70; chartProperties_.timeParam = 0.0; - chartProperties_.xTitle = "X Axis"; - chartProperties_.yTitle = "Y Axis"; - chartProperties_.zTitle = "Z Axis"; + chartProperties_.xTitle = "Y Axis"; + chartProperties_.yTitle = "Z Axis"; + chartProperties_.zTitle = "X Axis"; // Set default UI values ui->xCountSpinBox->setValue(chartProperties_.xCount); @@ -57,6 +57,12 @@ AddSurfaceFileDlg::AddSurfaceFileDlg(QWidget *parent) // Clear surface properties initially clearSurfaceProperties(); + + ui->dataFormatGroupBox->setVisible(false); + + ui->comboBox_x->setCurrentText("y"); + ui->comboBox_y->setCurrentText("z"); + ui->comboBox_z->setCurrentText("x"); } AddSurfaceFileDlg::~AddSurfaceFileDlg() @@ -81,6 +87,10 @@ void AddSurfaceFileDlg::setupConnections() connect(ui->surfaceNameLineEdit, &QLineEdit::textChanged, this, &AddSurfaceFileDlg::onSurfaceNameChanged); connect(ui->surfaceStartSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &AddSurfaceFileDlg::onSurfaceDataChanged); connect(ui->surfaceStopSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &AddSurfaceFileDlg::onSurfaceDataChanged); + + connect(ui->comboBox_x, QOverload::of(&QComboBox::currentTextChanged), this, &AddSurfaceFileDlg::onSurfaceDataChanged); + connect(ui->comboBox_y, QOverload::of(&QComboBox::currentTextChanged), this, &AddSurfaceFileDlg::onSurfaceDataChanged); + connect(ui->comboBox_z, QOverload::of(&QComboBox::currentTextChanged), this, &AddSurfaceFileDlg::onSurfaceDataChanged); // Chart properties connections (save to member variables when changed) connect(ui->xCountSpinBox, QOverload::of(&QSpinBox::valueChanged), [this](int value) { chartProperties_.xCount = value; }); @@ -92,7 +102,7 @@ void AddSurfaceFileDlg::setupConnections() connect(ui->yMaxSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), [this](double value) { chartProperties_.yMax = value; }); connect(ui->zMinSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), [this](double value) { chartProperties_.zMin = value; }); connect(ui->zMaxSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), [this](double value) { chartProperties_.zMax = value; }); - connect(ui->timeParamSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), [this](double value) { chartProperties_.timeParam = value; }); + connect(ui->timeParamSpinBox, QOverload::of(&QSpinBox::valueChanged), [this](int value) { chartProperties_.timeParam = value; }); connect(ui->xTitleLineEdit, &QLineEdit::textChanged, [this](const QString& text) { chartProperties_.xTitle = text; }); connect(ui->yTitleLineEdit, &QLineEdit::textChanged, [this](const QString& text) { chartProperties_.yTitle = text; }); connect(ui->zTitleLineEdit, &QLineEdit::textChanged, [this](const QString& text) { chartProperties_.zTitle = text; }); @@ -109,7 +119,7 @@ QString AddSurfaceFileDlg::getFileFilter() const QString AddSurfaceFileDlg::getDialogTitle() const { - return tr("Select Surface Data File"); + return tr("Add Surface Dialog"); } bool AddSurfaceFileDlg::validateSpecificParams() @@ -176,10 +186,10 @@ void AddSurfaceFileDlg::onAddSurfaceClicked() surface.name = generateSurfaceName(); surface.color = generateSurfaceColor(); surface.start = 0; - surface.stop = 1000; - surface.x = QString::number(ui->xColumnSpinBox->value()); - surface.y = QString::number(ui->yColumnSpinBox->value()); - surface.z = QString::number(ui->zColumnSpinBox->value()); + surface.stop = 0; + surface.x = ui->comboBox_x->currentText(); + surface.y = ui->comboBox_y->currentText(); + surface.z = ui->comboBox_z->currentText(); surfaces_.append(surface); addSurfaceToList(surface); @@ -249,6 +259,10 @@ void AddSurfaceFileDlg::onSurfaceDataChanged() if (currentSurfaceIndex_ >= 0 && currentSurfaceIndex_ < surfaces_.size()) { surfaces_[currentSurfaceIndex_].start = ui->surfaceStartSpinBox->value(); surfaces_[currentSurfaceIndex_].stop = ui->surfaceStopSpinBox->value(); + + surfaces_[currentSurfaceIndex_].x = ui->comboBox_x->currentText(); + surfaces_[currentSurfaceIndex_].y = ui->comboBox_y->currentText(); + surfaces_[currentSurfaceIndex_].z = ui->comboBox_z->currentText(); } } @@ -280,6 +294,10 @@ void AddSurfaceFileDlg::updateSurfaceProperties() ui->surfaceNameLineEdit->setText(surface.name); ui->surfaceStartSpinBox->setValue(surface.start); ui->surfaceStopSpinBox->setValue(surface.stop); + + ui->comboBox_x->setCurrentText(surface.x); + ui->comboBox_y->setCurrentText(surface.y); + ui->comboBox_z->setCurrentText(surface.z); selectedColor_ = surface.color; updateColorPreview(ui->surfaceColorButton, selectedColor_); @@ -304,8 +322,8 @@ void AddSurfaceFileDlg::clearSurfaceProperties() { ui->surfaceNameLineEdit->clear(); ui->surfaceStartSpinBox->setValue(0); - ui->surfaceStopSpinBox->setValue(1000); - selectedColor_ = Qt::blue; + ui->surfaceStopSpinBox->setValue(0); + selectedColor_ = QColor(61, 38, 168); updateColorPreview(ui->surfaceColorButton, selectedColor_); ui->surfacePropertiesGroupBox->setEnabled(false); } @@ -318,7 +336,7 @@ QString AddSurfaceFileDlg::generateSurfaceName() const QColor AddSurfaceFileDlg::generateSurfaceColor() const { // Generate different colors for each surface - static const QColor colors[] = { + static const QColor colors[] = { QColor(61, 38, 168), Qt::blue, Qt::red, Qt::green, Qt::magenta, Qt::cyan, Qt::yellow, Qt::darkBlue, Qt::darkRed, Qt::darkGreen, Qt::darkMagenta, Qt::darkCyan, Qt::darkYellow }; @@ -352,6 +370,13 @@ QString AddSurfaceFileDlg::GetDescription() const void AddSurfaceFileDlg::accept() { + QString sName = ui->NameLineEdit->text(); + // Validate table-specific parameters + if (sName.isEmpty()) { + QMessageBox::warning(this, tr("Warning"), tr("Please enter a Surface name.")); + return; + } + if (!validateSpecificParams()) { return; } @@ -366,6 +391,8 @@ void AddSurfaceFileDlg::accept() return; } + fileEntry->SetName(sName); + // Set chart properties fileEntry->SetChartProperties(chartProperties_); diff --git a/src/ui/WorkSpace/AddSurfaceFileDlg.ui b/src/ui/WorkSpace/AddSurfaceFileDlg.ui index 5a91dc25..4ed7c18d 100644 --- a/src/ui/WorkSpace/AddSurfaceFileDlg.ui +++ b/src/ui/WorkSpace/AddSurfaceFileDlg.ui @@ -6,8 +6,8 @@ 0 0 - 800 - 726 + 794 + 748 @@ -22,6 +22,12 @@ + + + 0 + 25 + + Select surface data file... @@ -29,6 +35,12 @@ + + + 0 + 0 + + Browse... @@ -42,68 +54,122 @@ Chart Properties - - + + + + + 0 + 25 + + X Axis Title: - - + + + + + 0 + 25 + + + - + + + + 0 + 25 + + Y Axis Title: - - + + + + + 0 + 25 + + + - + + + + 0 + 25 + + Z Axis Title: - - + + + + + 0 + 25 + + + - + + + + 0 + 25 + + Time Parameter: - - - - 6 - - - -999999.000000000000000 - - - 999999.000000000000000 + + + + + 0 + 25 + - + + + + 0 + 25 + + X Range: - + + + + 0 + 25 + + 6 @@ -117,6 +183,12 @@ + + + 0 + 25 + + to @@ -124,70 +196,12 @@ - - 6 + + + 0 + 25 + - - -999999.000000000000000 - - - 999999.000000000000000 - - - - - - - - - X Count: - - - - - - - 1 - - - 10000 - - - 100 - - - - - - - Y Range: - - - - - - - - - 6 - - - -999999.000000000000000 - - - 999999.000000000000000 - - - - - - - to - - - - - 6 @@ -202,14 +216,26 @@ - + + + + 0 + 25 + + - Y Count: + X Count: - + + + + 0 + 25 + + 1 @@ -222,16 +248,28 @@ - + + + + 0 + 25 + + - Z Range: + Y Range: - + - + + + + 0 + 25 + + 6 @@ -244,14 +282,26 @@ - + + + + 0 + 25 + + to - + + + + 0 + 25 + + 6 @@ -266,14 +316,129 @@ + + + + 0 + 25 + + + + Y Count: + + + + + + + + 0 + 25 + + + + 0 + + + 10000 + + + QAbstractSpinBox::DefaultStepType + + + 1 + + + + + + + + 0 + 25 + + + + Z Range: + + + + + + + + + + 0 + 25 + + + + 6 + + + -999999.000000000000000 + + + 999999.000000000000000 + + + + + + + + 0 + 25 + + + + to + + + + + + + + 0 + 25 + + + + 6 + + + -999999.000000000000000 + + + 999999.000000000000000 + + + + + + + + + 0 + 25 + + Z Count: - + + + + 0 + 25 + + 1 @@ -285,6 +450,33 @@ + + + + + + + 100 + 25 + + + + Name: + + + + + + + + 0 + 25 + + + + + + @@ -298,6 +490,12 @@ + + + 0 + 0 + + Add Surface @@ -315,6 +513,12 @@ + + + 0 + 0 + + Remove @@ -327,19 +531,38 @@ Surface Properties - + + + + 0 + 25 + + Name: - + + + + 0 + 25 + + + + + + 0 + 25 + + Color: @@ -360,6 +583,12 @@ + + + 0 + 25 + + Start Point: @@ -367,6 +596,12 @@ + + + 0 + 25 + + 0 @@ -377,6 +612,12 @@ + + + 0 + 25 + + End Point: @@ -384,6 +625,12 @@ + + + 0 + 25 + + 0 @@ -392,6 +639,120 @@ + + + + + 0 + 25 + + + + X-Map: + + + + + + + + 0 + 25 + + + + + x + + + + + y + + + + + z + + + + + + + + + 0 + 25 + + + + Y-Map: + + + + + + + + 0 + 25 + + + + + x + + + + + y + + + + + z + + + + + + + + + 0 + 25 + + + + Z-Map: + + + + + + + + 0 + 25 + + + + + x + + + + + y + + + + + z + + + + @@ -575,4 +936,4 @@ - \ No newline at end of file + diff --git a/src/ui/WorkSpace/AddTableFileDlg.cpp b/src/ui/WorkSpace/AddTableFileDlg.cpp index 5ed650de..571ec371 100644 --- a/src/ui/WorkSpace/AddTableFileDlg.cpp +++ b/src/ui/WorkSpace/AddTableFileDlg.cpp @@ -17,10 +17,14 @@ AddTableFileDlg::AddTableFileDlg(QWidget* parent) : BaseAddFileDlg(FileEntryType::Table, parent) , ui(new Ui::AddTableFileDlg) , m_currentCurveIndex(-1) { - + SetupUI(ui); SetTitle(getDialogTitle()); - setupConnections(); + setupConnections(); + + ui->paramsGroupBox->setVisible(false); + ui->curvesGroupBox->setVisible(false); + ui->hasHeaderCheckBox->setVisible(false); } AddTableFileDlg::~AddTableFileDlg() { @@ -298,35 +302,38 @@ bool AddTableFileDlg::validateSpecificParams() { } // Validate curves - if (m_curves.isEmpty()) { - QMessageBox::warning(this, tr("Warning"), - tr("Please add at least one curve.")); - return false; - } + //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; - } - } + if (m_curves.size() > 0) + { + // 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; } @@ -345,13 +352,13 @@ QString AddTableFileDlg::getSelectedFilePath() const { void AddTableFileDlg::accept() { + QString tableName = ui->tableNameEdit->text(); // Validate table-specific parameters - if (ui->tableNameEdit->text().isEmpty()) { + if (tableName.isEmpty()) { QMessageBox::warning(this, tr("Warning"), tr("Please enter a table name.")); return; } - // Create FileEntryTable using factory function auto fileEntry = CreateFileEntryTable(getSelectedFilePath()); if (!fileEntry) { @@ -369,6 +376,8 @@ void AddTableFileDlg::accept() //fileEntry->SetTableHeaders(headers); } + fileEntry->SetName(tableName); + // Set chart properties FileEntryTable::ChartProperties chartProps; chartProps.headerString = headersText; diff --git a/src/ui/WorkSpace/AddTableFileDlg.ui b/src/ui/WorkSpace/AddTableFileDlg.ui index e1db65b6..d05c4d1e 100644 --- a/src/ui/WorkSpace/AddTableFileDlg.ui +++ b/src/ui/WorkSpace/AddTableFileDlg.ui @@ -6,8 +6,8 @@ 0 0 - 676 - 600 + 667 + 533 @@ -29,6 +29,12 @@ + + + 0 + 25 + + File Path: @@ -36,6 +42,12 @@ + + + 0 + 25 + + true @@ -46,6 +58,12 @@ + + + 0 + 25 + + ... @@ -53,6 +71,12 @@ + + + 0 + 25 + + File Name: @@ -60,6 +84,12 @@ + + + 0 + 25 + + - @@ -67,6 +97,12 @@ + + + 0 + 25 + + File Size: @@ -74,6 +110,12 @@ + + + 0 + 25 + + - @@ -88,24 +130,14 @@ Basic Information - - - - 3 - - - 0.000000000000000 - - - 999999.000000000000000 - - - 1.000000000000000 - - - + + + 0 + 25 + + Enter table name... @@ -113,6 +145,12 @@ + + + 0 + 25 + + Table Name: @@ -120,11 +158,27 @@ + + + 0 + 25 + + Time Parameter: + + + + + 0 + 25 + + + + @@ -136,6 +190,12 @@ + + + 0 + 25 + + File has header row @@ -146,6 +206,12 @@ + + + 0 + 25 + + Headers (comma-separated): @@ -153,6 +219,12 @@ + + + 0 + 25 + + e.g., Time, Value1, Value2, Value3... @@ -160,6 +232,12 @@ + + + 0 + 25 + + color: gray; font-size: 11px; @@ -431,6 +509,12 @@ + + + 100 + 30 + + Add File @@ -441,6 +525,12 @@ + + + 100 + 30 + + Cancel diff --git a/src/ui/WorkSpace/WorkSpaceDlg.ui b/src/ui/WorkSpace/WorkSpaceDlg.ui index ed2de71d..2e343f6a 100644 --- a/src/ui/WorkSpace/WorkSpaceDlg.ui +++ b/src/ui/WorkSpace/WorkSpaceDlg.ui @@ -6,7 +6,7 @@ 0 0 - 528 + 632 418 @@ -18,6 +18,12 @@ + + + 80 + 25 + + Name @@ -25,6 +31,12 @@ + + + 0 + 25 + + @@ -39,6 +51,12 @@ + + + 80 + 25 + + Path @@ -46,6 +64,12 @@ + + + 0 + 25 + + @@ -59,6 +83,12 @@ + + + 0 + 25 + + ... @@ -70,6 +100,12 @@ + + + 80 + 25 + + commond Path @@ -77,6 +113,12 @@ + + + 0 + 25 + + @@ -90,6 +132,12 @@ + + + 0 + 25 + + ... @@ -101,6 +149,12 @@ + + + 0 + 25 + + describe @@ -128,6 +182,12 @@ + + + 100 + 30 + + Sure @@ -135,6 +195,12 @@ + + + 100 + 30 + + Cancel diff --git a/src/workspace/FileEntry.cpp b/src/workspace/FileEntry.cpp index b5d39a93..fdcd7578 100644 --- a/src/workspace/FileEntry.cpp +++ b/src/workspace/FileEntry.cpp @@ -44,6 +44,8 @@ std::shared_ptr CreateFileEntry(FileEntryType type, const QString& fi return CreateFileEntryTable(filePath); case FileEntryType::Light: return CreateFileEntryLight(filePath); + case FileEntryType::Polar: + return CreateFileEntryPolar(filePath); default: LOG_ERROR("Unknown FileEntryType: {}", static_cast(type)); return nullptr; @@ -106,6 +108,20 @@ std::shared_ptr CreateFileEntryLight(const QString& filePath) { return fileEntry; } +std::shared_ptr CreateFileEntryPolar(const QString& filePath) { + QFileInfo fileInfo(filePath); + if (!fileInfo.exists()) { + LOG_ERROR("File does not exist: {}", filePath.toUtf8().constData()); + return nullptr; + } + + auto fileEntry = std::make_shared(); + fileEntry->SetPath(filePath); + fileEntry->SetName(fileInfo.baseName()); // Use base name as default display name + + return fileEntry; +} + // Factory functions for creating empty FileEntry objects (for XML parsing) std::shared_ptr CreateEmptyFileEntry(FileEntryType type) { switch (type) { @@ -117,6 +133,8 @@ std::shared_ptr CreateEmptyFileEntry(FileEntryType type) { return CreateEmptyFileEntryTable(); case FileEntryType::Light: return CreateEmptyFileEntryLight(); + case FileEntryType::Polar: + return CreateEmptyFileEntryPolar(); default: LOG_ERROR("Unknown FileEntryType: {}", static_cast(type)); return nullptr; @@ -147,6 +165,12 @@ std::shared_ptr CreateEmptyFileEntryLight() { return fileEntry; } +std::shared_ptr CreateEmptyFileEntryPolar() { + auto fileEntry = std::make_shared(); + // Don't set path or name - these will be set during XML parsing + return fileEntry; +} + // FileEntrySurface method implementations void FileEntrySurface::SetChartProperties(const ChartProperties& properties) { chartProperties_ = properties; @@ -649,3 +673,134 @@ bool FileEntryCurve::ParseFiles(const tinyxml2::XMLElement* chartElement) { return true; } + + +FileEntryPolar* FileEntryPolar::AsPolar() { + return this; +} + +void FileEntryPolar::SetChartProperties(const ChartProperties& properties) { + chartProperties_ = properties; +} + +const FileEntryPolar::ChartProperties& FileEntryPolar::GetChartProperties() const { + return chartProperties_; +} + +void FileEntryPolar::AddLineProperty(const LineProperty& line) { + lineProperties_.append(line); +} + +void FileEntryPolar::RemoveLineProperty(int index) { + if (index >= 0 && index < lineProperties_.size()) { + lineProperties_.removeAt(index); + } +} + +void FileEntryPolar::SetLineProperty(int index, const LineProperty& line) { + if (index >= 0 && index < lineProperties_.size()) { + lineProperties_[index] = line; + } +} + +const FileEntryPolar::LineProperties& FileEntryPolar::GetLineProperties() const { + return lineProperties_; +} + +bool FileEntryPolar::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("AngularCount", chartProperties_.AngularCount); + chartElement->SetAttribute("RadialCount", chartProperties_.RadialCount); + chartElement->SetAttribute("AngularTitle", chartProperties_.AngularTitle.toUtf8().constData()); + chartElement->SetAttribute("RadialTitle", chartProperties_.RadialTitle.toUtf8().constData()); + chartElement->SetAttribute("AngularMin", chartProperties_.AngularMin); + chartElement->SetAttribute("AngularMax", chartProperties_.AngularMax); + chartElement->SetAttribute("RadialMin", chartProperties_.RadialMin); + chartElement->SetAttribute("RadialMax", chartProperties_.RadialMax); + chartElement->SetAttribute("AngularUnit", chartProperties_.AngularUnit.toUtf8().constData()); + chartElement->SetAttribute("RadialUnit", chartProperties_.RadialUnit.toUtf8().constData()); + chartElement->SetAttribute("t", chartProperties_.timeParam); + + // 为每个CurveProperty创建元素 + for (const auto& curve : lineProperties_) { + tinyxml2::XMLElement* curveElement = doc->NewElement("curve"); + chartElement->InsertEndChild(curveElement); + + curveElement->SetAttribute("name", curve.name.toUtf8().constData()); + curveElement->SetAttribute("color", QColorToString(curve.color).toUtf8().constData()); + curveElement->SetAttribute("Angular", curve.Angular); + curveElement->SetAttribute("Radial", curve.Radial); + } + + return true; +} + +bool FileEntryPolar::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(); + } + + chartProperties_.AngularCount = chartElement->IntAttribute("AngularCount", 0); + chartProperties_.RadialCount = chartElement->IntAttribute("RadialCount", 0); + + const char* AngularTitleAttr = chartElement->Attribute("AngularTitle"); + const char* RadialTitleAttr = chartElement->Attribute("RadialTitle"); + if (AngularTitleAttr) chartProperties_.AngularTitle = QString::fromUtf8(AngularTitleAttr); + if (RadialTitleAttr) chartProperties_.RadialTitle = QString::fromUtf8(RadialTitleAttr); + + chartProperties_.AngularMin = chartElement->DoubleAttribute("AngularMin", 0.0); + chartProperties_.AngularMax = chartElement->DoubleAttribute("AngularMax", 0.0); + chartProperties_.RadialMin = chartElement->DoubleAttribute("RadialMin", 0.0); + chartProperties_.RadialMax = chartElement->DoubleAttribute("RadialMax", 0.0); + + const char* AngularUnitAttr = chartElement->Attribute("AngularUnit"); + const char* RadialUnitAttr = chartElement->Attribute("RadialUnit"); + if (AngularUnitAttr) chartProperties_.AngularUnit = QString::fromUtf8(AngularUnitAttr); + if (RadialUnitAttr) chartProperties_.RadialUnit = QString::fromUtf8(RadialUnitAttr); + + chartProperties_.timeParam = chartElement->DoubleAttribute("t", 0.0); + + // 解析所有元素 + lineProperties_.clear(); + for (const tinyxml2::XMLElement* curveElement = chartElement->FirstChildElement("curve"); + curveElement != nullptr; + curveElement = curveElement->NextSiblingElement("curve")) { + + LineProperty prop; + + const char* curveNameAttr = curveElement->Attribute("name"); + const char* colorAttr = curveElement->Attribute("color"); + if (curveNameAttr) prop.name = QString::fromUtf8(curveNameAttr); + if (colorAttr) prop.color = StringToQColor(QString::fromUtf8(colorAttr)); + + prop.Angular = curveElement->IntAttribute("Angular", 0.0); + prop.Radial = curveElement->IntAttribute("Radial", 0.0); + + lineProperties_.append(prop); + } + + return true; +} \ No newline at end of file diff --git a/src/workspace/FileEntry.h b/src/workspace/FileEntry.h index 2f1efbbb..4b59b42e 100644 --- a/src/workspace/FileEntry.h +++ b/src/workspace/FileEntry.h @@ -8,7 +8,8 @@ enum class FileEntryType { Curve, Surface, Table, - Light + Light, + Polar }; enum class ChartType { @@ -37,6 +38,7 @@ inline const char* FileEntryTypeToString(FileEntryType t) { case FileEntryType::Surface: return "surface"; case FileEntryType::Table: return "table"; case FileEntryType::Light: return "light"; + case FileEntryType::Polar: return "polar"; } return "unknown"; } @@ -47,6 +49,7 @@ inline bool FileEntryTypeFromString(const char* s, FileEntryType& out) { if (0 == strcmp(s, "surface")) { out = FileEntryType::Surface; return true; } if (0 == strcmp(s, "table")) { out = FileEntryType::Table; return true; } if (0 == strcmp(s, "light")) { out = FileEntryType::Light; return true; } + if (0 == strcmp(s, "polar")) { out = FileEntryType::Polar; return true; } return false; } @@ -54,6 +57,7 @@ class FileEntryCurve; class FileEntryLight; class FileEntrySurface; class FileEntryTable; +class FileEntryPolar; class FileEntry { public: @@ -75,6 +79,7 @@ public: virtual FileEntryLight* AsLight() { return nullptr; } virtual FileEntrySurface* AsSurface() { return nullptr; } virtual FileEntryTable* AsTable() { return nullptr; } + virtual FileEntryPolar* AsPolar() { return nullptr; } virtual bool ParseFiles(const tinyxml2::XMLElement* element) { return false; } virtual bool SaveFiles(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc) { return false; } @@ -95,6 +100,7 @@ std::shared_ptr CreateFileEntryCurve(const QString& filePath); std::shared_ptr CreateFileEntrySurface(const QString& filePath); std::shared_ptr CreateFileEntryTable(const QString& filePath); std::shared_ptr CreateFileEntryLight(const QString& filePath); +std::shared_ptr CreateFileEntryPolar(const QString& filePath); // Factory functions for creating empty FileEntry objects (for XML parsing) std::shared_ptr CreateEmptyFileEntry(FileEntryType type); @@ -102,6 +108,7 @@ std::shared_ptr CreateEmptyFileEntryCurve(); std::shared_ptr CreateEmptyFileEntrySurface(); std::shared_ptr CreateEmptyFileEntryTable(); std::shared_ptr CreateEmptyFileEntryLight(); +std::shared_ptr CreateEmptyFileEntryPolar(); class FileEntryCurve : public FileEntry { @@ -295,3 +302,52 @@ private: LightRowProperties lightProperties_; }; +class FileEntryPolar : public FileEntry { +public: + struct ChartProperties { + int AngularCount; + int RadialCount; + QString AngularTitle; + QString RadialTitle; + double AngularMin; + double AngularMax; + double RadialMin; + double RadialMax; + QString AngularUnit; + QString RadialUnit; + double timeParam; // 对应XML的t + }; + + struct LineProperty { + QString name; + QColor color; + int Angular; + int Radial; + }; + + using LineProperties = QList; + +public: + FileEntryPolar() { type_ = FileEntryType::Polar; } + + // Chart properties management + void SetChartProperties(const ChartProperties& properties); + const ChartProperties& GetChartProperties() const; + + // Line properties management + void AddLineProperty(const LineProperty& line); + void RemoveLineProperty(int index); + void SetLineProperty(int index, const LineProperty& line); + const LineProperties& GetLineProperties() const; + + // Type conversion + FileEntryPolar* AsPolar() override; + + // XML处理方法 + bool SaveFiles(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc) override; + bool ParseFiles(const tinyxml2::XMLElement* element) override; + +private: + ChartProperties chartProperties_; + LineProperties lineProperties_; +}; \ No newline at end of file diff --git a/src/workspace/WorkSpace.cpp b/src/workspace/WorkSpace.cpp index ed65989c..9ec1825d 100644 --- a/src/workspace/WorkSpace.cpp +++ b/src/workspace/WorkSpace.cpp @@ -221,6 +221,9 @@ bool WorkSpace::SetFileEntryCount(FileEntryType type, int count) { case FileEntryType::Light: fileEntry = std::make_shared(); break; + case FileEntryType::Polar: + fileEntry = std::make_shared(); + break; default: return false; // Invalid type }