diff --git a/src/translations/Dyt_zh_CN.ts b/src/translations/Dyt_zh_CN.ts
index 1df46af7..7f03a00c 100644
--- a/src/translations/Dyt_zh_CN.ts
+++ b/src/translations/Dyt_zh_CN.ts
@@ -592,132 +592,132 @@
-
+
model elements
-
+
attribte
-
+
Main View
-
+
Wave Curve
-
+
Speed Curve
-
+
3D Curve
-
+
Target number
-
+
Signal-to-noise ratio
-
+
Azimuth line of sight
-
+
Pitch gaze angle
-
+
azimuth
-
+
Pitch angle
-
+
attribute
-
+
Doppler
-
+
course
-
+
Speed
-
+
longitude
-
+
latitude
-
+
distance
-
+
velocity
-
+
Radial dimensions
-
+
Target RCS
-
+
Report Table
-
+
Signal Indicator Lamp
-
+
ParamSetting
-
+
bat File
diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp
index a9dcf674..06f4419d 100644
--- a/src/ui/MainWindow.cpp
+++ b/src/ui/MainWindow.cpp
@@ -27,6 +27,9 @@
#include "Matlab/MatlabObject.h"
+// 曲线面板管理器
+#include "Panel/DataPanelManager.h"
+
#include "ui_MainWindow.h"
#include "viewer/OsgWidget.h"
#include "DockTitleBar.h"
@@ -211,6 +214,13 @@ void MainWindow::InitUI() {
// InitDockLayout();
+ // 初始化数据面板管理器
+ dataPanelManager_ = new DataPanelManager(this, this);
+
+ // 连接工作空间变化信号
+ connect(&WorkSpaceManager::Get(), &WorkSpaceManager::WorkSpaceChanged,
+ dataPanelManager_, &DataPanelManager::OnWorkspaceChanged);
+
// Restore previous UI layout if available
UiLayoutManager::Restore(this, 1);
@@ -228,6 +238,13 @@ void MainWindow::InitUI() {
void MainWindow::UninitUI() {
// Save layout state before tearing down widgets
UiLayoutManager::Save(this, 1);
+
+ // 清理数据面板管理器
+ if (dataPanelManager_) {
+ delete dataPanelManager_;
+ dataPanelManager_ = nullptr;
+ }
+
if (qtOsgViewWidget_) {
qtOsgViewWidget_->Uninitialize();
delete qtOsgViewWidget_;
diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h
index 376d73b7..aca6633c 100644
--- a/src/ui/MainWindow.h
+++ b/src/ui/MainWindow.h
@@ -8,6 +8,8 @@ namespace Ui {
class MainWindow;
}
+class DataPanelManager;
+
class MainWindow : public QMainWindow {
Q_OBJECT
@@ -35,6 +37,14 @@ public:
return surfaceDlg_;
}
+ /**
+ * @brief 获取数据面板管理器
+ * @return 数据面板管理器指针
+ */
+ DataPanelManager* GetDataPanelManager() const {
+ return dataPanelManager_;
+ }
+
void slotShowUISetting();
public slots:
@@ -62,6 +72,9 @@ private:
class CodeEdtUI* matlabFileDlg_{ nullptr };
class AddParamSetting* addParamDlg_{ nullptr };
+ // 数据面板管理器
+ DataPanelManager* dataPanelManager_{ nullptr };
+
QMap m_mapDockWidget;
};
\ No newline at end of file
diff --git a/src/ui/Panel/CurvePanel.cpp b/src/ui/Panel/CurvePanel.cpp
new file mode 100644
index 00000000..865222c8
--- /dev/null
+++ b/src/ui/Panel/CurvePanel.cpp
@@ -0,0 +1,48 @@
+#include "ui/Panel/CurvePanel.h"
+#include "ui/DockWidget.h"
+#include "common/SpdLogger.h"
+
+#include
+#include
+#include
+
+CurvePanel::CurvePanel(int index, const QString& filePath, QWidget* parent)
+ : DataPanel(index, FileEntryType::Curve, filePath, parent)
+{
+ LOG_INFO("Created CurvePanel {} for file: {}", index, filePath.toStdString());
+}
+
+CurvePanel::~CurvePanel()
+{
+ LOG_INFO("Destroyed CurvePanel {}", GetIndex());
+}
+
+void CurvePanel::RefreshPanel()
+{
+ // Implement curve-specific refresh logic here
+ // For now, just call the base class implementation
+ DataPanel::RefreshPanel();
+
+ LOG_INFO("Refreshed CurvePanel {}", GetIndex());
+}
+
+void CurvePanel::InitUI()
+{
+ // Create basic layout
+ QVBoxLayout* layout = new QVBoxLayout(this);
+
+ // Add placeholder label showing panel information
+ QLabel* infoLabel = new QLabel(QString("Curve Panel %1\nFile: %2\n\nCurve Drawing Area\nPlease inherit this class to implement specific drawing functionality")
+ .arg(GetIndex())
+ .arg(QFileInfo(GetFilePath()).fileName()));
+ infoLabel->setAlignment(Qt::AlignCenter);
+ infoLabel->setStyleSheet("QLabel { color: #666; font-size: 12px; padding: 20px; }");
+
+ layout->addWidget(infoLabel);
+ setLayout(layout);
+}
+
+QString CurvePanel::GetTypeDisplayName() const
+{
+ return "Curve";
+}
\ No newline at end of file
diff --git a/src/ui/Panel/CurvePanel.h b/src/ui/Panel/CurvePanel.h
new file mode 100644
index 00000000..1fa7e231
--- /dev/null
+++ b/src/ui/Panel/CurvePanel.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "DataPanel.h"
+
+/**
+ * @file CurvePanel.h
+ * @brief Curve Panel Class
+ * Specialized panel for curve data visualization and manipulation
+ */
+
+/**
+ * @brief Curve panel class
+ * Specialized panel for curve data, inherits from DataPanel
+ */
+class CurvePanel : public DataPanel
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief Constructor
+ * @param index Panel index
+ * @param filePath Associated file path
+ * @param parent Parent widget
+ */
+ explicit CurvePanel(int index, const QString& filePath, QWidget* parent = nullptr);
+
+ /**
+ * @brief Destructor
+ */
+ virtual ~CurvePanel();
+
+ /**
+ * @brief Get file type
+ * @return File type (always Curve for this class)
+ */
+ FileEntryType GetFileType() const override { return FileEntryType::Curve; }
+
+ /**
+ * @brief Refresh panel content
+ */
+ void RefreshPanel() override;
+
+protected:
+ /**
+ * @brief Initialize UI for curve-specific layout
+ */
+ void InitUI() override;
+
+ /**
+ * @brief Get type display name
+ * @return Display name for curve type
+ */
+ QString GetTypeDisplayName() const override;
+};
\ No newline at end of file
diff --git a/src/ui/Panel/DataPanel.cpp b/src/ui/Panel/DataPanel.cpp
new file mode 100644
index 00000000..fa90efeb
--- /dev/null
+++ b/src/ui/Panel/DataPanel.cpp
@@ -0,0 +1,75 @@
+#include "ui/Panel/DataPanel.h"
+#include "ui/DockWidget.h"
+#include "common/SpdLogger.h"
+
+#include
+#include
+#include
+#include
+
+DataPanel::DataPanel(int index, FileEntryType fileType, const QString& filePath, QWidget* parent)
+ : QWidget(parent)
+ , index_(index)
+ , fileType_(fileType)
+ , filePath_(filePath)
+ , title_()
+ , dockWidget_(nullptr)
+{
+ title_ = GenerateTitle();
+ InitUI();
+
+ LOG_INFO("Created DataPanel {} for {} file: {}", index_, FileEntryTypeToString(fileType_), filePath_.toStdString());
+}
+
+DataPanel::~DataPanel()
+{
+ LOG_INFO("Destroyed DataPanel {} ({})", index_, FileEntryTypeToString(fileType_));
+}
+
+void DataPanel::closeEvent(QCloseEvent* event)
+{
+ emit PanelClosed();
+ event->accept();
+}
+
+void DataPanel::InitUI()
+{
+ // Create basic layout
+ QVBoxLayout* layout = new QVBoxLayout(this);
+
+ // Add placeholder label showing panel information
+ QString typeDisplayName = GetTypeDisplayName();
+ QLabel* infoLabel = new QLabel(QString("Panel %1 (%2)\nFile: %3\n\n%4 Data Area\nPlease inherit this class to implement specific functionality")
+ .arg(index_)
+ .arg(typeDisplayName)
+ .arg(QFileInfo(filePath_).fileName())
+ .arg(typeDisplayName));
+ infoLabel->setAlignment(Qt::AlignCenter);
+ infoLabel->setStyleSheet("QLabel { color: #666; font-size: 12px; padding: 20px; }");
+
+ layout->addWidget(infoLabel);
+ setLayout(layout);
+}
+
+QString DataPanel::GenerateTitle()
+{
+ QFileInfo fileInfo(filePath_);
+ QString typeDisplayName = GetTypeDisplayName();
+ return QString("%1 Panel %2 - %3").arg(typeDisplayName).arg(index_).arg(fileInfo.baseName());
+}
+
+QString DataPanel::GetTypeDisplayName() const
+{
+ switch (fileType_) {
+ case FileEntryType::Curve:
+ return "Curve";
+ case FileEntryType::Surface:
+ return "Surface";
+ case FileEntryType::Table:
+ return "Table";
+ case FileEntryType::Light:
+ return "Light";
+ default:
+ return "Unknown";
+ }
+}
\ No newline at end of file
diff --git a/src/ui/Panel/DataPanel.h b/src/ui/Panel/DataPanel.h
new file mode 100644
index 00000000..611a7879
--- /dev/null
+++ b/src/ui/Panel/DataPanel.h
@@ -0,0 +1,115 @@
+#pragma once
+
+#include
+#include
+#include "workspace/FileEntry.h"
+
+class DockWidget;
+
+/**
+ * @file DataPanel.h
+ * @brief Data Panel Base Class
+ * Provides panel framework structure for different data types, specific functionality implemented by derived classes
+ */
+
+/**
+ * @brief Data panel base class
+ * Provides panel framework structure for different data types, specific functionality implemented by derived classes
+ */
+class DataPanel : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief Constructor
+ * @param index Panel index
+ * @param fileType File type
+ * @param filePath Associated file path
+ * @param parent Parent widget
+ */
+ explicit DataPanel(int index, FileEntryType fileType, const QString& filePath, QWidget* parent = nullptr);
+
+ /**
+ * @brief Destructor
+ */
+ virtual ~DataPanel();
+
+ /**
+ * @brief Get panel index
+ * @return Panel index
+ */
+ int GetIndex() const { return index_; }
+
+ /**
+ * @brief Get file type (virtual function, implemented by derived classes)
+ * @return File type
+ */
+ virtual FileEntryType GetFileType() const { return fileType_; }
+
+ /**
+ * @brief Get file path
+ * @return File path
+ */
+ QString GetFilePath() const { return filePath_; }
+
+ /**
+ * @brief Get panel title
+ * @return Panel title
+ */
+ QString GetTitle() const { return title_; }
+
+ /**
+ * @brief Set dock widget reference
+ * @param dockWidget Dock widget pointer
+ */
+ void SetDockWidget(DockWidget* dockWidget) { dockWidget_ = dockWidget; }
+
+ /**
+ * @brief Get dock widget reference
+ * @return Dock widget pointer
+ */
+ DockWidget* GetDockWidget() const { return dockWidget_; }
+
+ /**
+ * @brief Refresh panel content (virtual function, implemented by derived classes)
+ */
+ virtual void RefreshPanel() {}
+
+signals:
+ /**
+ * @brief Panel close signal
+ */
+ void PanelClosed();
+
+protected:
+ /**
+ * @brief Close event handler
+ * @param event Close event
+ */
+ void closeEvent(QCloseEvent* event) override;
+
+ /**
+ * @brief Initialize UI (virtual function, derived classes implement specific layout)
+ */
+ virtual void InitUI();
+
+ /**
+ * @brief Generate panel title
+ * @return Generated title
+ */
+ virtual QString GenerateTitle();
+
+ /**
+ * @brief Get type display name (virtual function, implemented by derived classes)
+ * @return Display name for the file type
+ */
+ virtual QString GetTypeDisplayName() const;
+
+private:
+ int index_; // Panel index
+ FileEntryType fileType_; // File type
+ QString filePath_; // Associated file path
+ QString title_; // Panel title
+ DockWidget* dockWidget_; // Dock widget reference
+};
\ No newline at end of file
diff --git a/src/ui/Panel/DataPanelFactory.cpp b/src/ui/Panel/DataPanelFactory.cpp
new file mode 100644
index 00000000..2e59f7a7
--- /dev/null
+++ b/src/ui/Panel/DataPanelFactory.cpp
@@ -0,0 +1,67 @@
+#include "DataPanelFactory.h"
+#include "DataPanel.h"
+#include "CurvePanel.h"
+#include "common/SpdLogger.h"
+
+// Forward declarations for future panel types
+// #include "SurfacePanel.h"
+// #include "TablePanel.h"
+// #include "LightPanel.h"
+
+DataPanel* DataPanelFactory::CreatePanel(int index, FileEntryType fileType, const QString& filePath, QWidget* parent)
+{
+ switch (fileType) {
+ case FileEntryType::Curve:
+ // For now, create CurvePanel which should inherit from DataPanel
+ // TODO: Update CurvePanel to inherit from DataPanel
+ return new CurvePanel(index, filePath, parent);
+
+ case FileEntryType::Surface:
+ // TODO: Implement SurfacePanel
+ LOG_WARN("SurfacePanel not implemented yet, creating base DataPanel");
+ return new DataPanel(index, fileType, filePath, parent);
+
+ case FileEntryType::Table:
+ // TODO: Implement TablePanel
+ LOG_WARN("TablePanel not implemented yet, creating base DataPanel");
+ return new DataPanel(index, fileType, filePath, parent);
+
+ case FileEntryType::Light:
+ // TODO: Implement LightPanel
+ LOG_WARN("LightPanel not implemented yet, creating base DataPanel");
+ return new DataPanel(index, fileType, filePath, parent);
+
+ default:
+ LOG_ERROR("Unsupported file type: {}", static_cast(fileType));
+ return nullptr;
+ }
+}
+
+bool DataPanelFactory::IsTypeSupported(FileEntryType fileType)
+{
+ switch (fileType) {
+ case FileEntryType::Curve:
+ case FileEntryType::Surface:
+ case FileEntryType::Table:
+ case FileEntryType::Light:
+ return true;
+ default:
+ return false;
+ }
+}
+
+QString DataPanelFactory::GetTypeDisplayName(FileEntryType fileType)
+{
+ switch (fileType) {
+ case FileEntryType::Curve:
+ return "Curve";
+ case FileEntryType::Surface:
+ return "Surface";
+ case FileEntryType::Table:
+ return "Table";
+ case FileEntryType::Light:
+ return "Light";
+ default:
+ return "Unknown";
+ }
+}
\ No newline at end of file
diff --git a/src/ui/Panel/DataPanelFactory.h b/src/ui/Panel/DataPanelFactory.h
new file mode 100644
index 00000000..a72b631c
--- /dev/null
+++ b/src/ui/Panel/DataPanelFactory.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include
+#include "workspace/FileEntry.h"
+
+class DataPanel;
+class QWidget;
+
+/**
+ * @file DataPanelFactory.h
+ * @brief Data Panel Factory
+ * Creates appropriate panel instances based on file type
+ */
+
+/**
+ * @brief Data panel factory class
+ * Creates appropriate panel instances based on file type using factory pattern
+ */
+class DataPanelFactory
+{
+public:
+ /**
+ * @brief Create panel based on file type
+ * @param index Panel index
+ * @param fileType File type
+ * @param filePath File path
+ * @param parent Parent widget
+ * @return Created panel pointer (caller takes ownership)
+ */
+ static DataPanel* CreatePanel(int index, FileEntryType fileType, const QString& filePath, QWidget* parent = nullptr);
+
+ /**
+ * @brief Check if file type is supported
+ * @param fileType File type to check
+ * @return True if supported, false otherwise
+ */
+ static bool IsTypeSupported(FileEntryType fileType);
+
+ /**
+ * @brief Get display name for file type
+ * @param fileType File type
+ * @return Display name
+ */
+ static QString GetTypeDisplayName(FileEntryType fileType);
+
+private:
+ // Private constructor to prevent instantiation
+ DataPanelFactory() = default;
+};
\ No newline at end of file
diff --git a/src/ui/Panel/DataPanelManager.cpp b/src/ui/Panel/DataPanelManager.cpp
new file mode 100644
index 00000000..8764e78b
--- /dev/null
+++ b/src/ui/Panel/DataPanelManager.cpp
@@ -0,0 +1,296 @@
+#include "DataPanelManager.h"
+#include "DataPanel.h"
+#include "DataPanelFactory.h"
+#include "ui/DockWidget.h"
+#include "ui/DockTitleBar.h"
+#include "ui/MainWindow.h"
+#include "workspace/FileEntry.h"
+#include "common/SpdLogger.h"
+
+#include
+#include
+
+const QString DataPanelManager::PANEL_OBJECT_NAME_PREFIX = "DataPanel_";
+
+DataPanelManager::DataPanelManager(MainWindow* mainWindow, QObject* parent)
+ : QObject(parent)
+ , mainWindow_(mainWindow)
+ , currentWorkspace_(nullptr)
+{
+ LOG_INFO("DataPanelManager initialized");
+}
+
+DataPanelManager::~DataPanelManager()
+{
+ ClearAllPanels();
+ LOG_INFO("DataPanelManager destroyed");
+}
+
+void DataPanelManager::SetWorkspace(WorkSpace* workspace)
+{
+ if (currentWorkspace_ == workspace) {
+ return;
+ }
+
+ // Disconnect old workspace signal connections
+ if (currentWorkspace_) {
+ disconnect(currentWorkspace_, nullptr, this, nullptr);
+ }
+
+ currentWorkspace_ = workspace;
+
+ // Connect new workspace signals
+ if (currentWorkspace_) {
+ connect(currentWorkspace_, &WorkSpace::FilesChanged, this, &DataPanelManager::OnFilesChanged);
+ }
+
+ // Update all panel types
+ if (currentWorkspace_) {
+ UpdatePanelsForType(FileEntryType::Curve);
+ UpdatePanelsForType(FileEntryType::Surface);
+ UpdatePanelsForType(FileEntryType::Table);
+ UpdatePanelsForType(FileEntryType::Light);
+ } else {
+ ClearAllPanels();
+ }
+}
+
+int DataPanelManager::GetActivePanelCount(FileEntryType fileType) const
+{
+ if (static_cast(fileType) == -1) {
+ // Return total count
+ return dataPanels_.size();
+ }
+
+ // Count panels of specific type
+ int count = 0;
+ for (auto it = dataPanels_.begin(); it != dataPanels_.end(); ++it) {
+ if (it.value()->GetFileType() == fileType) {
+ count++;
+ }
+ }
+ return count;
+}
+
+QList DataPanelManager::GetPanelsOfType(FileEntryType fileType) const
+{
+ QList panels;
+ for (auto it = dataPanels_.begin(); it != dataPanels_.end(); ++it) {
+ if (it.value()->GetFileType() == fileType) {
+ panels.append(it.value());
+ }
+ }
+ return panels;
+}
+
+void DataPanelManager::OnWorkspaceChanged(WorkSpace* workspace)
+{
+ SetWorkspace(workspace);
+}
+
+void DataPanelManager::OnFilesChanged(FileEntryType type)
+{
+ // Only respond to supported file types
+ if (!DataPanelFactory::IsTypeSupported(type)) {
+ return;
+ }
+
+ UpdatePanelsForType(type);
+}
+
+void DataPanelManager::OnPanelClosed()
+{
+ DataPanel* panel = qobject_cast(sender());
+ if (panel) {
+ RemovePanel(panel);
+ }
+}
+
+void DataPanelManager::UpdatePanelsForType(FileEntryType fileType)
+{
+ if (!currentWorkspace_) {
+ ClearPanelsOfType(fileType);
+ return;
+ }
+
+ // Get files of specified type from current workspace
+ std::vector files = currentWorkspace_->GetFileEntries(fileType);
+
+ // Limit to maximum panels per type
+ const int maxPanels = qMin(static_cast(files.size()), GetMaxPanelCount());
+
+ // Find panels to remove (excess panels of this type)
+ QStringList keysToRemove;
+ for (auto it = dataPanels_.begin(); it != dataPanels_.end(); ++it) {
+ DataPanel* panel = it.value();
+ if (panel->GetFileType() == fileType && panel->GetIndex() >= maxPanels) {
+ keysToRemove.append(it.key());
+ }
+ }
+
+ // Remove excess panels
+ for (const QString& key : keysToRemove) {
+ RemovePanel(dataPanels_[key]);
+ }
+
+ // Create or update panels
+ for (int i = 0; i < maxPanels; ++i) {
+ const FileEntry& fileEntry = files[i];
+ QString filePath = currentWorkspace_->GetFileEntryAbsPath(fileEntry.type, i);
+ QString panelKey = QString("%1_%2").arg(FileEntryTypeToString(fileType)).arg(i);
+
+ if (dataPanels_.contains(panelKey)) {
+ // Check if file path has changed
+ DataPanel* existingPanel = dataPanels_[panelKey];
+ if (existingPanel->GetFilePath() != filePath) {
+ // File path changed, recreate panel
+ RemovePanel(existingPanel);
+ CreateDataPanel(fileType, filePath);
+ } else {
+ // File path unchanged, refresh data
+ existingPanel->RefreshPanel();
+ }
+ } else {
+ // Create new panel
+ CreateDataPanel(fileType, filePath);
+ }
+ }
+}
+
+DataPanel* DataPanelManager::CreateDataPanel(FileEntryType fileType, const QString& filePath)
+{
+ if (GetActivePanelCount(fileType) >= GetMaxPanelCount()) {
+ LOG_WARN("Cannot create more {} panels, maximum count reached: {}",
+ FileEntryTypeToString(fileType), GetMaxPanelCount());
+ return nullptr;
+ }
+
+ // Find next available index for this file type
+ int index = FindNextAvailableIndex(fileType);
+ QString panelKey = QString("%1_%2").arg(FileEntryTypeToString(fileType)).arg(index);
+
+ // Create dock widget
+ DockWidget* dockWidget = new DockWidget(mainWindow_);
+ dockWidget->setObjectName(GeneratePanelObjectName(fileType, index));
+
+ // Set title bar
+ DockTitleBar* titleBar = new DockTitleBar(dockWidget);
+ dockWidget->SetDockWidgetTitleBar(titleBar);
+
+ // Connect signals
+ connect(dockWidget, &DockWidget::dockLocationChanged, this, [this](Qt::DockWidgetArea area) {
+ // Handle dock location changes if needed
+ });
+
+ // Add to main window
+ mainWindow_->addDockWidget(Qt::RightDockWidgetArea, dockWidget);
+
+ // Create panel using factory
+ DataPanel* panel = DataPanelFactory::CreatePanel(index, fileType, filePath, dockWidget);
+ if (!panel) {
+ LOG_ERROR("Failed to create panel for type: {}", FileEntryTypeToString(fileType));
+ dockWidget->deleteLater();
+ return nullptr;
+ }
+
+ dockWidget->setWidget(panel);
+
+ // Set panel's dock widget reference
+ panel->SetDockWidget(dockWidget);
+
+ // Connect panel signals
+ connect(panel, &DataPanel::PanelClosed, this, &DataPanelManager::OnPanelClosed);
+
+ // Save references
+ dataPanels_[panelKey] = panel;
+ dockWidgets_[panelKey] = dockWidget;
+
+ LOG_INFO("Created {} panel {} for file: {}",
+ FileEntryTypeToString(fileType), index, filePath.toStdString());
+ return panel;
+}
+
+void DataPanelManager::RemovePanel(DataPanel* panel)
+{
+ if (!panel) {
+ return;
+ }
+
+ // Find panel key
+ QString panelKey;
+ for (auto it = dataPanels_.begin(); it != dataPanels_.end(); ++it) {
+ if (it.value() == panel) {
+ panelKey = it.key();
+ break;
+ }
+ }
+
+ if (panelKey.isEmpty()) {
+ return;
+ }
+
+ // Remove dock widget
+ if (dockWidgets_.contains(panelKey)) {
+ DockWidget* dockWidget = dockWidgets_[panelKey];
+ mainWindow_->removeDockWidget(dockWidget);
+ dockWidget->deleteLater();
+ }
+
+ // Remove from mappings
+ dataPanels_.remove(panelKey);
+ dockWidgets_.remove(panelKey);
+
+ // Delete panel
+ panel->deleteLater();
+
+ LOG_INFO("Removed {} panel {}",
+ FileEntryTypeToString(panel->GetFileType()), panel->GetIndex());
+}
+
+void DataPanelManager::ClearAllPanels()
+{
+ // Get copy of all keys
+ QStringList keys = dataPanels_.keys();
+
+ // Remove panels one by one
+ for (const QString& key : keys) {
+ RemovePanel(dataPanels_[key]);
+ }
+
+ LOG_INFO("Cleared all data panels");
+}
+
+void DataPanelManager::ClearPanelsOfType(FileEntryType fileType)
+{
+ QStringList keysToRemove;
+ for (auto it = dataPanels_.begin(); it != dataPanels_.end(); ++it) {
+ if (it.value()->GetFileType() == fileType) {
+ keysToRemove.append(it.key());
+ }
+ }
+
+ for (const QString& key : keysToRemove) {
+ RemovePanel(dataPanels_[key]);
+ }
+
+ LOG_INFO("Cleared all {} panels", FileEntryTypeToString(fileType));
+}
+
+QString DataPanelManager::GeneratePanelObjectName(FileEntryType fileType, int index) const
+{
+ return QString("%1%2_%3").arg(PANEL_OBJECT_NAME_PREFIX)
+ .arg(FileEntryTypeToString(fileType))
+ .arg(index);
+}
+
+int DataPanelManager::FindNextAvailableIndex(FileEntryType fileType) const
+{
+ int index = 0;
+ QString baseKey = QString("%1_").arg(FileEntryTypeToString(fileType));
+
+ while (dataPanels_.contains(baseKey + QString::number(index))) {
+ index++;
+ }
+
+ return index;
+}
\ No newline at end of file
diff --git a/src/ui/Panel/DataPanelManager.h b/src/ui/Panel/DataPanelManager.h
new file mode 100644
index 00000000..084f4a33
--- /dev/null
+++ b/src/ui/Panel/DataPanelManager.h
@@ -0,0 +1,125 @@
+#pragma once
+
+#include
+#include
+#include
+#include "workspace/WorkSpace.h"
+#include "workspace/FileEntry.h"
+
+class DockWidget;
+class DataPanel;
+class MainWindow;
+
+/**
+ * @file DataPanelManager.h
+ * @brief Data Panel Manager
+ * Manages data panels created based on workspace files, supports multiple file types
+ */
+
+class DataPanelManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit DataPanelManager(MainWindow* mainWindow, QObject* parent = nullptr);
+ ~DataPanelManager();
+
+ /**
+ * @brief Set current workspace
+ * @param workspace Workspace pointer
+ */
+ void SetWorkspace(WorkSpace* workspace);
+
+ /**
+ * @brief Get current active panel count for specific type
+ * @param fileType File type (if not specified, returns total count)
+ * @return Panel count
+ */
+ int GetActivePanelCount(FileEntryType fileType = static_cast(-1)) const;
+
+ /**
+ * @brief Get maximum panel count per type
+ * @return Maximum panel count
+ */
+ int GetMaxPanelCount() const { return 9; }
+
+ /**
+ * @brief Get panels of specific type
+ * @param fileType File type
+ * @return List of panels for the specified type
+ */
+ QList GetPanelsOfType(FileEntryType fileType) const;
+
+public slots:
+ /**
+ * @brief Handle workspace changes
+ * @param workspace New workspace
+ */
+ void OnWorkspaceChanged(WorkSpace* workspace);
+
+ /**
+ * @brief Handle file changes
+ * @param type File type
+ */
+ void OnFilesChanged(FileEntryType type);
+
+ /**
+ * @brief Handle panel close event
+ */
+ void OnPanelClosed();
+
+private:
+ /**
+ * @brief Update panels for specific file type
+ * @param fileType File type to update
+ */
+ void UpdatePanelsForType(FileEntryType fileType);
+
+ /**
+ * @brief Create new data panel
+ * @param fileType File type
+ * @param filePath File path
+ * @return Created panel pointer
+ */
+ DataPanel* CreateDataPanel(FileEntryType fileType, const QString& filePath);
+
+ /**
+ * @brief Remove panel
+ * @param panel Panel pointer
+ */
+ void RemovePanel(DataPanel* panel);
+
+ /**
+ * @brief Clear all panels
+ */
+ void ClearAllPanels();
+
+ /**
+ * @brief Clear panels of specific type
+ * @param fileType File type
+ */
+ void ClearPanelsOfType(FileEntryType fileType);
+
+ /**
+ * @brief Generate panel object name
+ * @param fileType File type
+ * @param index Panel index
+ * @return Object name
+ */
+ QString GeneratePanelObjectName(FileEntryType fileType, int index) const;
+
+ /**
+ * @brief Find next available index for file type
+ * @param fileType File type
+ * @return Next available index
+ */
+ int FindNextAvailableIndex(FileEntryType fileType) const;
+
+private:
+ MainWindow* mainWindow_; // Main window pointer
+ WorkSpace* currentWorkspace_; // Current workspace
+ QMap dataPanels_; // Panel mapping (key -> panel)
+ QMap dockWidgets_; // Dock widget mapping (key -> dock widget)
+
+ static const QString PANEL_OBJECT_NAME_PREFIX; // Panel object name prefix
+};
\ No newline at end of file
diff --git a/src/workspace/WorkSpaceManager.cpp b/src/workspace/WorkSpaceManager.cpp
index 582e244f..7b85fea4 100644
--- a/src/workspace/WorkSpaceManager.cpp
+++ b/src/workspace/WorkSpaceManager.cpp
@@ -190,7 +190,7 @@ void WorkSpaceManager::OnFrame() {
QString WorkSpaceManager::GetDefaultWorkSpaceName() {
#if _DEBUG
- const QString iniFile = QString("%1workspace/config.ini").arg(QString(CONFIG_PATH)).arg(skin);
+ const QString iniFile = QString("%1workspace/config.ini").arg(QString(CONFIG_PATH));
#else
const QString appDirPath = QApplication::applicationDirPath();
const QString iniFile = QString("%1/config/workspace/config.ini").arg(appDirPath);