From a41b428b09dd81e13cde3508d8416599b3201a32 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Wed, 24 Jun 2026 21:37:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20VolumeParamsDialog=20=E6=89=A9=E5=B1=95?= =?UTF-8?q?-=E5=B7=A6=E4=BE=A7=E6=BA=90=E5=88=97=E8=A1=A8(=E5=8F=AF?= =?UTF-8?q?=E5=A2=9E=E5=88=A0)+=E7=94=9F=E6=88=90=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E4=B8=8B=E6=8B=89(GS/TM)+req=E5=BD=92=E5=B1=9E(Task12=20#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/VolumeParamsDialog.cpp | 67 ++++++++++++++++++++++++++++++---- src/app/VolumeParamsDialog.hpp | 26 +++++++++++-- src/app/main.cpp | 44 ++++++++++++---------- 3 files changed, 106 insertions(+), 31 deletions(-) diff --git a/src/app/VolumeParamsDialog.cpp b/src/app/VolumeParamsDialog.cpp index bba5461..b0e5454 100644 --- a/src/app/VolumeParamsDialog.cpp +++ b/src/app/VolumeParamsDialog.cpp @@ -3,11 +3,12 @@ #include #include "EmptyAwareComboBox.hpp" -#include #include #include +#include #include #include +#include #include #include @@ -22,37 +23,68 @@ constexpr double kDefCellXY = 1.0; constexpr double kDefCellZ = 0.5; constexpr double kDefPower = 2.0; constexpr double kDefMaxDist = 4.0; +constexpr int kRoleDsId = Qt::UserRole + 1; // 源列表项存 dsId +constexpr int kRoleMountId = Qt::UserRole + 1; // 生成位置项存 id(itemData 默认存 confType) } // namespace -VolumeParamsDialog::VolumeParamsDialog(int sourceCount, QWidget* parent) : QDialog(parent) { +VolumeParamsDialog::VolumeParamsDialog(const QVector>& sources, + const QVector& mounts, + int defaultMountIndex, QWidget* parent) + : QDialog(parent) { setWindowTitle(QStringLiteral("生成三维体")); setModal(true); auto* root = formkit::dialogRoot(this); - auto* intro = new QLabel(QStringLiteral("由 %1 个源数据集插值生成三维体").arg(sourceCount)); + auto* intro = new QLabel(QStringLiteral("勾选参与生成的源数据集,设置参数与生成位置")); geopro::app::applyTokenizedStyleSheet(intro, QStringLiteral("color:{{text/secondary}};")); root->addWidget(intro); - // 统一外壳:表单卡片 + 分组标题(与「数据详情 / 属性面板」同款),编辑态/只读态一致。 + // 左右两栏:左·源数据集列表(可勾选/取消,二次确认或修改) | 右·参数卡片。 + auto* cols = new QHBoxLayout(); + + // ── 左:源数据集列表 ── + auto* leftCard = formkit::formCard(this); + auto* leftLay = formkit::cardBody(leftCard); + formkit::addSection(leftLay, QStringLiteral("源数据集"), leftCard, false); + sourceList_ = new QListWidget(); + for (const auto& s : sources) { + auto* it = new QListWidgetItem(s.second.isEmpty() ? s.first : s.second, sourceList_); + it->setData(kRoleDsId, s.first); + it->setFlags(it->flags() | Qt::ItemIsUserCheckable); + it->setCheckState(Qt::Checked); // 默认全勾(=传入的当前勾选源) + } + leftLay->addWidget(sourceList_); + cols->addWidget(leftCard, 1); + + // ── 右:参数 ── auto* card = formkit::formCard(this); auto* cardLay = formkit::cardBody(card); formkit::addSection(cardLay, QStringLiteral("参数"), card, false); - auto* form = formkit::makeEditForm(); name_ = new QLineEdit(QStringLiteral("三维体")); formkit::capField(name_); form->addRow(formkit::editLabel(QStringLiteral("名称")), name_); + // 生成位置下拉:项目内 GS/项目根/TM(itemData 存 confType,UserRole+1 存 id)。 + mount_ = new EmptyAwareComboBox(); + for (const auto& m : mounts) { + mount_->addItem(m.name, m.confType); + mount_->setItemData(mount_->count() - 1, m.id, kRoleMountId); + } + if (defaultMountIndex >= 0 && defaultMountIndex < mounts.size()) + mount_->setCurrentIndex(defaultMountIndex); + formkit::capField(mount_); + form->addRow(formkit::editLabel(QStringLiteral("生成位置")), mount_); + model_ = new EmptyAwareComboBox(); model_->addItem(QStringLiteral("反距离加权 (IDW)"), static_cast(geopro::data::VolumeBuildParams::Model::Idw)); model_->addItem(QStringLiteral("克里金 (Kriging)"), static_cast(geopro::data::VolumeBuildParams::Model::Kriging)); - // 克里金本期未实现(core 仅 IDW)→ 禁用该项,默认选 IDW。 if (auto* m = qobject_cast(model_->model())) { - if (auto* it = m->item(1)) it->setEnabled(false); + if (auto* it = m->item(1)) it->setEnabled(false); // 克里金本期未实现 → 禁用 } model_->setCurrentIndex(0); formkit::capField(model_); @@ -77,7 +109,8 @@ VolumeParamsDialog::VolumeParamsDialog(int sourceCount, QWidget* parent) : QDial form->addRow(formkit::editLabel(QStringLiteral("最大影响距离 (米)")), maxDist_); cardLay->addLayout(form); - root->addWidget(card); + cols->addWidget(card, 1); + root->addLayout(cols); formkit::addDialogButtons(root, this, QStringLiteral("生成"), QStringLiteral("取消")); } @@ -97,4 +130,22 @@ geopro::data::VolumeBuildParams VolumeParamsDialog::params() const { return p; } +QStringList VolumeParamsDialog::sourceDatasetIds() const { + QStringList ids; + for (int i = 0; i < sourceList_->count(); ++i) { + QListWidgetItem* it = sourceList_->item(i); + if (it->checkState() == Qt::Checked) ids << it->data(kRoleDsId).toString(); + } + return ids; +} + +QString VolumeParamsDialog::mountTargetId() const { + return mount_->currentData(kRoleMountId).toString(); +} + +int VolumeParamsDialog::mountConfType() const { + const int ct = mount_->currentData().toInt(); + return ct == 0 ? 1 : ct; // 缺省按 GS/项目根 +} + } // namespace geopro::app diff --git a/src/app/VolumeParamsDialog.hpp b/src/app/VolumeParamsDialog.hpp index ba50cf9..ce41469 100644 --- a/src/app/VolumeParamsDialog.hpp +++ b/src/app/VolumeParamsDialog.hpp @@ -1,33 +1,51 @@ #pragma once #include +#include #include +#include +#include #include "repo/VolumeBuildParams.hpp" class QLineEdit; class QComboBox; class QDoubleSpinBox; +class QListWidget; namespace geopro::app { -// 「生成三维体」参数对话框:名称 + 插值模型(IDW;克里金占位禁用)+ cellXY/cellZ/power/maxDist。 -// sourceCount 仅用于提示文案("由 N 个源数据集插值生成")。 -// accept 后经 volumeName()/params() 取结果;params() 不含 sourceDatasetIds(由调用方填充)。 +// 生成位置候选(spec §7/§8):项目内 GS/项目根/TM。confType:1=GS/项目根 2=TM。 +struct VolumeMountTarget { + QString id; + QString name; + int confType = 1; +}; + +// 「生成三维体」对话框(spec §7/§8):左侧·源数据集列表(可勾选/取消,二次确认或修改); +// 右侧·参数(名称 + 生成位置下拉 + 插值模型 + cellXY/cellZ/power/maxDist)。 class VolumeParamsDialog : public QDialog { Q_OBJECT public: - explicit VolumeParamsDialog(int sourceCount, QWidget* parent = nullptr); + // sources:参与生成的源数据集 (dsId, 显示名);mounts:生成位置候选;defaultMountIndex:默认选中位置下标。 + VolumeParamsDialog(const QVector>& sources, + const QVector& mounts, int defaultMountIndex, + QWidget* parent = nullptr); QString volumeName() const; geopro::data::VolumeBuildParams params() const; + QStringList sourceDatasetIds() const; // 左侧最终勾选的源 ds id + QString mountTargetId() const; // 生成位置 id(structParentId) + int mountConfType() const; // 生成位置 confType(1=GS/项目根 2=TM) private: QLineEdit* name_ = nullptr; + QComboBox* mount_ = nullptr; QComboBox* model_ = nullptr; QDoubleSpinBox* cellXY_ = nullptr; QDoubleSpinBox* cellZ_ = nullptr; QDoubleSpinBox* power_ = nullptr; QDoubleSpinBox* maxDist_ = nullptr; + QListWidget* sourceList_ = nullptr; }; } // namespace geopro::app diff --git a/src/app/main.cpp b/src/app/main.cpp index ac298e8..2c46c78 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -454,6 +454,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re // 三维分析数据源 = 最近对象树勾选拉取的 ds + 客户端三维体(mock) + 已保存切片; // splitByCategory 后注入 5 段(电阻率/视电阻率/瞬变/三维体/切片);二维(足迹)经 dim2D 仍走 col2D。 auto lastSourceRows = std::make_shared>(); + auto lastStructNodes = std::make_shared>(); // 生成位置候选(项目内 GS/TM) auto refreshAnalysis = [drawer, scene3dRepo, lastSourceRows]() { const auto vols = scene3dRepo->volumeRows(); const auto slices = scene3dRepo->sliceRows(); @@ -715,16 +716,33 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re }); // 段头「+新增三维体」:弹参数对话框 → 组装真实 VoxelGenerateRequest → createVolume(mock) → 刷新。 QObject::connect(analysisTab, &geopro::app::CategoryAnalysisTab::generateVolumeRequested, &window, - [&window, &nav, scene3dRepo, refreshAnalysis](const QString& /*dsTypeCode*/, - const QStringList& sourceIds) { + [&window, &nav, scene3dRepo, refreshAnalysis, lastSourceRows, + lastStructNodes](const QString& /*dsTypeCode*/, const QStringList& sourceIds) { if (sourceIds.isEmpty()) return; - geopro::app::VolumeParamsDialog dlg(static_cast(sourceIds.size()), &window); + // 源 ds (id,名称):名称从最近拉取的行查(缺则用 id)。 + QVector> sources; + for (const QString& id : sourceIds) { + QString name = id; + for (const auto& r : *lastSourceRows) + if (r.id == id.toStdString()) { name = QString::fromStdString(r.dsName); break; } + sources.push_back({id, name}); + } + // 生成位置候选:项目内 GS/项目根/TM(StructNode.type==2 → TM(2),其余 → GS/项目根(1))。 + QVector mounts; + for (const auto& n : *lastStructNodes) + mounts.push_back({QString::fromStdString(n.id), QString::fromStdString(n.name), + n.type == 2 ? 2 : 1}); + geopro::app::VolumeParamsDialog dlg(sources, mounts, /*defaultMountIndex=*/0, &window); if (dlg.exec() != QDialog::Accepted) return; + const QStringList chosen = dlg.sourceDatasetIds(); + if (chosen.isEmpty()) return; // 全取消则不生成 const geopro::data::VolumeBuildParams p = dlg.params(); geopro::data::VoxelGenerateRequest req; req.projectId = nav.currentProjectId().toStdString(); + req.structParentId = dlg.mountTargetId().toStdString(); // 生成位置(归属) + req.structParentConfType = dlg.mountConfType(); req.name = dlg.volumeName().toStdString(); - for (const QString& id : sourceIds) req.sourceDatasetIds.push_back(id.toStdString()); + for (const QString& id : chosen) req.sourceDatasetIds.push_back(id.toStdString()); req.interpModel = (p.interpModel == geopro::data::VolumeBuildParams::Model::Kriging) ? "Kriging" : "Idw"; req.cellXY = p.cellXY; @@ -763,20 +781,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re []() { /* TODO P4: O点位置弹框 */ }); QObject::connect(c3, &geopro::app::Column3DDataset::fontClicked, vtkWidget, []() { /* TODO P4: 字体弹框 */ }); - // 三维数据集栏右键「生成三维体」:弹参数对话框 → 客户端 createVolume(mock)→ 刷新三维分析栏 - // (新三维体作为"分析产物"出现在三维分析栏,勾选即渲染体)。 - QObject::connect(c3, &geopro::app::Column3DDataset::generateVolumeRequested, &window, - [&window, scene3dRepo, refreshAnalysis](const QStringList& sourceIds) { - geopro::app::VolumeParamsDialog dlg(static_cast(sourceIds.size()), - &window); - if (dlg.exec() != QDialog::Accepted) return; - geopro::data::VolumeBuildParams params = dlg.params(); - for (const QString& id : sourceIds) - params.sourceDatasetIds.push_back(id.toStdString()); - scene3dRepo->createVolume(std::move(params), - dlg.volumeName().toStdString()); - refreshAnalysis(); // 新体行进入三维分析栏,勾选即渲染体 - }); + // (旧三维数据集栏「生成三维体」接线已退役——改由 analysisTab::generateVolumeRequested 走新对话框。) auto* ca = drawer->colAnalysis(); // 三维分析栏勾选(三维体/切片):体走控制器体素路径;切片(dd_slice)不进控制器(否则 loadSection @@ -1775,9 +1780,10 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re }); QObject::connect(&nav, &geopro::controller::WorkbenchNavController::structureLoaded, objectTree, [objectTree, datasetList, fileList, datasetTitle, datasetTabs, exceptionPanel, - objAttrView, propView, anomalyBadge]( + objAttrView, propView, anomalyBadge, lastStructNodes]( const QString& projectName, const std::vector& nodes) { + *lastStructNodes = nodes; // 供「生成三维体」对话框的生成位置下拉 objectTree->setStructure(projectName, nodes); datasetList->clear(); fileList->clear();