diff --git a/.gitignore b/.gitignore index 21989986..5ae90f68 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ build/ bin/ thirdparty/ +tritoin/ +QWEN.md diff --git a/doc/dyttu.docx b/doc/dyttu.docx new file mode 100644 index 00000000..aef5cafc Binary files /dev/null and b/doc/dyttu.docx differ diff --git a/doc/交互设计.md b/doc/交互设计.md new file mode 100644 index 00000000..5ebf8dbf --- /dev/null +++ b/doc/交互设计.md @@ -0,0 +1,26 @@ +# 交互设计 +## 拖拽式添加实体 +## 图表数据格式 +### WAVE图表 +按照一个文件多个曲线来,一行一个曲线 + + + 曲线、曲面每一个类型一个tab页面 最大可以显示9宫格 + + 表格、灯 灯在上方,表格在下方(待定) + + ## 菜单 + ### 文件 + * 新建wordspace + * 打开wordspace + * 保存wordspace + ---- + * 新建曲线 + * 新建曲面 + * 新建表格 + * 新建灯 + + ### 仿真管理 + * 命令运行(手动编辑xml: ) + * 参数编辑:弹出编辑框 + diff --git a/doc/流程图.vsdx b/doc/流程图.vsdx new file mode 100644 index 00000000..8b444531 Binary files /dev/null and b/doc/流程图.vsdx differ diff --git a/doc/预设实体清单.txt b/doc/预设实体清单.txt new file mode 100644 index 00000000..060cde52 --- /dev/null +++ b/doc/预设实体清单.txt @@ -0,0 +1,15 @@ +船: +驱逐机(阿利伯克)、航母(尼米兹) + +卫星: +高轨(静止) + +弹: +高超音速弹(DF21) + +无源干扰: +角反、箔条 + +有源干扰: +弦内、弦外 + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 826a32ba..a8a6d2d0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,8 +14,8 @@ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Widgets LinguistTools DataVis message("qt VERSION " ${QT_VERSION_MAJOR}) -FILE(GLOB_RECURSE HEADER_FILES ./*.h common/*.h common/*.hpp model/*.h app/*.h) -FILE(GLOB_RECURSE CPP_FILES ./*.cpp common/*.cpp model/*.cpp app/*.cpp) +FILE(GLOB_RECURSE HEADER_FILES ./*.h common/*.h common/*.hpp model/*.h app/*.h utils/*.h) +FILE(GLOB_RECURSE CPP_FILES ./*.cpp common/*.cpp model/*.cpp app/*.cpp utils/*.cpp) FILE(GLOB_RECURSE CC_FILES ./*.cc) FILE(GLOB_RECURSE UI_FILES ./*.ui) FILE(GLOB_RECURSE QRC_FILES ./*.qrc) diff --git a/src/Dyt.qrc b/src/Dyt.qrc index 53582c31..367668a1 100644 --- a/src/Dyt.qrc +++ b/src/Dyt.qrc @@ -1,6 +1,7 @@ res/sys_close.png + res/sys_float.png res/sys_max.png res/sys_min.png res/sys_restore.png @@ -11,7 +12,10 @@ res/default/menu_new_file.png res/default/menu_open_file.png res/default/menu_save_file.png - res/default/menu_save_as_file.png + res/default/menu_wave_file.png + res/default/menu_light_file.png + res/default/menu_table_file.png + res/default/menu_surface_file.png res/default/menu_save_shape_file.png res/default/menu_save_store_file.png res/default/menu_report_mesh.png diff --git a/src/config/skin/default.qss b/src/config/skin/default.qss index d4c2f311..259c2cec 100644 --- a/src/config/skin/default.qss +++ b/src/config/skin/default.qss @@ -72,6 +72,17 @@ QPushButton#sys_close { padding: 0; } +QPushButton#sys_float { + max-width:48px; + min-width:48px; + max-height:28px; + min-height:28px; + border: none; + background: transparent; + image: url(:/res/sys_float.png); + padding: 0; +} + QPushButton#sys_min { max-width:48px; min-width:48px; @@ -219,6 +230,18 @@ QWidget#FileManagerMenu > QToolButton#menu_save_shape_file { QWidget#FileManagerMenu > QToolButton#menu_save_shape_file { qproperty-icon: url(:/res/default/menu_report_mesh.png); } +QWidget#FileManagerMenu > QToolButton#menu_wave_file { + qproperty-icon: url(:/res/default/menu_wave_file.png); +} +QWidget#FileManagerMenu > QToolButton#menu_surface_file { + qproperty-icon: url(:/res/default/menu_surface_file.png); +} +QWidget#FileManagerMenu > QToolButton#menu_table_file { + qproperty-icon: url(:/res/default/menu_table_file.png); +} +QWidget#FileManagerMenu > QToolButton#menu_light_file { + qproperty-icon: url(:/res/default/menu_light_file.png); +} QWidget#SystemManagerMenu > QToolButton#menu_exit { qproperty-icon: url(:/res/default/menu_exit.png); @@ -322,6 +345,7 @@ QMenu { background: #212F3C; color: #e0e0e0; padding: 4px; + border-radius: 3px; } QMenu::item { padding: 6px 30px 6px 20px; @@ -340,4 +364,4 @@ QMenu::separator { height: 1px; background: #555; margin: 5px 10px; -} +} \ No newline at end of file diff --git a/src/entities/Entity.cpp b/src/entities/Entity.cpp index 4f12b6cb..e91420e8 100644 --- a/src/entities/Entity.cpp +++ b/src/entities/Entity.cpp @@ -1,6 +1,8 @@ #include "entities/Entity.h" #include +// Ensure QVariant can hold and convert Entity* in signals/slots +Q_DECLARE_METATYPE(Entity*) #include "common/SpdLogger.h" #include "entities/SceneComponent.h" diff --git a/src/entities/PathComponent.cpp b/src/entities/PathComponent.cpp index 5d675c2d..e7281a01 100644 --- a/src/entities/PathComponent.cpp +++ b/src/entities/PathComponent.cpp @@ -68,7 +68,7 @@ void PathComponent::Begin() { for (int index = 0; index < num; ++index) { const Transform& transform = transforms[index]; osg::AnimationPath::ControlPoint controlPoint(transform.GetLocation() /*+ osg::Vec3(index * 10, 0, 0)*/, - OsgUtils::HPRToQuat(transform.GetRotation() + osg::Vec3(0, 0, -90.0)), transform.GetScale()); + OsgUtils::HPRToQuat(transform.GetRotation()), transform.GetScale()); animationPath_->insert(steps[index], controlPoint); } diff --git a/src/res/default/menu_light_file.png b/src/res/default/menu_light_file.png new file mode 100644 index 00000000..c0279ec8 Binary files /dev/null and b/src/res/default/menu_light_file.png differ diff --git a/src/res/default/menu_surface_file.png b/src/res/default/menu_surface_file.png new file mode 100644 index 00000000..836ed95e Binary files /dev/null and b/src/res/default/menu_surface_file.png differ diff --git a/src/res/default/menu_table_file.png b/src/res/default/menu_table_file.png new file mode 100644 index 00000000..16dfbe72 Binary files /dev/null and b/src/res/default/menu_table_file.png differ diff --git a/src/res/default/menu_wave_file.png b/src/res/default/menu_wave_file.png new file mode 100644 index 00000000..5ae4cd95 Binary files /dev/null and b/src/res/default/menu_wave_file.png differ diff --git a/src/res/sys_float.png b/src/res/sys_float.png new file mode 100644 index 00000000..1ceeab87 Binary files /dev/null and b/src/res/sys_float.png differ diff --git a/src/translations/Dyt_zh_CN.ts b/src/translations/Dyt_zh_CN.ts index d83e8bd1..b1ba8005 100644 --- a/src/translations/Dyt_zh_CN.ts +++ b/src/translations/Dyt_zh_CN.ts @@ -382,30 +382,102 @@ - + + new wave file + + + + + new surface file + + + + + new table file + + + + + new light file + + + + Dyt (*.dyt) - + open dyt file - + Dyt (*.dyt);;All files (*.*) - + warning - + workspace is nullptr + + + + + + + + + + + + + + + + + + prompt + + + + + + + + please create workspace first + + + + + + + + up to 9 files allowed for this type + + + + + + + + file already added for this type + + + + + + + + copy file failed + + FitCurveChartView @@ -520,127 +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 @@ -711,12 +788,12 @@ OsgWidget - + warning - + open dyt file failed @@ -820,22 +897,23 @@ + WorkSpace - - + + Entity - + ModelBase - + color base @@ -893,6 +971,30 @@ altitude: + + + + Curve[%1] + + + + + + Surface[%1] + + + + + + Table[%1] + + + + + + Light[%1] + + QtBoolEdit @@ -913,12 +1015,12 @@ QtBoolPropertyManager - + True - + False @@ -942,22 +1044,22 @@ QtColorPropertyManager - + Red - + Green - + Blue - + Alpha @@ -965,48 +1067,48 @@ QtConeWaveComponentManager - - + + ConeWaveComponent - + Height - + Radius - + waveCount - + waveSpeed - + baseColor - + waveColor - + ringBrightAlpha - + ringDarkAlpha @@ -1112,28 +1214,28 @@ QtDashedLineComponentManager - - + + DashedLineComponent - + Start - + End - + Radius - + Color @@ -1149,17 +1251,17 @@ QtEntityPropertyManager - + Name - + Visible - + Transform @@ -1180,37 +1282,37 @@ QtFontPropertyManager - + Family - + Point Size - + Bold - + Italic - + Underline - + Strikeout - + Kerning @@ -1218,22 +1320,22 @@ QtLocalePropertyManager - + <Invalid> - + %1, %2 - + Language - + Country @@ -1241,13 +1343,13 @@ QtMeshComponetManager - - + + MeshComponent - + Mesh @@ -1255,27 +1357,27 @@ QtModelBasePropertyManager - + Name - + Description - + Inflow - + InnerBottomElevation - + Visible @@ -1296,13 +1398,13 @@ QtPathComponentManager - - + + PathComponent - + Path @@ -1310,17 +1412,17 @@ QtPointFPropertyManager - + (%1, %2) - + X - + Y @@ -1328,17 +1430,17 @@ QtPointPropertyManager - + (%1, %2) - + X - + Y @@ -1356,12 +1458,12 @@ - + [%1, %2, %3] - + [%1, %2, %3] [%4, %5, %6] [%7, %8, %9] @@ -1369,27 +1471,27 @@ QtRectFPropertyManager - + [(%1, %2), %3 x %4] - + X - + Y - + Width - + Height @@ -1397,27 +1499,27 @@ QtRectPropertyManager - + [(%1, %2), %3 x %4] - + X - + Y - + Width - + Height @@ -1425,17 +1527,17 @@ QtSizeFPropertyManager - + %1 x %2 - + Width - + Height @@ -1443,33 +1545,33 @@ QtSizePolicyPropertyManager - + <Invalid> - + [%1, %2, %3, %4] - + Horizontal Policy - + Vertical Policy - + Horizontal Stretch - + Vertical Stretch @@ -1477,17 +1579,17 @@ QtSizePropertyManager - + %1 x %2 - + Width - + Height @@ -1503,17 +1605,17 @@ QtTransfromPropertyManager - + Location - + Rotation - + Scale @@ -1534,17 +1636,17 @@ QtVec3PropertyManager - + X - + Y - + Z @@ -1552,20 +1654,90 @@ QtWorkspacePropertyManager - + Name - + Description - + Timestep + + + SimMatlab + + + + + MatlabParam + + + + + WavePath + + + + + ReportPath + + + + + RDPath + + + + + Count + + + + + Curve[%1] + + + + + Surface[%1] + + + + + Table[%1] + + + + + Light[%1] + + + + + Curves + + + + + Surfaces + + + + + Tables + + + + + Lights + + SignalIndicatorLampUI @@ -1742,21 +1914,32 @@ + ... + commond Path + + + + + select commond file path + + + + describe - + Sure - + Cancel @@ -1766,49 +1949,54 @@ - - - - - - + + + + + + warning - + name or save path is empty, please check it - + save current workspace? - + current path is contains current folder, do you want to overwrite it? - + removeRecursively failed - + mkpath failed - + name is exits - + save spaceWork directory + + + select commond file directory + + diff --git a/src/ui/DockTitleBar.cpp b/src/ui/DockTitleBar.cpp index 1271d3d7..3f63dc78 100644 --- a/src/ui/DockTitleBar.cpp +++ b/src/ui/DockTitleBar.cpp @@ -14,6 +14,7 @@ DockTitleBar::DockTitleBar(QWidget* parent) , ui(new Ui::DockTitleBar) { ui->setupUi(this); connect(ui->sys_close, &QPushButton::clicked, this, &DockTitleBar::signalClose); + connect(ui->sys_float, &QPushButton::clicked, this, &DockTitleBar::signalToggleFloating); } DockTitleBar::~DockTitleBar() { diff --git a/src/ui/DockTitleBar.ui b/src/ui/DockTitleBar.ui index 88939bf0..10d2d74f 100644 --- a/src/ui/DockTitleBar.ui +++ b/src/ui/DockTitleBar.ui @@ -49,6 +49,19 @@ + + + + + 32 + 32 + + + + + + + diff --git a/src/ui/DockWidget.cpp b/src/ui/DockWidget.cpp index 6bb51d60..69f6a2b4 100644 --- a/src/ui/DockWidget.cpp +++ b/src/ui/DockWidget.cpp @@ -1,6 +1,7 @@ #include "DockWidget.h" #include +#include #include "ui/Menu/SystemManagerMenu.h" #include "common/SpdLogger.h" @@ -8,7 +9,7 @@ DockWidgetTitleBar::DockWidgetTitleBar(QWidget* parent) - : QWidget(parent) { + : QWidget(parent) { } @@ -54,6 +55,7 @@ void DockWidget::setWindowTitle(const QString& text) { void DockWidget::SetDockWidgetTitleBar(DockWidgetTitleBar* titleBar) { if (nullptr != titleBar_) { disconnect(titleBar_, &DockWidgetTitleBar::signalClose, this, &DockWidget::close); + disconnect(titleBar_, &DockWidgetTitleBar::signalToggleFloating, this, &DockWidget::OnToggleFloat); } titleBar_ = titleBar; if (nullptr == titleBar_) { @@ -63,6 +65,7 @@ void DockWidget::SetDockWidgetTitleBar(DockWidgetTitleBar* titleBar) { titleBar_->SetTitle(windowTitle()); connect(titleBar_, &DockWidgetTitleBar::signalClose, this, &DockWidget::OnClose); + connect(titleBar_, &DockWidgetTitleBar::signalToggleFloating, this, &DockWidget::OnToggleFloat); QDockWidget::setTitleBarWidget(titleBar_); } @@ -86,3 +89,12 @@ void DockWidget::OnClose() { emit signalClose(); } +void DockWidget::OnToggleFloat() { + setFloating(!isFloating()); +} + +void DockWidgetTitleBar::mouseDoubleClickEvent(QMouseEvent* event) { + QWidget::mouseDoubleClickEvent(event); + emit signalToggleFloating(); +} + diff --git a/src/ui/DockWidget.h b/src/ui/DockWidget.h index 8a049bf4..8e878c59 100644 --- a/src/ui/DockWidget.h +++ b/src/ui/DockWidget.h @@ -6,7 +6,7 @@ #include class DockWidgetTitleBar : public QWidget { - Q_OBJECT + Q_OBJECT public: DockWidgetTitleBar(QWidget* parent); @@ -18,9 +18,12 @@ public: // return QSize(270, 900); //} //QSize minimumSizeHint() const override; +protected: + void mouseDoubleClickEvent(QMouseEvent* event) override; Q_SIGNALS: void signalClose(); + void signalToggleFloating(); }; class DockWidget : public QDockWidget { @@ -45,6 +48,7 @@ protected: private: void OnClose(); + void OnToggleFloat(); private: DockWidgetTitleBar* titleBar_{ nullptr }; diff --git a/src/ui/EntityBrowser.cpp b/src/ui/EntityBrowser.cpp new file mode 100644 index 00000000..404b8348 --- /dev/null +++ b/src/ui/EntityBrowser.cpp @@ -0,0 +1,48 @@ +#include "EntityBrowser.h" + +#include +#include + +#include "DockTitleBar.h" +#include "DockWidget.h" +// #include "ModelBrowser/ModelTreeWidget.h" +#include "workspace/WorkSpaceManager.h" + +EntityBrowser::EntityBrowser(QWidget *parent) : + QWidget(parent) { + + InitUI(); +} + +EntityBrowser::~EntityBrowser() { + +} + +void EntityBrowser::AttachDock(DockWidget* dockWidget) { + if (nullptr == dockWidget) { + qDebug() << __FUNCTION__ << "dockwidget is nullptr"; + return; + } + + dockWidget->SetDockWidgetTitleBar(nullptr); + dockWidget->setWidget(this); + + DockTitleBar* dockTitleBar = new DockTitleBar; + dockWidget->SetDockWidgetTitleBar(dockTitleBar); +} + +void EntityBrowser::InitUI() { + // QBoxLayout* layout = new QVBoxLayout(this); + // layout->setContentsMargins(0, 0, 0, 0); + + // treeWidget_ = new ModelTreeWidget; + // treeWidget_->setHeaderHidden(true); + // layout->addWidget(treeWidget_); + + // connect(&WorkSpaceManager::Get(), &WorkSpaceManager::WorkSpaceChanged, + // treeWidget_, &ModelTreeWidget::OnWorkspaceChange); + + // //ModelTreeWidget* treeWidget = modelBrowserPannal_->GetModelTreeWidget(); + // connect(treeWidget_, &ModelTreeWidget::WorkSpaceChange, this, &ModelBrowser::WorkSpaceChange); + // connect(treeWidget_, &ModelTreeWidget::EntityChange, this, &ModelBrowser::EntityChange); +} diff --git a/src/ui/EntityBrowser.h b/src/ui/EntityBrowser.h new file mode 100644 index 00000000..07412fae --- /dev/null +++ b/src/ui/EntityBrowser.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class EntityBrowser : public QWidget { + Q_OBJECT + +public: + explicit EntityBrowser(QWidget *parent = nullptr); + ~EntityBrowser() override; + + void AttachDock(class DockWidget* dockWidget); + +Q_SIGNALS: + void WorkSpaceChange(const QVariant& workSpace); + void EntityChange(const QVariant& workSpace); + +private: + void InitUI(); + +private: + // class ModelTreeWidget* treeWidget_; +}; + diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 487e1133..de1bfdec 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -1,6 +1,7 @@ #include "MainWindow.h" #include +#include "utils/UiLayoutManager.h" #include "PropertyBrowser.h" #include "ModelBrowser.h" @@ -28,6 +29,7 @@ #include "ui_MainWindow.h" #include "viewer/OsgWidget.h" +#include "DockTitleBar.h" MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) @@ -43,43 +45,49 @@ MainWindow::~MainWindow() { } void MainWindow::InitUI() { - - tabWidget_ = new QTabWidget; - tabWidget_->setTabPosition(QTabWidget::South); - tabWidget_->tabBar()->setMinimumWidth(300); - - ui->viewWidget->layout()->addWidget(tabWidget_); - - pSettingUI = new LayoutSettingUI(); - - const QString uiLaytouPath = RecourceHelper::Get().GetBasePath() + "/workspace/UILayout.xml"; - - pSettingUI->InitConfig(uiLaytouPath); - //pSettingUI->show(); - - connect(pSettingUI, &LayoutSettingUI::signalUpdate, this, &MainWindow::InitDockLayout); + QWidget* centralWidget = takeCentralWidget(); + if (nullptr != centralWidget) { + delete centralWidget; + } + setDockNestingEnabled(true); setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); + setDockOptions(QMainWindow::AllowTabbedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AnimatedDocks); DockWidget* model = new DockWidget(tr("model elements"), 0); + model->SetDockWidgetTitleBar(new DockTitleBar(model)); + model->setObjectName("Dock.ModelBrowser"); // addDockWidget(pSettingUI->GetArea("ModelBrowser"), model); modelBrowser_ = new ModelBrowser(0); modelBrowser_->AttachDock(model); m_mapDockWidget.insert("ModelBrowser", model); DockWidget* attribte = new DockWidget(tr("attribte"), 0); + attribte->SetDockWidgetTitleBar(new DockTitleBar(attribte)); + attribte->setObjectName("Dock.PropertyBrowser"); //addDockWidget(pSettingUI->GetArea("PropertyBrowser"), attribte); propertyBrowser_ = new PropertyBrowser(0); propertyBrowser_->AttachDock(attribte); m_mapDockWidget.insert("PropertyBrowser", attribte); + addDockWidget(Qt::RightDockWidgetArea, attribte); connect(modelBrowser_, &ModelBrowser::WorkSpaceChange, propertyBrowser_, &PropertyBrowser::OnWorkSpaceChange); connect(modelBrowser_, &ModelBrowser::EntityChange, propertyBrowser_, &PropertyBrowser::OnEntityChange); qtOsgViewWidget_ = new OsgWidget; qtOsgViewWidget_->Initialize(); - //m_mapDockWidget.insert("PropertyBrowser", attribte); + + // 主视图改为 DockWidget,支持自由停靠 + DockWidget* viewDock = new DockWidget(tr("Main View"), 0); + viewDock->SetDockWidgetTitleBar(new DockTitleBar(viewDock)); + viewDock->setObjectName("Dock.MainView"); + viewDock->setWidget(qtOsgViewWidget_); + addDockWidget(Qt::LeftDockWidgetArea, viewDock); + m_mapDockWidget.insert("MainView", viewDock); + + addDockWidget(Qt::LeftDockWidgetArea, model); + splitDockWidget(model, viewDock, Qt::Horizontal); QString wavePath ="", speedPath = "", rdPath = "", matlabParam=""; if (WorkSpaceManager::Get().GetCurrent()) @@ -106,25 +114,36 @@ void MainWindow::InitUI() { } DockWidget* fitCurveDock = new DockWidget(tr("Wave Curve"), 0); + fitCurveDock->SetDockWidgetTitleBar(new DockTitleBar(fitCurveDock)); + fitCurveDock->setObjectName("Dock.WaveCurveDialog"); fitCurveDlg_ = new FitCurveDialog(1); fitCurveDlg_->AttachDock(fitCurveDock); m_mapDockWidget.insert("WaveCurveDialog", fitCurveDock); + addDockWidget(Qt::BottomDockWidgetArea, fitCurveDock); fitCurveDlg_->InitWaveFile(wavePath); DockWidget* fitLgCurveDock = new DockWidget(tr("Speed Curve"), 0); + fitLgCurveDock->SetDockWidgetTitleBar(new DockTitleBar(fitLgCurveDock)); + fitLgCurveDock->setObjectName("Dock.SpeedCurveDialog"); fitYLgCurveDlg_ = new FitCurveDialog(1); fitYLgCurveDlg_->AttachDock(fitLgCurveDock); m_mapDockWidget.insert("SpeedCurveDialog", fitLgCurveDock); + addDockWidget(Qt::BottomDockWidgetArea, fitLgCurveDock); + tabifyDockWidget(fitCurveDock, fitLgCurveDock); fitYLgCurveDlg_->InitReportFile(speedPath); DockWidget* surfaceCurveDock = new DockWidget(tr("3D Curve"), 0); + surfaceCurveDock->SetDockWidgetTitleBar(new DockTitleBar(surfaceCurveDock)); + surfaceCurveDock->setObjectName("Dock.3DCurveDialog"); surfaceDlg_ = new SurfaceDialog(); surfaceDlg_->AttachDock(surfaceCurveDock); m_mapDockWidget.insert("3DCurveDialog", surfaceCurveDock); + addDockWidget(Qt::BottomDockWidgetArea, surfaceCurveDock); + tabifyDockWidget(fitCurveDock, surfaceCurveDock); surfaceDlg_->InitRD(rdPath); @@ -146,37 +165,55 @@ void MainWindow::InitUI() { targetUITable_->InitFile(speedPath, 50); DockWidget* dataTableDock = new DockWidget(tr("Report Table"), 0); + dataTableDock->SetDockWidgetTitleBar(new DockTitleBar(dataTableDock)); + dataTableDock->setObjectName("Dock.TargetListWgt_Table"); // addDockWidget(pSettingUI->GetArea("TargetListWgt"), dataTableDock); targetUITable_->AttachDock(dataTableDock); m_mapDockWidget.insert("TargetListWgt_Table", dataTableDock); + addDockWidget(Qt::BottomDockWidgetArea, dataTableDock); + tabifyDockWidget(fitCurveDock, dataTableDock); } const QString lampPath = RecourceHelper::Get().GetBasePath() + "/workspace/Lamp.txt"; DockWidget* signalIndicatorLampDock = new DockWidget(tr("Signal Indicator Lamp"), 0); + signalIndicatorLampDock->SetDockWidgetTitleBar(new DockTitleBar(signalIndicatorLampDock)); + signalIndicatorLampDock->setObjectName("Dock.SignalIndicatorLampUI"); signalIndicatorLampUI_ = new SignalIndicatorLampUI; signalIndicatorLampUI_->AttachDock(signalIndicatorLampDock); signalIndicatorLampUI_->InitLamp(lampPath); m_mapDockWidget.insert("SignalIndicatorLampUI", signalIndicatorLampDock); + addDockWidget(Qt::BottomDockWidgetArea, signalIndicatorLampDock); + tabifyDockWidget(fitCurveDock, signalIndicatorLampDock); DockWidget* addParamSettingDock = new DockWidget(tr("ParamSetting"), 0); + addParamSettingDock->SetDockWidgetTitleBar(new DockTitleBar(addParamSettingDock)); + addParamSettingDock->setObjectName("Dock.ParamSetting"); addParamDlg_ = new AddParamSetting(matlabParam); addParamDlg_->AttachDock(addParamSettingDock); m_mapDockWidget.insert("ParamSetting", addParamSettingDock); + addDockWidget(Qt::BottomDockWidgetArea, addParamSettingDock); + tabifyDockWidget(fitCurveDock, addParamSettingDock); DockWidget* matlabDock = new DockWidget(tr("bat File"), 0); + matlabDock->SetDockWidgetTitleBar(new DockTitleBar(matlabDock)); + matlabDock->setObjectName("Dock.Matlab"); matlabFileDlg_ = new CodeEdtUI; matlabFileDlg_->AttachDock(matlabDock); m_mapDockWidget.insert("Matlab", matlabDock); + addDockWidget(Qt::BottomDockWidgetArea, matlabDock); + tabifyDockWidget(fitCurveDock, matlabDock); //ui->discript->setText(tr("name: 5year 0412")); //ui->status->setText(tr("start: no start")); - - InitDockLayout(); - InitChartLayout(); + // InitDockLayout(); + + // Restore previous UI layout if available + UiLayoutManager::Restore(this, 1); + //ui->viewWidget->layout()->addWidget(qtOsgViewWidget_); qtOsgViewWidget_->LoadDefaultScene(); /*OsgViewer::Get().Initialize(); @@ -189,6 +226,8 @@ void MainWindow::InitUI() { } void MainWindow::UninitUI() { + // Save layout state before tearing down widgets + UiLayoutManager::Save(this, 1); if (qtOsgViewWidget_) { qtOsgViewWidget_->Uninitialize(); delete qtOsgViewWidget_; @@ -196,248 +235,6 @@ void MainWindow::UninitUI() { } } -void MainWindow::InitDockLayout() { - while (tabWidget_->count() > 0) { - tabWidget_->removeTab(0); - } - - tabWidget_->tabBar()->setExpanding(true); - - QVariantList listTab = pSettingUI->GetAreaLayout().toList(); - for (int i = 0; i < listTab.size(); i++) { - QVariantMap mapTab = listTab[i].toMap(); - QString strTabName = mapTab.value("Name").toString(); - - QMainWindow* mainWindow_ = new QMainWindow; - connect(mainWindow_, &QMainWindow::tabifiedDockWidgetActivated, this, &MainWindow::OnTabifiedDockWidgetActivated); - - QVariantList listDocArea = mapTab.value("Widget").toList(); - - tabWidget_->insertTab(i, mainWindow_, strTabName); - if (listDocArea[0].toList().size() > 0) { - mainWindow_->setCentralWidget(qtOsgViewWidget_); - //OsgViewer::Get().Initialize(); - //OsgViewer::Get().OnFrame(); - } else { - mainWindow_->takeCentralWidget(); - } - - - if (listDocArea.size() > 0) { - QDockWidget* lastDock = nullptr; - - for (int j = 1; j < listDocArea.size(); j++) { - Qt::DockWidgetArea dockArea; - if (j == 1) { - dockArea = Qt::LeftDockWidgetArea; - } else if (j == 2) { - dockArea = Qt::TopDockWidgetArea; - } else if (j == 3) { - dockArea = Qt::RightDockWidgetArea; - } else if (j == 4) { - dockArea = Qt::BottomDockWidgetArea; - } - - QVariantList listDocAreaChild = listDocArea[j].toList(); - for (int m = 0; m < listDocAreaChild.size(); m++) { - QVariant varWidget = listDocAreaChild[m]; - - if (varWidget.type() == QVariant::String) { - QDockWidget* pDock = m_mapDockWidget.value(varWidget.toString()); - if (pDock == nullptr) { - continue; - } - mainWindow_->addDockWidget(dockArea, pDock); - lastDock = pDock; - } else { - QVariantList listWidget = varWidget.toList(); - for (int k = 0; k < listWidget.size(); k++) { - QDockWidget* pDock = m_mapDockWidget.value(listWidget[k].toString()); - mainWindow_->addDockWidget(dockArea, pDock); - - if (k == 0) { - if (lastDock) { - mainWindow_->splitDockWidget(lastDock, pDock, Qt::Vertical); - } - } else { - mainWindow_->splitDockWidget(lastDock, pDock, Qt::Horizontal); - } - - lastDock = pDock; - } - } - } - } - } - } - - tabWidget_->tabBar()->setMinimumWidth(500); - /* AddDockArea("DockLeftArea"); - AddDockArea("DockTopArea"); - AddDockArea("DockRightArea"); - AddDockArea("DockBottomArea");*/ -} - -void MainWindow::AddDockArea(const QString& strArea) { - Qt::DockWidgetArea dockArea; - Qt::Orientation orient; - - if (strArea == "DockLeftArea") { - dockArea = Qt::LeftDockWidgetArea; - orient = Qt::Vertical; - } else if (strArea == "DockTopArea") { - dockArea = Qt::TopDockWidgetArea; - orient = Qt::Horizontal; - } else if (strArea == "DockRightArea") { - dockArea = Qt::RightDockWidgetArea; - orient = Qt::Vertical; - } else if (strArea == "DockBottomArea") { - dockArea = Qt::BottomDockWidgetArea; - orient = Qt::Horizontal; - } else { - return; - } - - QList listAdd; - - QVariant varArea = pSettingUI->GetAreaLayout(); - if (varArea.isValid()) { - QVariantList listWidget = varArea.toList(); - for each(QVariant varWidget in listWidget) { - if (varWidget.type() == QVariant::String) { - QDockWidget* pDock = m_mapDockWidget.value(varWidget.toString()); - addDockWidget(dockArea, pDock); - - listAdd.push_back(pDock); - } else { - QDockWidget* pLastDock = nullptr; - QVariantList listTab = varWidget.toList(); - for each(QVariant tabChild in listTab) { - QDockWidget* pDock = m_mapDockWidget.value(tabChild.toString()); - addDockWidget(dockArea, pDock); - - if (pLastDock) { - //tabifyDockWidget(pLastDock, pDock); - splitDockWidget(pLastDock, pDock, Qt::Horizontal); - } - - listAdd.push_back(pDock); - - pLastDock = pDock; - } - } - } - } - QList listSpliter; - for (size_t i = 0; i < listAdd.size(); i++) { - listSpliter.push_back(1); - } - - resizeDocks(listAdd, listSpliter, orient); -} - -void MainWindow::InitChartLayout() -{ - QString strChart = RecourceHelper::Get().GetBasePath() + "/workspace/Chart.xml"; - m_mgrChart.Load(strChart); - - QVariantList varList = m_mgrChart.GetChartWgt(); - for (int nI = 0; nI < varList.size(); nI++) - { - QVariantMap mapWgt = varList.at(nI).toMap(); - QString strWgt = mapWgt.begin().key(); - QVariantList listChart = mapWgt.begin().value().toList(); - - QMainWindow* mainWindow_ = new QMainWindow; - connect(mainWindow_, &QMainWindow::tabifiedDockWidgetActivated, this, &MainWindow::OnTabifiedDockWidgetActivated); - - int nCount = tabWidget_->count(); - tabWidget_->insertTab(nCount, mainWindow_, strWgt); - - QVariantMap varRows; - for (int nJ = 0; nJ < listChart.size(); nJ++) - { - QVariantMap mapChart = listChart.at(nJ).toMap(); - int nRow = mapChart.value("row").toInt(); - int nCol = mapChart.value("column").toInt(); - QString strKey = QString::number(nRow) + "-" + QString::number(nCol); - varRows.insert(strKey, mapChart); - } - - QDockWidget* lastRowDock = nullptr; - QDockWidget* lastColDock = nullptr; - int lastRow = -1; - int lastCol = -1; - for (QVariantMap::Iterator ite = varRows.begin(); ite != varRows.end(); ++ite) - { - QVariantMap mapChart = ite.value().toMap(); - int nRow = mapChart.value("row").toInt(); - int nCol = mapChart.value("column").toInt(); - QString strName = mapChart.value("Name").toString(); - QString xTitle = mapChart.value("xTitle").toString(); - QString yTitle = mapChart.value("yTitle").toString(); - float xMin = mapChart.value("xMin").toFloat(); - float xMax = mapChart.value("xMax").toFloat(); - int xCount = mapChart.value("xCount").toInt(); - float yMin = mapChart.value("yMin").toFloat(); - float yMax = mapChart.value("yMax").toFloat(); - - DockWidget* pDock = new DockWidget(strName, 0); - fitCurveDlg_ = new FitCurveDialog(1); - fitCurveDlg_->AttachDock(pDock); - fitCurveDlg_->updateTitle(xTitle, yTitle); - fitCurveDlg_->updateMinMaxX(xMin, xMax, xCount); - fitCurveDlg_->updateMinMaxY(yMin, yMax); - - mainWindow_->addDockWidget(Qt::LeftDockWidgetArea, pDock); - - if (lastRow > -1) - { - if (lastRow == nRow) - { - if (lastCol > -1) - { - if (lastCol != nCol) - { - mainWindow_->splitDockWidget(lastColDock, pDock, Qt::Horizontal); - } - lastColDock = pDock; - } - else - { - lastColDock = pDock; - } - } - else - { - //mainWindow_->splitDockWidget(lastRowDock, pDock, Qt::Vertical); - - lastRowDock = pDock; - } - } - else - { - lastRowDock = pDock; - lastColDock = pDock; - } - - lastRow = nRow; - lastCol = nCol; - } - } -} - -void MainWindow::OnTabifiedDockWidgetActivated(QDockWidget* dockWidget) { - //if (dockWidget) { - // QMainWindow* mainWindow = qobject_cast(dockWidget->parentWidget()); - // if (mainWindow) { - // mainWindow->removeDockWidget(dockWidget); - // } - - // // tabWidget_->setCurrentWidget(dockWidget->parentWidget()); - //} -} - void MainWindow::slotShowUISetting() { pSettingUI->show(); } diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index adb9d0f8..8b77370b 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -46,14 +46,6 @@ private: void InitUI(); void UninitUI(); - void InitDockLayout(); - void AddDockArea(const QString& strArea); - - void InitChartLayout(); - -protected: - void OnTabifiedDockWidgetActivated(QDockWidget* dockWidget); - private: Ui::MainWindow* ui; diff --git a/src/ui/MainWindow.ui b/src/ui/MainWindow.ui index 61c17f6e..63d92d14 100644 --- a/src/ui/MainWindow.ui +++ b/src/ui/MainWindow.ui @@ -1,131 +1,20 @@ - MainWindow - - - - 0 - 0 - 800 - 658 - - - - MainWindow - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 19 - - - 19 - - - 19 - - - 19 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - - 0 - 20 - - - - - 16777215 - 30 - - - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - Qt::Horizontal - - - - 767 - 20 - - - - - - - - - - - - + MainWindow + + + + 0 + 0 + 1223 + 950 + + + + MainWindow + + + + + diff --git a/src/ui/Menu/FileManagerMenu.cpp b/src/ui/Menu/FileManagerMenu.cpp index aff15399..653ea602 100644 --- a/src/ui/Menu/FileManagerMenu.cpp +++ b/src/ui/Menu/FileManagerMenu.cpp @@ -12,6 +12,7 @@ #include "common/SpdLogger.h" #include "workspace/WorkSpace.h" #include "workspace/WorkSpaceManager.h" +#include "workspace/FileEntry.h" #include "utils/FileUtils.h" #include "ui_FileManagerMenu.h" @@ -32,6 +33,12 @@ void FileManagerMenu::InitConnect() { connect(ui->menu_new_file, &QToolButton::clicked, this, &FileManagerMenu::NewWorkSpace); connect(ui->menu_open_file, &QToolButton::clicked, this, &FileManagerMenu::OpenWorkSpace); connect(ui->menu_save_file, &QToolButton::clicked, this, &FileManagerMenu::SaveWorkSpace); + + // add file entity buttons + connect(ui->menu_wave_file, &QToolButton::clicked, this, &FileManagerMenu::AddWaveFile); + 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); } void FileManagerMenu::NewWorkSpace() { @@ -70,19 +77,94 @@ void FileManagerMenu::SaveWorkSpace() { QString dytFile = workspace->GetPath(); LOG_INFO("save {} dyt file: {}", name.toLocal8Bit().constData(), dytFile.toLocal8Bit().constData()); - /* if (!FileUtils::IsExist(dytFile)) { - QString selfilter = tr("Dyt (*.dyt)"); - const QString workspacePath = Application::GetWorkSpacePath(); - dytFile = QFileDialog::getSaveFileName(&MainFrame::Get(), tr("save dyt file"), workspacePath, - tr("Dyt (*.dyt);;All files (*.*)"), - &selfilter); - LOG_INFO("user select file: {}", dytFile.toLocal8Bit().constData()); - if (dytFile.isEmpty()) { - return; - } - - }*/ bool success = workspace->Save(dytFile); LOG_INFO("save dyt: {}", success); } + +void FileManagerMenu::AddWaveFile() { + auto current = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == current) { + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("please create workspace first")); + return; + } + + switch (current->CreateFileEntry(FileEntryType::Curve)) { + case WorkSpace::FileEntryResult::Ok: + break; + case WorkSpace::FileEntryResult::LimitExceeded: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("up to 9 files allowed for this type")); + break; + case WorkSpace::FileEntryResult::Duplicate: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("file already added for this type")); + break; + case WorkSpace::FileEntryResult::CopyFailed: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("copy file failed")); + break; + } +} + +void FileManagerMenu::AddSurfaceFile() { + auto current = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == current) { + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("please create workspace first")); + return; + } + + switch (current->CreateFileEntry(FileEntryType::Surface)) { + case WorkSpace::FileEntryResult::Ok: + break; + case WorkSpace::FileEntryResult::LimitExceeded: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("up to 9 files allowed for this type")); + break; + case WorkSpace::FileEntryResult::Duplicate: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("file already added for this type")); + break; + case WorkSpace::FileEntryResult::CopyFailed: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("copy file failed")); + break; + } +} + +void FileManagerMenu::AddTableFile() { + auto current = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == current) { + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("please create workspace first")); + return; + } + switch (current->CreateFileEntry(FileEntryType::Table)) { + case WorkSpace::FileEntryResult::Ok: + break; + case WorkSpace::FileEntryResult::LimitExceeded: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("up to 9 files allowed for this type")); + break; + case WorkSpace::FileEntryResult::Duplicate: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("file already added for this type")); + break; + case WorkSpace::FileEntryResult::CopyFailed: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("copy file failed")); + break; + } +} + +void FileManagerMenu::AddLightFile() { + auto current = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == current) { + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("please create workspace first")); + return; + } + + switch (current->CreateFileEntry(FileEntryType::Light)) { + case WorkSpace::FileEntryResult::Ok: + break; + case WorkSpace::FileEntryResult::LimitExceeded: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("up to 9 files allowed for this type")); + break; + case WorkSpace::FileEntryResult::Duplicate: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("file already added for this type")); + break; + case WorkSpace::FileEntryResult::CopyFailed: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("copy file failed")); + break; + } +} diff --git a/src/ui/Menu/FileManagerMenu.h b/src/ui/Menu/FileManagerMenu.h index d0948b5a..ff93612e 100644 --- a/src/ui/Menu/FileManagerMenu.h +++ b/src/ui/Menu/FileManagerMenu.h @@ -24,6 +24,11 @@ private: void OpenWorkSpace(); void SaveWorkSpace(); + void AddWaveFile(); + void AddSurfaceFile(); + void AddTableFile(); + void AddLightFile(); + private: Ui::FileManagerMenu* ui; }; \ No newline at end of file diff --git a/src/ui/Menu/FileManagerMenu.ui b/src/ui/Menu/FileManagerMenu.ui index d356a9e2..0aa0b80b 100644 --- a/src/ui/Menu/FileManagerMenu.ui +++ b/src/ui/Menu/FileManagerMenu.ui @@ -54,6 +54,46 @@ + + + + new wave file + + + + + + + + + + new surface file + + + + + + + + + + new table file + + + + + + + + + + new light file + + + + + + diff --git a/src/ui/PropertyBrowser.cpp b/src/ui/PropertyBrowser.cpp index db566930..2c53dbd0 100644 --- a/src/ui/PropertyBrowser.cpp +++ b/src/ui/PropertyBrowser.cpp @@ -56,6 +56,14 @@ void PropertyBrowser::OnWorkSpaceChange(const QVariant& value) { QWorkspaceAttribute worksapceAttribute(workspace); workSpaceManager_->setValue(property, worksapceAttribute); addProperty(property, tr("WorkSpace")); + + // Track and react to runtime workspace changes + if (currentWorkspace_) { + QObject::disconnect(currentWorkspace_, nullptr, this, nullptr); + } + currentWorkspace_ = workspace; + QObject::connect(currentWorkspace_, &WorkSpace::FilesChanged, + this, &PropertyBrowser::OnWorkspaceFilesChanged); } void PropertyBrowser::OnEntityChange(const QVariant& value) { @@ -152,6 +160,8 @@ void PropertyBrowser::InitPropertyManager() { browser_->setFactoryForManager(sizeManager_->subIntPropertyManager(), spinBoxFactory); browser_->setFactoryForManager(workSpaceManager_->subStringProperyManager(), lineEditFactory); browser_->setFactoryForManager(workSpaceManager_->subFilesProperyManager(), filePathFactory); + // Enable editing for grouped file entry counts + browser_->setFactoryForManager(workSpaceManager_->subIntProperyManager(), spinBoxFactory); browser_->setFactoryForManager(entityManager_->subStringProperyManager(), lineEditFactory); browser_->setFactoryForManager(entityManager_->subBoolProperyManager(), checkBoxFactory); browser_->setFactoryForManager( @@ -165,6 +175,16 @@ void PropertyBrowser::InitPropertyManager() { doubleSpinBoxFactory); } +void PropertyBrowser::OnWorkspaceFilesChanged(FileEntryType /*type*/) { + if (!currentWorkspace_) return; + auto it = idToProperty_.find(tr("WorkSpace")); + if (it == idToProperty_.end()) return; + QtProperty* property = it.value(); + QWorkspaceAttribute worksapceAttribute(currentWorkspace_); + // Refresh the full workspace property tree to reflect new counts/paths + workSpaceManager_->setValue(property, worksapceAttribute); +} + void PropertyBrowser::InitComponentPropertyManager() { QtDoubleSpinBoxFactory* doubleSpinBoxFactory = new QtDoubleSpinBoxFactory(this); QtCheckBoxFactory* checkBoxFactory = new QtCheckBoxFactory(this); diff --git a/src/ui/PropertyBrowser.h b/src/ui/PropertyBrowser.h index 770529c2..9effc06c 100644 --- a/src/ui/PropertyBrowser.h +++ b/src/ui/PropertyBrowser.h @@ -2,6 +2,7 @@ #include #include +#include "workspace/FileEntry.h" class QtProperty; @@ -17,6 +18,7 @@ public: void OnWorkSpaceChange(const QVariant& value); void OnEntityChange(const QVariant& value); + void OnWorkspaceFilesChanged(enum class FileEntryType type); void Test(); @@ -49,5 +51,8 @@ private: QMap idToExpanded_; QMap componetManager_; + + // Track current workspace for real-time refresh + class WorkSpace* currentWorkspace_{ nullptr }; }; diff --git a/src/ui/PropertyBrowser/qtpropertymanager.cpp b/src/ui/PropertyBrowser/qtpropertymanager.cpp index 7c6ca7cf..0de1686e 100644 --- a/src/ui/PropertyBrowser/qtpropertymanager.cpp +++ b/src/ui/PropertyBrowser/qtpropertymanager.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -7893,35 +7894,84 @@ void QtModelBasePropertyManager::uninitializeProperty(QtProperty* property) { #pragma region QtWorkspacePropertyManager class QtWorkspacePropertyManagerPrivate { - QtWorkspacePropertyManager* q_ptr; - Q_DECLARE_PUBLIC(QtWorkspacePropertyManager) + QtWorkspacePropertyManager* q_ptr; + Q_DECLARE_PUBLIC(QtWorkspacePropertyManager) public: - void slotStringChanged(QtProperty* property, QString value); + void slotStringChanged(QtProperty* property, QString value); + void slotIntChanged(QtProperty* property, int value); - void slotPropertyDestroyed(QtProperty* property); + void slotPropertyDestroyed(QtProperty* property); - typedef QMap PropertyValueMap; - PropertyValueMap m_values; + typedef QMap PropertyValueMap; + PropertyValueMap m_values; - QtStringPropertyManager* m_stringProperyManager; - QtFilesPropertyManager* m_filesProperyManager; + QtStringPropertyManager* m_stringProperyManager; + QtFilesPropertyManager* m_filesProperyManager; + QtIntPropertyManager* m_intProperyManager{ nullptr }; + QtGroupPropertyManager* m_groupProperyManager{ nullptr }; - QMap m_properyToName; - QMap m_properyToDescription; - QMap m_properyToTimestep; + QMap m_properyToName; + QMap m_properyToDescription; + QMap m_properyToTimestep; + QMap m_properyToSimMatlab; + QMap m_properyToMatlabParam; + QMap m_properyToWavePath; + QMap m_properyToReportPath; + QMap m_properyToRDPath; - QMap m_nameToPropery; - QMap m_descriptionToPropery; - QMap m_timestepToPropery; + QMap m_nameToPropery; + QMap m_descriptionToPropery; + QMap m_timestepToPropery; + QMap m_simMatlabToPropery; + QMap m_matlabParamToPropery; + QMap m_wavePathToPropery; + QMap m_reportPathToPropery; + QMap m_rdPathToPropery; + + // Grouped file entries: Curve + QMap m_properyToCurveGroup; + QMap m_curveGroupToPropery; + QMap m_properyToCurveCount; + QMap m_curveCountToPropery; + QMap> m_properyToCurvePaths; + QMap m_curvePathToPropery; + QMap m_curvePathIndex; + + // Surface + QMap m_properyToSurfaceGroup; + QMap m_surfaceGroupToPropery; + QMap m_properyToSurfaceCount; + QMap m_surfaceCountToPropery; + QMap> m_properyToSurfacePaths; + QMap m_surfacePathToPropery; + QMap m_surfacePathIndex; + + // Table + QMap m_properyToTableGroup; + QMap m_tableGroupToPropery; + QMap m_properyToTableCount; + QMap m_tableCountToPropery; + QMap> m_properyToTablePaths; + QMap m_tablePathToPropery; + QMap m_tablePathIndex; + + // Light + QMap m_properyToLightGroup; + QMap m_lightGroupToPropery; + QMap m_properyToLightCount; + QMap m_lightCountToPropery; + QMap> m_properyToLightPaths; + QMap m_lightPathToPropery; + QMap m_lightPathIndex; }; void QtWorkspacePropertyManagerPrivate::slotStringChanged(QtProperty* property, QString value) { - if (QtProperty* prop = m_nameToPropery.value(property, 0)) { - QWorkspaceAttribute c = m_values[prop]; - c.SetName(value); - q_ptr->setValue(prop, c); + if (QtProperty* prop = m_nameToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetName(value); + q_ptr->setValue(prop, c); } else if (QtProperty* prop = m_descriptionToPropery.value(property, 0)) { QWorkspaceAttribute c = m_values[prop]; c.SetDescription(value); @@ -7930,9 +7980,102 @@ void QtWorkspacePropertyManagerPrivate::slotStringChanged(QtProperty* property, QWorkspaceAttribute c = m_values[prop]; c.SetTimeStep(value); q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_simMatlabToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetSimMatlab(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_matlabParamToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetMatlabParam(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_wavePathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetWavePath(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_reportPathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetReportPath(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_rdPathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetRDPath(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_curvePathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + int idx = m_curvePathIndex.value(property, 0); + c.SetFileEntryPath(FileEntryType::Curve, idx, value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_surfacePathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + int idx = m_surfacePathIndex.value(property, 0); + c.SetFileEntryPath(FileEntryType::Surface, idx, value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_tablePathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + int idx = m_tablePathIndex.value(property, 0); + c.SetFileEntryPath(FileEntryType::Table, idx, value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_lightPathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + int idx = m_lightPathIndex.value(property, 0); + c.SetFileEntryPath(FileEntryType::Light, idx, value); + q_ptr->setValue(prop, c); } } +void QtWorkspacePropertyManagerPrivate::slotIntChanged(QtProperty* property, int value) { + // Determine which type this count property belongs to + auto handleType = [&](FileEntryType type, + QMap& countToProp, + QMap>& propToPaths, + QMap& pathToProp, + QMap& pathIndex, + QMap& propToGroup) { + if (QtProperty* root = countToProp.value(property, nullptr)) { + // Adjust UI path properties to match new count + QVector& paths = propToPaths[root]; + int current = paths.size(); + // Add new path properties + if (value > current) { + QtProperty* group = propToGroup.value(root, nullptr); + for (int i = current; i < value; ++i) { + QtProperty* p = m_filesProperyManager->addProperty(); + QString title; + switch (type) { + case FileEntryType::Curve: title = QObject::tr("Curve[%1]").arg(i + 1); break; + 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; + } + p->setPropertyName(title); + group->addSubProperty(p); + paths.append(p); + pathToProp[p] = root; + pathIndex[p] = i; + } + } else if (value < current) { + // Remove excess path properties + for (int i = current - 1; i >= value; --i) { + QtProperty* p = paths.at(i); + pathIndex.remove(p); + pathToProp.remove(p); + paths.remove(i); + delete p; + } + } + // Update underlying model count + QWorkspaceAttribute c = m_values[root]; + c.SetFileEntryCount(type, value); + q_ptr->setValue(root, c); + } + }; + + handleType(FileEntryType::Curve, m_curveCountToPropery, m_properyToCurvePaths, m_curvePathToPropery, m_curvePathIndex, m_properyToCurveGroup); + 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); +} + void QtWorkspacePropertyManagerPrivate::slotPropertyDestroyed(QtProperty* property) { if (QtProperty* subProp = m_nameToPropery.value(property, nullptr)) { m_nameToPropery[subProp] = 0; @@ -7943,23 +8086,48 @@ void QtWorkspacePropertyManagerPrivate::slotPropertyDestroyed(QtProperty* proper m_descriptionToPropery[subProp] = 0; m_descriptionToPropery.remove(property); } - - if (QtProperty* subProp = m_timestepToPropery.value(property, nullptr)) { - m_timestepToPropery[subProp] = 0; - m_timestepToPropery.remove(property); - } + + if (QtProperty* subProp = m_timestepToPropery.value(property, nullptr)) { + m_timestepToPropery[subProp] = 0; + m_timestepToPropery.remove(property); + } + + if (QtProperty* subProp = m_simMatlabToPropery.value(property, nullptr)) { + m_simMatlabToPropery[subProp] = 0; + m_simMatlabToPropery.remove(property); + } + if (QtProperty* subProp = m_matlabParamToPropery.value(property, nullptr)) { + m_matlabParamToPropery[subProp] = 0; + m_matlabParamToPropery.remove(property); + } + if (QtProperty* subProp = m_wavePathToPropery.value(property, nullptr)) { + m_wavePathToPropery[subProp] = 0; + m_wavePathToPropery.remove(property); + } + if (QtProperty* subProp = m_reportPathToPropery.value(property, nullptr)) { + m_reportPathToPropery[subProp] = 0; + m_reportPathToPropery.remove(property); + } + if (QtProperty* subProp = m_rdPathToPropery.value(property, nullptr)) { + m_rdPathToPropery[subProp] = 0; + m_rdPathToPropery.remove(property); + } } QtWorkspacePropertyManager::QtWorkspacePropertyManager(QObject* parent) - : QtAbstractPropertyManager(parent), d_ptr(new QtWorkspacePropertyManagerPrivate) { - d_ptr->q_ptr = this; + : QtAbstractPropertyManager(parent), d_ptr(new QtWorkspacePropertyManagerPrivate) { + d_ptr->q_ptr = this; - d_ptr->m_stringProperyManager = new QtStringPropertyManager(this); - connect(d_ptr->m_stringProperyManager, SIGNAL(valueChanged(QtProperty*, QString)), - this, SLOT(slotStringChanged(QtProperty*, QString))); + d_ptr->m_stringProperyManager = new QtStringPropertyManager(this); + connect(d_ptr->m_stringProperyManager, SIGNAL(valueChanged(QtProperty*, QString)), + this, SLOT(slotStringChanged(QtProperty*, QString))); d_ptr->m_filesProperyManager = new QtFilesPropertyManager(this); connect(d_ptr->m_filesProperyManager, SIGNAL(valueChanged(QtProperty*, QString)), this, SLOT(slotStringChanged(QtProperty*, QString))); + d_ptr->m_intProperyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intProperyManager, SIGNAL(valueChanged(QtProperty*, int)), + this, SLOT(slotIntChanged(QtProperty*, int))); + d_ptr->m_groupProperyManager = new QtGroupPropertyManager(this); } /*! @@ -7982,11 +8150,19 @@ QWorkspaceAttribute QtWorkspacePropertyManager::value(const QtProperty* property } QtStringPropertyManager* QtWorkspacePropertyManager::subStringProperyManager() const { - return d_ptr->m_stringProperyManager; + return d_ptr->m_stringProperyManager; } QtFilesPropertyManager* QtWorkspacePropertyManager::subFilesProperyManager() const { - return d_ptr->m_filesProperyManager; + return d_ptr->m_filesProperyManager; +} + +QtIntPropertyManager* QtWorkspacePropertyManager::subIntProperyManager() const { + return d_ptr->m_intProperyManager; +} + +QtGroupPropertyManager* QtWorkspacePropertyManager::subGroupProperyManager() const { + return d_ptr->m_groupProperyManager; } /*! @@ -8019,30 +8195,88 @@ QIcon QtWorkspacePropertyManager::valueIcon(const QtProperty* property) const { \sa value(), valueChanged() */ void QtWorkspacePropertyManager::setValue(QtProperty* property, const QWorkspaceAttribute& value) { - const QtWorkspacePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; + const QtWorkspacePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; - if (it.value() == value) - return; + if (it.value() == value) + return; - it.value() = value; + it.value() = value; - d_ptr->m_stringProperyManager->setValue(d_ptr->m_properyToName[property], value.GetName()); - d_ptr->m_stringProperyManager->setValue(d_ptr->m_properyToDescription[property], value.GetDescription()); - d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToTimestep[property], value.GetTimeStep()); + d_ptr->m_stringProperyManager->setValue(d_ptr->m_properyToName[property], value.GetName()); + d_ptr->m_stringProperyManager->setValue(d_ptr->m_properyToDescription[property], value.GetDescription()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToTimestep[property], value.GetTimeStep()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToSimMatlab[property], value.GetSimMatlab()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToMatlabParam[property], value.GetMatlabParam()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToWavePath[property], value.GetWavePath()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToReportPath[property], value.GetReportPath()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToRDPath[property], value.GetRDPath()); - emit propertyChanged(property); - emit valueChanged(property, value); + auto syncGroup = [&](FileEntryType type, + QMap& propToGroup, + QMap& propToCount, + QMap>& propToPaths, + QMap& pathToProp, + QMap& pathIndex) { + QtProperty* group = propToGroup.value(property, nullptr); + if (!group) return; + auto entries = value.GetFileEntries(type); + int count = static_cast(entries.size()); + // update count without triggering slot + QtProperty* countProp = propToCount.value(property, nullptr); + if (countProp) d_ptr->m_intProperyManager->setValueOnly(countProp, count); + QVector& paths = propToPaths[property]; + int current = paths.size(); + // expand + if (count > current) { + for (int i = current; i < count; ++i) { + QtProperty* p = d_ptr->m_filesProperyManager->addProperty(); + QString title; + switch (type) { + case FileEntryType::Curve: title = QObject::tr("Curve[%1]").arg(i + 1); break; + 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; + } + p->setPropertyName(title); + group->addSubProperty(p); + paths.append(p); + pathToProp[p] = property; + pathIndex[p] = i; + } + } else if (count < current) { + for (int i = current - 1; i >= count; --i) { + QtProperty* p = paths.at(i); + pathIndex.remove(p); + pathToProp.remove(p); + paths.remove(i); + delete p; + } + } + // set values + for (int i = 0; i < count; ++i) { + const QString absPath = value.GetFileEntryAbsPath(type, i); + d_ptr->m_filesProperyManager->setValueOnly(paths.at(i), absPath); + } + }; + + syncGroup(FileEntryType::Curve, d_ptr->m_properyToCurveGroup, d_ptr->m_properyToCurveCount, d_ptr->m_properyToCurvePaths, d_ptr->m_curvePathToPropery, d_ptr->m_curvePathIndex); + 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); + + emit propertyChanged(property); + emit valueChanged(property, value); } /*! \reimp */ void QtWorkspacePropertyManager::initializeProperty(QtProperty* property) { - QWorkspaceAttribute val; - d_ptr->m_values[property] = val; + QWorkspaceAttribute val; + d_ptr->m_values[property] = val; QtProperty* prop = d_ptr->m_stringProperyManager->addProperty(); prop->setPropertyName(tr("Name")); @@ -8064,18 +8298,113 @@ void QtWorkspacePropertyManager::initializeProperty(QtProperty* property) { d_ptr->m_properyToTimestep[property] = prop; d_ptr->m_timestepToPropery[prop] = property; property->addSubProperty(prop); + + prop = d_ptr->m_filesProperyManager->addProperty(); + prop->setPropertyName(tr("SimMatlab")); + d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetSimMatlab()); + d_ptr->m_properyToSimMatlab[property] = prop; + d_ptr->m_simMatlabToPropery[prop] = property; + property->addSubProperty(prop); + + prop = d_ptr->m_filesProperyManager->addProperty(); + prop->setPropertyName(tr("MatlabParam")); + d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetMatlabParam()); + d_ptr->m_properyToMatlabParam[property] = prop; + d_ptr->m_matlabParamToPropery[prop] = property; + property->addSubProperty(prop); + + prop = d_ptr->m_filesProperyManager->addProperty(); + prop->setPropertyName(tr("WavePath")); + d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetWavePath()); + d_ptr->m_properyToWavePath[property] = prop; + d_ptr->m_wavePathToPropery[prop] = property; + property->addSubProperty(prop); + + prop = d_ptr->m_filesProperyManager->addProperty(); + prop->setPropertyName(tr("ReportPath")); + d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetReportPath()); + d_ptr->m_properyToReportPath[property] = prop; + d_ptr->m_reportPathToPropery[prop] = property; + property->addSubProperty(prop); + + prop = d_ptr->m_filesProperyManager->addProperty(); + prop->setPropertyName(tr("RDPath")); + d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetRDPath()); + d_ptr->m_properyToRDPath[property] = prop; + d_ptr->m_rdPathToPropery[prop] = property; + property->addSubProperty(prop); + + // Add grouped file sections + auto addGroup = [&](FileEntryType type, const QString& groupName, + QMap& propToGroup, + QMap& groupToProp, + QMap& propToCount, + QMap& countToProp, + QMap>& propToPaths, + QMap& pathToProp, + QMap& pathIndex) { + QtProperty* group = d_ptr->m_groupProperyManager->addProperty(); + group->setPropertyName(groupName); + property->addSubProperty(group); + propToGroup[property] = group; + groupToProp[group] = property; + + // Count property + QtProperty* countProp = d_ptr->m_intProperyManager->addProperty(); + countProp->setPropertyName(tr("Count")); + d_ptr->m_intProperyManager->setRange(countProp, 0, 1024); + // initial count from workspace + int initialCount = static_cast(val.GetFileEntries(type).size()); + d_ptr->m_intProperyManager->setValueOnly(countProp, initialCount); + propToCount[property] = countProp; + countToProp[countProp] = property; + group->addSubProperty(countProp); + + // initial paths + auto entries = val.GetFileEntries(type); + QVector& paths = propToPaths[property]; + for (int i = 0; i < static_cast(entries.size()); ++i) { + QtProperty* p = d_ptr->m_filesProperyManager->addProperty(); + QString title; + switch (type) { + case FileEntryType::Curve: title = tr("Curve[%1]").arg(i + 1); break; + case FileEntryType::Surface: title = tr("Surface[%1]").arg(i + 1); break; + case FileEntryType::Table: title = tr("Table[%1]").arg(i + 1); break; + case FileEntryType::Light: title = tr("Light[%1]").arg(i + 1); break; + } + p->setPropertyName(title); + d_ptr->m_filesProperyManager->setValueOnly(p, val.GetFileEntryAbsPath(type, i)); + group->addSubProperty(p); + paths.append(p); + pathToProp[p] = property; + pathIndex[p] = i; + } + }; + + addGroup(FileEntryType::Curve, tr("Curves"), d_ptr->m_properyToCurveGroup, d_ptr->m_curveGroupToPropery, + d_ptr->m_properyToCurveCount, d_ptr->m_curveCountToPropery, + d_ptr->m_properyToCurvePaths, d_ptr->m_curvePathToPropery, d_ptr->m_curvePathIndex); + addGroup(FileEntryType::Surface, tr("Surfaces"), d_ptr->m_properyToSurfaceGroup, d_ptr->m_surfaceGroupToPropery, + d_ptr->m_properyToSurfaceCount, d_ptr->m_surfaceCountToPropery, + d_ptr->m_properyToSurfacePaths, d_ptr->m_surfacePathToPropery, d_ptr->m_surfacePathIndex); + addGroup(FileEntryType::Table, tr("Tables"), d_ptr->m_properyToTableGroup, d_ptr->m_tableGroupToPropery, + d_ptr->m_properyToTableCount, d_ptr->m_tableCountToPropery, + d_ptr->m_properyToTablePaths, d_ptr->m_tablePathToPropery, d_ptr->m_tablePathIndex); + 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); } /*! \reimp */ void QtWorkspacePropertyManager::uninitializeProperty(QtProperty* property) { - QtProperty* prop = d_ptr->m_nameToPropery[property]; - if (prop) { - d_ptr->m_nameToPropery.remove(prop); - delete prop; - } - d_ptr->m_properyToName.remove(property); + QtProperty* prop = d_ptr->m_nameToPropery[property]; + if (prop) { + d_ptr->m_nameToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToName.remove(property); prop = d_ptr->m_descriptionToPropery[property]; if (prop) { @@ -8090,6 +8419,84 @@ void QtWorkspacePropertyManager::uninitializeProperty(QtProperty* property) { delete prop; } d_ptr->m_properyToTimestep.remove(property); + + prop = d_ptr->m_simMatlabToPropery[property]; + if (prop) { + d_ptr->m_simMatlabToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToSimMatlab.remove(property); + + prop = d_ptr->m_matlabParamToPropery[property]; + if (prop) { + d_ptr->m_matlabParamToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToMatlabParam.remove(property); + + prop = d_ptr->m_wavePathToPropery[property]; + if (prop) { + d_ptr->m_wavePathToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToWavePath.remove(property); + + prop = d_ptr->m_reportPathToPropery[property]; + if (prop) { + d_ptr->m_reportPathToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToReportPath.remove(property); + + prop = d_ptr->m_rdPathToPropery[property]; + if (prop) { + d_ptr->m_rdPathToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToRDPath.remove(property); + + // Cleanup grouped file properties + auto cleanupGroup = [&](QMap& propToGroup, + QMap& groupToProp, + QMap& propToCount, + QMap& countToProp, + QMap>& propToPaths, + QMap& pathToProp, + QMap& pathIndex) { + QtProperty* group = propToGroup.value(property, nullptr); + if (group) { + propToGroup.remove(property); + groupToProp.remove(group); + } + QtProperty* countProp = propToCount.value(property, nullptr); + if (countProp) { + countToProp.remove(countProp); + propToCount.remove(property); + delete countProp; + } + QVector& paths = propToPaths[property]; + for (QtProperty* p : paths) { + pathIndex.remove(p); + pathToProp.remove(p); + delete p; + } + paths.clear(); + propToPaths.remove(property); + if (group) delete group; + }; + + cleanupGroup(d_ptr->m_properyToCurveGroup, d_ptr->m_curveGroupToPropery, + d_ptr->m_properyToCurveCount, d_ptr->m_curveCountToPropery, + d_ptr->m_properyToCurvePaths, d_ptr->m_curvePathToPropery, d_ptr->m_curvePathIndex); + cleanupGroup(d_ptr->m_properyToSurfaceGroup, d_ptr->m_surfaceGroupToPropery, + d_ptr->m_properyToSurfaceCount, d_ptr->m_surfaceCountToPropery, + d_ptr->m_properyToSurfacePaths, d_ptr->m_surfacePathToPropery, d_ptr->m_surfacePathIndex); + cleanupGroup(d_ptr->m_properyToTableGroup, d_ptr->m_tableGroupToPropery, + d_ptr->m_properyToTableCount, d_ptr->m_tableCountToPropery, + d_ptr->m_properyToTablePaths, d_ptr->m_tablePathToPropery, d_ptr->m_tablePathIndex); + 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); } #pragma endregion diff --git a/src/ui/PropertyBrowser/qtpropertymanager.h b/src/ui/PropertyBrowser/qtpropertymanager.h index 1c9edfee..c1fabc4e 100644 --- a/src/ui/PropertyBrowser/qtpropertymanager.h +++ b/src/ui/PropertyBrowser/qtpropertymanager.h @@ -1027,6 +1027,8 @@ public: QtStringPropertyManager* subStringProperyManager() const; QtFilesPropertyManager* subFilesProperyManager() const; + QtIntPropertyManager* subIntProperyManager() const; + QtGroupPropertyManager* subGroupProperyManager() const; public Q_SLOTS: void setValue(QtProperty* property, const QWorkspaceAttribute& val); @@ -1042,6 +1044,7 @@ private: Q_DECLARE_PRIVATE(QtWorkspacePropertyManager) Q_DISABLE_COPY_MOVE(QtWorkspacePropertyManager) Q_PRIVATE_SLOT(d_func(), void slotStringChanged(QtProperty*, QString)) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty*, int)) }; #pragma endregion diff --git a/src/ui/PropertyBrowser/qtworkspaceattribute.cpp b/src/ui/PropertyBrowser/qtworkspaceattribute.cpp index 1b1ddc29..d4b7f89f 100644 --- a/src/ui/PropertyBrowser/qtworkspaceattribute.cpp +++ b/src/ui/PropertyBrowser/qtworkspaceattribute.cpp @@ -14,15 +14,18 @@ QWorkspaceAttribute::QWorkspaceAttribute(class WorkSpace* workspace) : workspace_(workspace) { - + if (workspace_) { + filesSeq_ = workspace_->GetFilesSeq(); + } } bool QWorkspaceAttribute::operator==(const QWorkspaceAttribute& other) { - return workspace_ == other.workspace_; + return workspace_ == other.workspace_ && filesSeq_ == other.filesSeq_; } QWorkspaceAttribute& QWorkspaceAttribute::operator=(const QWorkspaceAttribute& other) { workspace_ = other.workspace_; + filesSeq_ = other.filesSeq_; return *this; } @@ -163,7 +166,7 @@ void QWorkspaceAttribute::SetRDPath(const QString& path) workspace_->SetRDPath(path); } -const QString QWorkspaceAttribute::GetRDPath() const +const QString QWorkspaceAttribute::GetRDPath() const { if (nullptr == workspace_) { return ""; @@ -171,6 +174,34 @@ const QString QWorkspaceAttribute::GetRDPath() const return workspace_->GetRDPath(); } +std::vector QWorkspaceAttribute::GetFileEntries(FileEntryType type) const { + if (nullptr == workspace_) { + return {}; + } + return workspace_->GetFileEntries(type); +} + +void QWorkspaceAttribute::SetFileEntryCount(FileEntryType type, int count) { + if (nullptr == workspace_) { + return; + } + workspace_->SetFileEntryCount(type, count); +} + +void QWorkspaceAttribute::SetFileEntryPath(FileEntryType type, int index, const QString& path) { + if (nullptr == workspace_) { + return; + } + workspace_->SetFileEntryPath(type, index, path); +} + +QString QWorkspaceAttribute::GetFileEntryAbsPath(FileEntryType type, int index) const { + if (nullptr == workspace_) { + return QString(); + } + return workspace_->GetFileEntryAbsPath(type, index); +} + QTransformAttribute::QTransformAttribute(class Transform* obj) : object_(obj) { diff --git a/src/ui/PropertyBrowser/qtworkspaceattribute.h b/src/ui/PropertyBrowser/qtworkspaceattribute.h index 7cd45748..397a3ccc 100644 --- a/src/ui/PropertyBrowser/qtworkspaceattribute.h +++ b/src/ui/PropertyBrowser/qtworkspaceattribute.h @@ -43,6 +43,9 @@ #include #include #include +#include "workspace/FileEntry.h" +#include +#include class QWorkspaceAttribute { public: @@ -76,8 +79,16 @@ public: void SetRDPath(const QString& path); const QString GetRDPath() const; + + // Grouped files API + std::vector GetFileEntries(FileEntryType type) const; + void SetFileEntryCount(FileEntryType type, int count); + void SetFileEntryPath(FileEntryType type, int index, const QString& path); + QString GetFileEntryAbsPath(FileEntryType type, int index) const; private: class WorkSpace* workspace_{ nullptr }; + // Snapshot of workspace files change sequence to detect mutations + std::uint64_t filesSeq_{ 0 }; }; diff --git a/src/ui/WorkSpace/WorkSpaceDlg.cpp b/src/ui/WorkSpace/WorkSpaceDlg.cpp index 4918bb9e..1460c7e2 100644 --- a/src/ui/WorkSpace/WorkSpaceDlg.cpp +++ b/src/ui/WorkSpace/WorkSpaceDlg.cpp @@ -33,6 +33,7 @@ void WorkSpaceDlg::InitConnect() { connect(ui->pbSure, &QPushButton::clicked, this, &WorkSpaceDlg::OnSure); connect(ui->pbCancel, &QPushButton::clicked, this, &WorkSpaceDlg::reject); connect(ui->tbPath, &QPushButton::clicked, this, &WorkSpaceDlg::OnSelectSavePath); + connect(ui->tbCommondPath, &QPushButton::clicked, this, &WorkSpaceDlg::OnSelectCommondPath); connect(ui->leName, &QLineEdit::textChanged, [this](const QString& txt) { QString path = QString("%1/%2").arg(path_).arg(txt); ui->lePath->setText(path); @@ -93,6 +94,9 @@ void WorkSpaceDlg::OnSure() { workspacePath += QString("/%1.dyt").arg(name); WorkSpace* workSpace = WorkSpaceManager::Get().GetOrCreate(workspacePath, name); workSpace->SetDescribe(ui->etDescribe->toPlainText()); + workSpace->SetCommondFilePath(commondPath_); + // Execute commands configured for onCreate right after workspace is set up + workSpace->ExecuteCommands(WorkSpace::CommandWhen::OnCreate); WorkSpaceManager::Get().SetCurrent(workSpace); accept(); @@ -110,23 +114,20 @@ void WorkSpaceDlg::OnSelectSavePath() { ui->lePath->setText(QString("%1/%2").arg(path_).arg(ui->leName->text())); LOG_INFO("save path: {}", path_.toLocal8Bit().constData()); } -// -//void WorkSpaceDlg::InitFrame() { -// FrameTitleBar* titleBar = new FrameTitleBar(this); -// titleBar->SetMainWidget(this); -// -// titleBar->SetSysButton(FrameTitleBar::FTB_ICON | FrameTitleBar::FTB_CLOSE); -// -// QVBoxLayout* layout = new QVBoxLayout(this); -// layout->setContentsMargins(0, 0, 0, 0); -// layout->setSpacing(0); -// -// layout->setStretch(0, 0); -// layout->setStretch(1, 1); -// layout->setAlignment(Qt::AlignLeft | Qt::AlignTop); -// SetTitleBar(titleBar); -// -// QWidget* mainDilag_ = new QWidget(this); -// layout->addWidget(mainDilag_, 1); -// ui->setupUi(mainDilag_); -//} + +void WorkSpaceDlg::OnSelectCommondPath() { + const QString workspacePath = Application::GetWorkSpacePath(); + const QString xmlPath = QFileDialog::getOpenFileName( + this, + tr("select command xml file"), + workspacePath, + tr("XML files (*.xml);;All files (*.*)")); + if (xmlPath.isEmpty()) { + LOG_WARN("command xml file is empty"); + return; + } + commondPath_ = xmlPath; + ui->leCommondPath->setText(commondPath_); + LOG_INFO("select command xml: {}", commondPath_.toLocal8Bit().constData()); +} + diff --git a/src/ui/WorkSpace/WorkSpaceDlg.h b/src/ui/WorkSpace/WorkSpaceDlg.h index 1719ae5f..5f5dd525 100644 --- a/src/ui/WorkSpace/WorkSpaceDlg.h +++ b/src/ui/WorkSpace/WorkSpaceDlg.h @@ -18,10 +18,12 @@ protected: void OnSure(); void OnSelectSavePath(); + void OnSelectCommondPath(); //void InitFrame(); private: Ui::WorkSpaceDlg* ui; QString path_; + QString commondPath_; }; \ No newline at end of file diff --git a/src/ui/WorkSpace/WorkSpaceDlg.ui b/src/ui/WorkSpace/WorkSpaceDlg.ui index cc58cbd1..ed2de71d 100644 --- a/src/ui/WorkSpace/WorkSpaceDlg.ui +++ b/src/ui/WorkSpace/WorkSpaceDlg.ui @@ -6,8 +6,8 @@ 0 0 - 345 - 243 + 528 + 418 @@ -66,6 +66,37 @@ + + + + + + commond Path + + + + + + + + + + true + + + select commond file path + + + + + + + ... + + + + + @@ -113,8 +144,6 @@ - - - + diff --git a/src/utils/UiLayoutManager.cpp b/src/utils/UiLayoutManager.cpp new file mode 100644 index 00000000..db35dd40 --- /dev/null +++ b/src/utils/UiLayoutManager.cpp @@ -0,0 +1,38 @@ +#include "UiLayoutManager.h" + +#include +#include +#include + +#include "common/RecourceHelper.h" + +namespace { +static inline QString layoutIniPath() { + return RecourceHelper::Get().GetBasePath() + "/config/UIState.ini"; +} +} + +void UiLayoutManager::Save(QMainWindow* mainWindow, int version) { + if (!mainWindow) return; + const QString iniPath = layoutIniPath(); + QSettings settings(iniPath, QSettings::IniFormat); + settings.setValue("MainWindow/geometry", mainWindow->saveGeometry()); + settings.setValue("MainWindow/state", mainWindow->saveState(version)); +} + +void UiLayoutManager::Restore(QMainWindow* mainWindow, int version) { + if (!mainWindow) return; + const QString iniPath = layoutIniPath(); + if (!QFile::exists(iniPath)) { + return; + } + QSettings settings(iniPath, QSettings::IniFormat); + const QByteArray geometry = settings.value("MainWindow/geometry").toByteArray(); + if (!geometry.isEmpty()) { + mainWindow->restoreGeometry(geometry); + } + const QByteArray state = settings.value("MainWindow/state").toByteArray(); + if (!state.isEmpty()) { + mainWindow->restoreState(state, version); + } +} \ No newline at end of file diff --git a/src/utils/UiLayoutManager.h b/src/utils/UiLayoutManager.h new file mode 100644 index 00000000..4d1796e7 --- /dev/null +++ b/src/utils/UiLayoutManager.h @@ -0,0 +1,13 @@ +#pragma once + +#include +class QMainWindow; + +class UiLayoutManager { +public: + // 保存主窗口布局到 workspace/UIState.ini + static void Save(QMainWindow* mainWindow, int version = 1); + + // 从 workspace/UIState.ini 恢复布局 + static void Restore(QMainWindow* mainWindow, int version = 1); +}; \ No newline at end of file diff --git a/src/viewer/OsgWidget.cpp b/src/viewer/OsgWidget.cpp index c2902f79..2e484e25 100644 --- a/src/viewer/OsgWidget.cpp +++ b/src/viewer/OsgWidget.cpp @@ -46,6 +46,8 @@ OsgWidget::OsgWidget(QWidget* parent, Qt::WindowFlags f) connect( &timer_, SIGNAL(timeout()), this, SLOT(update()) ); timer_.start( 10 ); + + setMinimumSize(100, 100); LOG_INFO("OsgWidget::OsgWidget"); } diff --git a/src/workspace/CommandExecutor.cpp b/src/workspace/CommandExecutor.cpp new file mode 100644 index 00000000..d8d34db5 --- /dev/null +++ b/src/workspace/CommandExecutor.cpp @@ -0,0 +1,68 @@ +#include "workspace/CommandExecutor.h" + +#include +#include +#include + +#include "common/SpdLogger.h" + +void CommandExecutor::Execute(WorkSpace* ws, WorkSpace::CommandWhen when) { + if (!ws) return; + if (!cmd_.enabled) return; + + const QString whenStr = (when == WorkSpace::CommandWhen::OnCreate) ? QStringLiteral("oncreate") : QStringLiteral("onload"); + + // Build final arguments (already prepared by manager but honor rawArgs if provided) + QStringList argsList = cmd_.args; + auto pushArgs = [&argsList](const QString& s) { + if (!s.isEmpty()) { + for (const auto& part : s.split(' ', Qt::SkipEmptyParts)) { + argsList << part; + } + } + }; + if (!cmd_.rawArgs.isEmpty()) { + pushArgs(cmd_.rawArgs); + } + + const QString programLower = cmd_.program.toLower(); + if (!cmd_.path.isEmpty()) { + if (programLower.endsWith("cmd.exe")) { + argsList << "/c" << cmd_.path; + } else if (programLower.endsWith("powershell.exe")) { + argsList << "-NoProfile" << "-ExecutionPolicy" << "Bypass" << "-File" << cmd_.path; + } else { + argsList << cmd_.path; + } + } + + QProcess proc; + // Apply environment if provided + if (!cmd_.env.empty()) { + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + for (auto it = cmd_.env.begin(); it != cmd_.env.end(); ++it) { + env.insert(it.key(), it.value()); + } + proc.setProcessEnvironment(env); + } + + proc.setProgram(cmd_.program); + proc.setArguments(argsList); + proc.setWorkingDirectory(cmd_.workingDir.isEmpty() ? ws->GetDir() : cmd_.workingDir); + LOG_INFO("run command: name={} prog={} args={} cwd={} when={} desc={}", + cmd_.name.toLocal8Bit().constData(), + cmd_.program.toLocal8Bit().constData(), + argsList.join(' ').toLocal8Bit().constData(), + proc.workingDirectory().toLocal8Bit().constData(), + whenStr.toLocal8Bit().constData(), + cmd_.descript.toLocal8Bit().constData()); + proc.start(); + if (!proc.waitForStarted()) { + LOG_WARN("command failed to start: {}", cmd_.program.toLocal8Bit().constData()); + return; + } + proc.waitForFinished(cmd_.timeoutMs); + const QByteArray out = proc.readAllStandardOutput(); + const QByteArray err = proc.readAllStandardError(); + LOG_INFO("command '{}' exitCode={} stdout={} stderr={}", cmd_.name.toLocal8Bit().constData(), proc.exitCode(), out.constData(), err.constData()); +} \ No newline at end of file diff --git a/src/workspace/CommandExecutor.h b/src/workspace/CommandExecutor.h new file mode 100644 index 00000000..14a03bc9 --- /dev/null +++ b/src/workspace/CommandExecutor.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +#include "workspace/WorkSpace.h" + +// Merge Command model into this header to reduce files +struct Command { + QString name; + QString program; + QStringList args; // final argument list to pass to QProcess + QString rawArgs; // original args string from XML (optional) + QString path; // script or executable path + QString workingDir; // working directory + bool enabled{true}; + QMap env; // environment key/value pairs + QString descript; // description + int timeoutMs{30000}; // default 30s +}; + +class CommandExecutor { +public: + explicit CommandExecutor(const Command& cmd) : cmd_(cmd) {} + void Execute(WorkSpace* ws, WorkSpace::CommandWhen when); + + const Command& Get() const { return cmd_; } + +private: + Command cmd_; +}; \ No newline at end of file diff --git a/src/workspace/CommandManager.cpp b/src/workspace/CommandManager.cpp new file mode 100644 index 00000000..f5313b9d --- /dev/null +++ b/src/workspace/CommandManager.cpp @@ -0,0 +1,117 @@ +#include "workspace/CommandManager.h" + +#include + +#include "xml/tinyxml2.h" +#include "common/SpdLogger.h" + +static QMap parseEnvAttr(const QString& envAttr) { + QMap env; + if (envAttr.isEmpty()) return env; + const auto pairs = envAttr.split(';', Qt::SkipEmptyParts); + for (const auto& p : pairs) { + const auto kv = p.split('=', Qt::KeepEmptyParts); + if (kv.size() >= 2) env.insert(kv[0].trimmed(), kv[1].trimmed()); + } + return env; +} + +void CommandManager::Reload(WorkSpace* ws) { + onCreate_.clear(); + onLoad_.clear(); + if (!ws) return; + + const QString cmdPath = ws->GetCommondFilePath(); + if (cmdPath.isEmpty()) { + LOG_INFO("no command xml configured"); + return; + } + QFileInfo fi(cmdPath); + if (!fi.exists() || !fi.isFile()) { + LOG_WARN("command xml not found: {}", cmdPath.toLocal8Bit().constData()); + return; + } + + tinyxml2::XMLDocument doc; + auto rc = doc.LoadFile(cmdPath.toLocal8Bit().constData()); + if (rc != tinyxml2::XML_SUCCESS) { + LOG_WARN("load command xml failed: {} rc:{}", cmdPath.toLocal8Bit().constData(), static_cast(rc)); + return; + } + auto* root = doc.RootElement(); + if (!root) { + LOG_WARN("command xml has no root: {}", cmdPath.toLocal8Bit().constData()); + return; + } + + for (auto* node = root->FirstChildElement(); node; node = node->NextSiblingElement()) { + const char* tag = node->Name(); + if (!tag) continue; + QString tagQ = QString::fromUtf8(tag).toLower(); + if (tagQ != QLatin1String("commond") && tagQ != QLatin1String("command")) continue; + + Command cmd; + if (const char* nameAttr = node->Attribute("name")) cmd.name = QString::fromUtf8(nameAttr); + if (const char* exeAttr = node->Attribute("exe")) cmd.program = QString::fromUtf8(exeAttr); + if (cmd.program.isEmpty()) { + if (const char* programAttr = node->Attribute("program")) cmd.program = QString::fromUtf8(programAttr); + } + if (const char* pathAttr = node->Attribute("path")) cmd.path = QString::fromUtf8(pathAttr); + if (cmd.path.isEmpty()) { + if (const char* pathTypo = node->Attribute("paht")) cmd.path = QString::fromUtf8(pathTypo); + } + if (const char* argsAttr = node->Attribute("args")) cmd.rawArgs = QString::fromUtf8(argsAttr); + if (const char* cwdAttr = node->Attribute("workingDir")) cmd.workingDir = QString::fromUtf8(cwdAttr); + if (cmd.workingDir.isEmpty()) { + if (const char* cwdAttr2 = node->Attribute("cwd")) cmd.workingDir = QString::fromUtf8(cwdAttr2); + } + if (const char* enabledAttr = node->Attribute("enabled")) { + QString en = QString::fromUtf8(enabledAttr).toLower(); + cmd.enabled = !(en == QLatin1String("false") || en == QLatin1String("0")); + } + if (const char* descAttr = node->Attribute("descript")) cmd.descript = QString::fromUtf8(descAttr); + if (cmd.descript.isEmpty()) { + if (const char* desc2 = node->Attribute("description")) cmd.descript = QString::fromUtf8(desc2); + } + if (const char* timeoutAttr = node->Attribute("timeoutSec")) { + bool ok = false; int v = QString::fromUtf8(timeoutAttr).toInt(&ok); + if (ok && v > 0) cmd.timeoutMs = v * 1000; + } + // env: either attribute env="KEY=VAL;K2=V2" or child elements + if (const char* envAttr = node->Attribute("env")) { + cmd.env = parseEnvAttr(QString::fromUtf8(envAttr)); + } + for (auto* envNode = node->FirstChildElement("env"); envNode; envNode = envNode->NextSiblingElement("env")) { + const char* k = envNode->Attribute("key"); + const char* v = envNode->Attribute("value"); + if (k && v) cmd.env.insert(QString::fromUtf8(k), QString::fromUtf8(v)); + } + + // Pre-build args list from rawArgs (actual insertion of path happens in executor) + if (!cmd.rawArgs.isEmpty()) { + for (const auto& part : cmd.rawArgs.split(' ', Qt::SkipEmptyParts)) { + cmd.args << part; + } + } + + // when routing + WorkSpace::CommandWhen target = WorkSpace::CommandWhen::OnCreate; // default + if (const char* whenAttr = node->Attribute("when")) { + QString wa = QString::fromUtf8(whenAttr).toLower(); + if (wa == QLatin1String("onload")) target = WorkSpace::CommandWhen::OnLoad; + } + + auto exec = std::make_unique(cmd); + if (target == WorkSpace::CommandWhen::OnCreate) onCreate_.push_back(std::move(exec)); + else onLoad_.push_back(std::move(exec)); + } +} + +void CommandManager::Execute(WorkSpace* ws, WorkSpace::CommandWhen when) { + // Reload each time to reflect latest XML + Reload(ws); + auto& list = (when == WorkSpace::CommandWhen::OnCreate) ? onCreate_ : onLoad_; + for (auto& exec : list) { + exec->Execute(ws, when); + } +} \ No newline at end of file diff --git a/src/workspace/CommandManager.h b/src/workspace/CommandManager.h new file mode 100644 index 00000000..178f2fa3 --- /dev/null +++ b/src/workspace/CommandManager.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include "workspace/WorkSpace.h" +#include "workspace/CommandExecutor.h" + +class CommandManager { +public: + void Reload(WorkSpace* ws); + void Execute(WorkSpace* ws, WorkSpace::CommandWhen when); + +private: + std::vector> onCreate_; + std::vector> onLoad_; +}; \ No newline at end of file diff --git a/src/workspace/FileEntry.h b/src/workspace/FileEntry.h new file mode 100644 index 00000000..51495ff6 --- /dev/null +++ b/src/workspace/FileEntry.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +enum class FileEntryType { + Curve, + Surface, + Table, + Light +}; + +struct FileEntry { + FileEntryType type; + QString fileName; // relative file name under workspace dir; may be empty +}; + +inline const char* FileEntryTypeToString(FileEntryType t) { + switch (t) { + case FileEntryType::Curve: return "curve"; + case FileEntryType::Surface: return "surface"; + case FileEntryType::Table: return "table"; + case FileEntryType::Light: return "light"; + } + return "unknown"; +} + +inline bool FileEntryTypeFromString(const char* s, FileEntryType& out) { + if (!s) return false; + if (0 == strcmp(s, "curve")) { out = FileEntryType::Curve; return true; } + 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; } + return false; +} \ No newline at end of file diff --git a/src/workspace/WorkSpace.cpp b/src/workspace/WorkSpace.cpp index 9836479f..b971b926 100644 --- a/src/workspace/WorkSpace.cpp +++ b/src/workspace/WorkSpace.cpp @@ -6,6 +6,7 @@ #include "workspace/WorkSpaceXMLParse.h" #include "workspace/WorkSpaceXMLWrite.h" +#include "workspace/CommandManager.h" #include "workspace/WorkSpaceItem.h" #include "workspace/Timestep.h" @@ -15,6 +16,7 @@ #include "common/SpdLogger.h" #include "entities/Entity.h" #include "utils/FileUtils.h" +#include //#include "workspace/WorkSpaceItemGroup.h" //#include "workspace/WorkSpaceRiverGroup.h" //#include "workspace/WorkSpaceRiverNetGroup.h" @@ -25,6 +27,7 @@ WorkSpace::WorkSpace(QObject* parent) noexcept : QObject(parent) { uuid_ = QUuid::createUuid().toString(); homeViewpoint_ = osgEarth::Viewpoint("home", 120.000000, 25.000000, 100.000000, -2.500000, -90.000000, 8200000.000000); + cmdMgr_ = std::make_unique(); } WorkSpace::WorkSpace(const QString& path, QObject* parent) @@ -32,6 +35,7 @@ WorkSpace::WorkSpace(const QString& path, QObject* parent) , path_(path){ uuid_ = QUuid::createUuid().toString(); homeViewpoint_ = osgEarth::Viewpoint("home", 120.000000, 25.000000, 100.000000, -2.500000, -90.000000, 8200000.000000); + cmdMgr_ = std::make_unique(); } const QString WorkSpace::GetDir() const { @@ -39,6 +43,22 @@ const QString WorkSpace::GetDir() const { return info.absolutePath(); } +void WorkSpace::SetCommondFilePath(const QString& path) { + QFileInfo fileInfo(path); + QString dirPath = QString("%1/%2").arg(GetDir(), fileInfo.fileName()); + bool sucess = FileUtils::CopyFileToPath(path, dirPath, true); + LOG_INFO("copy commond file {}: {} to {}", + path.toLocal8Bit().data(), + dirPath.toLocal8Bit().data(), + sucess); + commondPath_ = fileInfo.fileName(); +} + +const QString WorkSpace::GetCommondFilePath() const { + QString path = QString("%1/%2").arg(GetDir(), commondPath_); + return path; +} + void WorkSpace::SetSimMatlab(const QString& path) { QFileInfo fileInfo(path); QString dirPath = QString("%1/%2").arg(GetDir(), fileInfo.fileName()); @@ -103,6 +123,87 @@ void WorkSpace::SetRDPath(const QString& path) rdFile_ = fileInfo.fileName(); } +std::vector WorkSpace::GetFileEntries(FileEntryType type) const { + auto it = files_.find(type); + if (it == files_.end()) { + return {}; + } + return it->second; +} + +WorkSpace::FileEntryResult WorkSpace::CreateFileEntry(FileEntryType type) { + auto& vec = files_[type]; + if (vec.size() >= 9) { + return FileEntryResult::LimitExceeded; + } + // push a placeholder; actual filename may be set elsewhere via SetWavePath/SetRDPath/etc + vec.push_back(FileEntry{ type, QString() }); + ++filesSeq_; + // Notify listeners (e.g., PropertyBrowser) to refresh workspace properties + emit FilesChanged(type); + return FileEntryResult::Ok; +} + +bool WorkSpace::SetFileEntryCount(FileEntryType type, int count) { + if (count < 0) count = 0; + if (count > 9) count = 9; + auto& vec = files_[type]; + if (static_cast(vec.size()) == count) { + return true; + } + if (static_cast(vec.size()) < count) { + int toAdd = count - static_cast(vec.size()); + for (int i = 0; i < toAdd; ++i) { + vec.push_back(FileEntry{ type, QString() }); + } + } else { + vec.resize(count); + } + ++filesSeq_; + emit FilesChanged(type); + return true; +} + +bool WorkSpace::SetFileEntryPath(FileEntryType type, int index, const QString& path) { + auto& vec = files_[type]; + if (index < 0 || index >= static_cast(vec.size())) { + return false; + } + QFileInfo fileInfo(path); + if (!fileInfo.exists()) { + return false; + } + QString dirPath = QString("%1/%2").arg(GetDir(), fileInfo.fileName()); + bool sucess = FileUtils::CopyFileToPath(path, dirPath, true); + LOG_INFO("copy grouped file {}: {} to {}", + path.toLocal8Bit().data(), + dirPath.toLocal8Bit().data(), + sucess); + if (!sucess) { + return false; + } + vec[index].fileName = fileInfo.fileName(); + ++filesSeq_; + emit FilesChanged(type); + return true; +} + +QString WorkSpace::GetFileEntryAbsPath(FileEntryType type, int index) const { + auto it = files_.find(type); + if (it == files_.end()) { + return QString(); + } + const auto& vec = it->second; + if (index < 0 || index >= static_cast(vec.size())) { + return QString(); + } + const QString& name = vec[index].fileName; + if (name.isEmpty()) { + return QString(); + } + return QString("%1/%2").arg(GetDir(), name); +} + const QString WorkSpace::GetRDPath() const { QString path = QString("%1/%2").arg(GetDir(), rdFile_); @@ -282,4 +383,13 @@ void WorkSpace::OnLoaded() { if (nullptr != timestep_) { emit TimestepChanged(timestep_); } + // Execute commands configured for onLoad + ExecuteCommands(CommandWhen::OnLoad); +} + +void WorkSpace::ExecuteCommands(CommandWhen when) { + if (!cmdMgr_) { + cmdMgr_ = std::make_unique(); + } + cmdMgr_->Execute(this, when); } diff --git a/src/workspace/WorkSpace.h b/src/workspace/WorkSpace.h index b5b44259..e649b2ba 100644 --- a/src/workspace/WorkSpace.h +++ b/src/workspace/WorkSpace.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include @@ -9,10 +11,12 @@ #include "scene/OEScene.h" #include "config.h" #include "common/SpdLogger.h" +#include "workspace/FileEntry.h" //#include "../ui/chartPlot/DYTChart.h" class WorkSpaceItem; +class CommandManager; class WorkSpace : public QObject { Q_OBJECT @@ -46,6 +50,14 @@ public: inline const QString& GetDescribe() const { return describe_; } + + void SetCommondFilePath(const QString& path); + const QString GetCommondFilePath() const; + + // Execute command xml according to trigger + enum class CommandWhen { OnCreate, OnLoad }; + void ExecuteCommands(CommandWhen when); + void SetSimMatlab(const QString& path); const QString GetSimMatlab() const; @@ -65,6 +77,16 @@ public: void SetRDPath(const QString& path); const QString GetRDPath() const; + // Files list API (per-type, max 9 per type) + enum class FileEntryResult { Ok, LimitExceeded, Duplicate, CopyFailed }; + FileEntryResult CreateFileEntry(FileEntryType type); + std::vector GetFileEntries(FileEntryType type) const; + + // Manage grouped file entries + bool SetFileEntryCount(FileEntryType type, int count); + bool SetFileEntryPath(FileEntryType type, int index, const QString& path); + QString GetFileEntryAbsPath(FileEntryType type, int index) const; + inline void SetHomeViewpoint(const osgEarth::Viewpoint& viewpoint) { homeViewpoint_ = viewpoint; homeViewpoint_.setHeading(0.0); // Ensure heading is set to 0.0 @@ -119,10 +141,12 @@ public: void OnLoaded(); Q_SIGNALS: - void EntityAdded(class Entity* entity); - void EntityRemoved(class Entity* entity); - void TimestepChanged(class Timestep* timestep); - void LampStatusChanged(class LampStatus* lampStatus); + void EntityAdded(class Entity* entity); + void EntityRemoved(class Entity* entity); + void TimestepChanged(class Timestep* timestep); + void LampStatusChanged(class LampStatus* lampStatus); + // Emitted when grouped file entries change (count or path or creation) + void FilesChanged(FileEntryType type); protected: const QString& GetSimMatlabName() const { @@ -134,6 +158,7 @@ private: QString uuid_; QString describe_; QString path_; + QString commondPath_; QString simMatlabPath_; QString waveFile_; @@ -144,11 +169,19 @@ private: osgEarth::Viewpoint homeViewpoint_; bool leaded_{ false }; - std::vector entities_; - OEScene* scene_{ nullptr }; - class Timestep* timestep_{ nullptr }; - class LampStatus* lampStatus_{ nullptr }; + std::vector entities_; + OEScene* scene_{ nullptr }; + class Timestep* timestep_{ nullptr }; + class LampStatus* lampStatus_{ nullptr }; class Entity* trackedEntity_{ nullptr }; + // Stored as file entries under workspace dir, keyed by type + std::map> files_; + // Monotonic sequence for file entries changes, used to trigger UI refresh + std::uint64_t filesSeq_{ 0 }; + // Executor for command XML actions + std::unique_ptr cmdMgr_; +public: + std::uint64_t GetFilesSeq() const { return filesSeq_; } friend class WorkSpaceXMLWrite; }; diff --git a/src/workspace/WorkSpaceXMLParse.cpp b/src/workspace/WorkSpaceXMLParse.cpp index e3321cfc..f4089e24 100644 --- a/src/workspace/WorkSpaceXMLParse.cpp +++ b/src/workspace/WorkSpaceXMLParse.cpp @@ -84,6 +84,30 @@ bool WorkSpaceXMLParse::ParseLamp(const tinyxml2::XMLElement* element) { return workSpace_->SetLampPath(path); } +bool WorkSpaceXMLParse::ParseFiles(const tinyxml2::XMLElement* element) { + if (nullptr == element) { + LOG_WARN("element is nullptr"); + return false; + } + + const tinyxml2::XMLElement* typeElement = element->FirstChildElement("type"); + while (nullptr != typeElement) { + const char* name = typeElement->Attribute("name"); + int count = 0; + typeElement->QueryIntAttribute("count", &count); + if (nullptr != name && count > 0) { + FileEntryType enumType; + if (FileEntryTypeFromString(name, enumType)) { + for (int i = 0; i < count; ++i) { + workSpace_->CreateFileEntry(enumType); + } + } + } + typeElement = typeElement->NextSiblingElement("type"); + } + return true; +} + bool WorkSpaceXMLParse::ParseEntities(const tinyxml2::XMLElement* element) { if (nullptr == element) { LOG_WARN("element is nullptr"); @@ -238,6 +262,9 @@ bool WorkSpaceXMLParse::Load(const QString& dyt) { else if (0 == strcmp(name, "SimMatlab")) { ParseSimMatlab(xmlElement); } + else if (0 == strcmp(name, "files")) { + ParseFiles(xmlElement); + } xmlElement = xmlElement->NextSiblingElement(); } diff --git a/src/workspace/WorkSpaceXMLParse.h b/src/workspace/WorkSpaceXMLParse.h index 60f4b468..626af5b2 100644 --- a/src/workspace/WorkSpaceXMLParse.h +++ b/src/workspace/WorkSpaceXMLParse.h @@ -35,6 +35,7 @@ private: bool ParseChart(const tinyxml2::XMLElement* element); bool ParseReport(const tinyxml2::XMLElement* element); bool ParseSimMatlab(const tinyxml2::XMLElement* element); + bool ParseFiles(const tinyxml2::XMLElement* element); private: QString name_; diff --git a/src/workspace/WorkSpaceXMLWrite.cpp b/src/workspace/WorkSpaceXMLWrite.cpp index 917be532..dc4e2625 100644 --- a/src/workspace/WorkSpaceXMLWrite.cpp +++ b/src/workspace/WorkSpaceXMLWrite.cpp @@ -10,6 +10,7 @@ #include "utils/StringUtils.h" #include "workspace/WorkSpaceManager.h" +#include "workspace/FileEntry.h" WorkSpaceXMLWrite::WorkSpaceXMLWrite(WorkSpace* workspace, QObject* parent) noexcept : QObject(parent) @@ -35,6 +36,7 @@ bool WorkSpaceXMLWrite::Save(const QString& path) { SaveChart(scene, &doc); SaveTimeStep(scene); SaveLamp(scene); + SaveFiles(scene, &doc); tinyxml2::XMLElement* entitiesXml = scene->InsertNewChildElement("entities"); std::vector& entities = workSpace_->GetEntities(); @@ -120,3 +122,19 @@ bool WorkSpaceXMLWrite::SaveChart(tinyxml2::XMLElement* scene, tinyxml2::XMLDocu return true; } + +bool WorkSpaceXMLWrite::SaveFiles(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc) { + // Persist multi-file entries per type as counts + tinyxml2::XMLElement* files = doc->NewElement("files"); + scene->LinkEndChild(files); + + for (const auto& kv : workSpace_->files_) { + const FileEntryType type = kv.first; + const auto& vec = kv.second; + tinyxml2::XMLElement* typeElem = doc->NewElement("type"); + typeElem->SetAttribute("name", FileEntryTypeToString(type)); + typeElem->SetAttribute("count", static_cast(vec.size())); + files->LinkEndChild(typeElem); + } + return true; +} diff --git a/src/workspace/WorkSpaceXMLWrite.h b/src/workspace/WorkSpaceXMLWrite.h index c3cf6ab9..2e3785e8 100644 --- a/src/workspace/WorkSpaceXMLWrite.h +++ b/src/workspace/WorkSpaceXMLWrite.h @@ -16,11 +16,12 @@ public: bool Save(const QString& path); protected: - bool SaveScene(tinyxml2::XMLElement* scene); - bool SaveTimeStep(tinyxml2::XMLElement* scene); - bool SaveLamp(tinyxml2::XMLElement* scene); - bool SaveEntities(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc); - bool SaveChart(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc); + bool SaveScene(tinyxml2::XMLElement* scene); + bool SaveTimeStep(tinyxml2::XMLElement* scene); + bool SaveLamp(tinyxml2::XMLElement* scene); + bool SaveEntities(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc); + bool SaveChart(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc); + bool SaveFiles(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc); private: WorkSpace* workSpace_;