diff --git a/src/app/main.cpp b/src/app/main.cpp index 34ad9a2..1e83e22 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -1300,6 +1300,25 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re if (!applyFittedAxes(dsId)) sceneView->showSceneAxes(); renderWindowPtr->Render(); }); + // 双击 DS(决策6/T4):(a) 相机适配到该 ds 子树空间范围(复用 T2 subtreeDsIds + T1 + // datasetBounds/fitToBounds,与选中贴合轴同一子树盒);(b) 有详情页的类型联动打开中下方详情面板, + // 三维体等无详情页类型静默——gate 在联动入口(detailCtrl.supports),不走 openDataset→loadFailed + // 的状态栏提示。属性弹窗改由右键「详情」(detailRequested) 触发,双击不再弹属性框。 + QObject::connect(analysisTab, &geopro::app::CategoryAnalysisTab::datasetActivated, &window, + [sceneView, renderWindowPtr, analysisTab, &detailCtrl]( + const QString& dsId, const QString& ddCode, const QString& name) { + const QStringList sub = analysisTab->subtreeDsIds(dsId); + std::vector ids; + ids.reserve(static_cast(sub.size())); + for (const QString& s : sub) ids.push_back(s.toStdString()); + double box[6]; + if (!ids.empty() && sceneView->datasetBounds(ids, box)) { // 未渲染/无盒 → 跳过适配(静默) + sceneView->fitToBounds(box); + renderWindowPtr->Render(); + } + if (detailCtrl.supports(ddCode)) // 无详情页策略(三维体等)→ 只适配、不开面板(静默) + detailCtrl.openDataset(dsId, ddCode, name, QString()); + }); // 2D 段「z 值」滑块 → 整体升降该 2D 类型平面(Plane2DRenderStrategy 重摆其全部足迹)。 QObject::connect(analysisTab, &geopro::app::CategoryAnalysisTab::planeZChanged, sceneCtrl, &geopro::controller::VtkSceneController::setPlaneZ); diff --git a/src/app/panels/columns/CategoryAnalysisTab.cpp b/src/app/panels/columns/CategoryAnalysisTab.cpp index 62fcbe0..f2b0122 100644 --- a/src/app/panels/columns/CategoryAnalysisTab.cpp +++ b/src/app/panels/columns/CategoryAnalysisTab.cpp @@ -53,6 +53,7 @@ CategoryAnalysisTab::CategoryAnalysisTab(geopro::data::DatasetFieldDictionary* d // 注:「导入雷达测线」入口已迁至 TopBar「设备」菜单(Task D1);CategorySection 段头按钮与 // CategoryAnalysisTab::radarImportRequested 信号均已移除。 connect(sec, &CategorySection::detailRequested, this, &CategoryAnalysisTab::detailRequested); + connect(sec, &CategorySection::datasetActivated, this, &CategoryAnalysisTab::datasetActivated); connect(sec, &CategorySection::deleteDatasetRequested, this, &CategoryAnalysisTab::deleteDatasetRequested); connect(sec, &CategorySection::sliceRequested, this, &CategoryAnalysisTab::sliceRequested); diff --git a/src/app/panels/columns/CategoryAnalysisTab.hpp b/src/app/panels/columns/CategoryAnalysisTab.hpp index fe67064..7753da9 100644 --- a/src/app/panels/columns/CategoryAnalysisTab.hpp +++ b/src/app/panels/columns/CategoryAnalysisTab.hpp @@ -42,7 +42,8 @@ public: signals: void checkedDatasetsChanged(const QStringList& dsIds); // 5 段勾选并集 void generateVolumeRequested(const QString& dsTypeCode, const QStringList& sourceDsIds); - void detailRequested(const QString& dsId, const QString& ddCode, const QString& name); + void detailRequested(const QString& dsId, const QString& ddCode, const QString& name); // 右键「详情」=属性弹窗 + void datasetActivated(const QString& dsId, const QString& ddCode, const QString& name); // 双击=适配+图表联动(T4) void deleteDatasetRequested(const QString& dsId, const QString& ddCode); // 右键删除切片/异常 // ── 三维体段操作转发(迁自旧 Column3DAnalysis,全接)── void sliceRequested(geopro::render::interact::SliceAxis axis, const QString& volumeDsId); diff --git a/src/app/panels/columns/CategorySection.cpp b/src/app/panels/columns/CategorySection.cpp index f0fbb74..acf6b07 100644 --- a/src/app/panels/columns/CategorySection.cpp +++ b/src/app/panels/columns/CategorySection.cpp @@ -154,7 +154,9 @@ CategorySection::CategorySection(const geopro::data::CategoryDescriptor& desc, connect(list_, &QTreeWidget::itemDoubleClicked, this, [this](QTreeWidgetItem* it, int) { const QString id = it->data(0, kDsIdRole).toString(); if (id.isEmpty()) return; - emit detailRequested(id, it->data(0, kDsDdCodeRole).toString(), it->data(0, kDsNameRole).toString()); + // 双击=适配相机到该 ds 子树盒 + 联动中下方图表详情页(无图表页类型静默,见 main.cpp T4 接线)。 + // 属性弹窗改由右键「详情」触发(detailRequested),双击不再弹属性框(决策6:三维体只适配、静默)。 + emit datasetActivated(id, it->data(0, kDsDdCodeRole).toString(), it->data(0, kDsNameRole).toString()); }); if (desc_.id == "voxel") { // 仅三维体段提供右键操作菜单(体/切片/异常) list_->setContextMenuPolicy(Qt::CustomContextMenu); diff --git a/src/app/panels/columns/CategorySection.hpp b/src/app/panels/columns/CategorySection.hpp index 38ea630..f3ecf3d 100644 --- a/src/app/panels/columns/CategorySection.hpp +++ b/src/app/panels/columns/CategorySection.hpp @@ -60,7 +60,10 @@ signals: void planeZChanged(const QString& typeId, double z); // PlaneZ 滑块:整体升降该 2D 类型平面(z=绝对高程,米) void basemapKindChanged(const QString& typeId, int kind); // 底图弹窗:矢量平面(0)/无(1) void basemapOpacityChanged(const QString& typeId, double o); // 底图弹窗透明度滑块[0,1](防抖发射) - void detailRequested(const QString& dsId, const QString& ddCode, const QString& name); // 双击/右键=详情 + void detailRequested(const QString& dsId, const QString& ddCode, const QString& name); // 右键「详情」=属性弹窗 + // 双击某行(决策6/T4):适配相机到该 ds 子树空间范围 + 联动中下方详情面板(无详情页类型静默)。 + // 与 detailRequested(右键属性弹窗)分开,使双击「只适配 + 图表联动」,三维体等无图表页时静默。 + void datasetActivated(const QString& dsId, const QString& ddCode, const QString& name); void deleteDatasetRequested(const QString& dsId, const QString& ddCode); // 右键删除(切片/异常) // ── 三维体段右键操作(迁自旧 Column3DAnalysis,全接)── void sliceRequested(geopro::render::interact::SliceAxis axis, const QString& volumeDsId); // 体→生成切片(轴+目标体) diff --git a/src/controller/DatasetDetailController.hpp b/src/controller/DatasetDetailController.hpp index ea7b06b..fc62c8c 100644 --- a/src/controller/DatasetDetailController.hpp +++ b/src/controller/DatasetDetailController.hpp @@ -31,6 +31,10 @@ public slots: void loadTabPaged(const QString& dsId, const QString& ddCode, int tabIndex, int pageNo, int pageSize); void focusDataset(const QString& dsId); +public: + // 该 ddCode 是否有已注册的详情页策略(=有中下方图表详情页)。供联动入口 gate: + // 无策略的类型(如 dd_voxel/dd_radar_3d 三维体)不走 openDataset,避免 loadFailed 的状态栏提示,静默。 + bool supports(const QString& ddCode) const { return registry_.supports(ddCode.toStdString()); } signals: void datasetOpened(const QString& dsId, const QString& ddCode, const QString& dsName, const QString& tmObjectId, const std::vector& tabs); diff --git a/tests/app/test_chart_strategy_registry.cpp b/tests/app/test_chart_strategy_registry.cpp index 9a508b6..71d8f0c 100644 --- a/tests/app/test_chart_strategy_registry.cpp +++ b/tests/app/test_chart_strategy_registry.cpp @@ -1,6 +1,7 @@ #include #include "DatasetDetailTab.hpp" #include "IDatasetChartStrategy.hpp" // geopro::controller(控制器层) +#include "panels/chart/ErtInversionStrategy.hpp" #include "panels/chart/MeasurementStrategy.hpp" #include "panels/chart/GrMeasurementStrategy.hpp" #include "panels/chart/TrajectoryStrategy.hpp" @@ -39,6 +40,29 @@ TEST(ChartStrategyRegistry, ExposesTabSpecsFromStrategy) { EXPECT_EQ(tabs[1].kind, ViewKind::FilledContour); } +// T4 双击详情联动 gate 契约(决策6):DatasetDetailController::supports() 直接委托本注册表的 supports()。 +// 三维体(dd_voxel/dd_radar_3d)等无详情页策略 → supports()=false → 双击只适配、静默不开面板; +// 5 种已注册类型 supports()=true → 双击联动打开中下方详情页。此测锁定这条 gate 的真实注册集行为。 +TEST(ChartStrategyRegistry, T4GateSilentForVolumeTypesSupportsRegistered) { + ChartStrategyRegistry reg; + reg.add(std::make_unique()); + reg.add(std::make_unique()); + reg.add(std::make_unique()); + reg.add(std::make_unique()); + reg.add(std::make_unique()); + // 有详情页 → 双击联动打开中下方详情面板。 + EXPECT_TRUE(reg.supports("dd_inversion_data")); + EXPECT_TRUE(reg.supports("dd_ert_measurement_data")); + EXPECT_TRUE(reg.supports("dd_ert_measurement_gr_data")); + EXPECT_TRUE(reg.supports("dd_trajectory_data")); + EXPECT_TRUE(reg.supports("dd_grid")); + // 无详情页(三维体/切片/异常)→ 静默:双击只适配、不开面板、不弹状态栏。 + EXPECT_FALSE(reg.supports("dd_voxel")); + EXPECT_FALSE(reg.supports("dd_radar_3d")); + EXPECT_FALSE(reg.supports("dd_slice")); + EXPECT_FALSE(reg.supports("dd_anomaly")); +} + TEST(MeasurementStrategy, DrivesTwoTabsScatterAndTable) { geopro::app::MeasurementStrategy s; EXPECT_EQ(s.ddCode(), "dd_ert_measurement_data");