feat/vtk-3d-view #7

Merged
gaozheng merged 301 commits from feat/vtk-3d-view into main 2026-06-27 18:43:52 +08:00
6 changed files with 57 additions and 6 deletions
Showing only changes of commit 888a63081b - Show all commits

View File

@ -477,15 +477,24 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
// splitByCategory 后注入 5 段(电阻率/视电阻率/瞬变/三维体/切片);二维(足迹)经 dim2D 仍走 col2D。
auto lastSourceRows = std::make_shared<std::vector<geopro::data::DsRow>>();
auto lastStructNodes = std::make_shared<std::vector<geopro::data::StructNode>>(); // 生成位置候选(项目内 GS/TM)
auto refreshAnalysis = [drawer, scene3dRepo, lastSourceRows]() {
auto refreshAnalysis = [drawer, scene3dRepo, lastSourceRows, lastStructNodes]() {
const auto vols = scene3dRepo->volumeRows();
const auto slices = scene3dRepo->sliceRows();
const auto anomalies = scene3dRepo->anomalyRows();
// 电阻率/视/瞬变段=对象树反演 dssplitByCategory 按 dsTypeCode 分voxel 段下方单独喂三级树)。
// 电阻率/视/瞬变段=对象树反演 dssplitByCategory 按 dsTypeCode 分voxel 段下方单独喂完整树)。
drawer->analysisTab()->setBuckets(geopro::app::splitByCategory(*lastSourceRows));
// 三维体段=「体→切片/异常」三级树:体(根)+切片(parentId=体)+异常(parentId=体/切片)
// 切片不单列段只在此树spec §8 修订。populateDatasetList 按 parentId 自动建树。
std::vector<geopro::data::DsRow> voxelTree = vols;
// 三维体段=从项目根的完整层级树:项目根/GS/TM 容器节点 → 体(挂生成位置) → 切片(挂体) → 异常(挂体/切片)。
// populateDatasetList 按 parentId 自动建树spec §7/§8
std::vector<geopro::data::DsRow> voxelTree;
for (const auto& n : *lastStructNodes) { // 项目根/GS/TM 容器节点ddCode="container",不可勾选)
geopro::data::DsRow c;
c.id = n.id;
c.dsName = n.name;
c.ddCode = "container";
c.parentId = n.parentId;
voxelTree.push_back(std::move(c));
}
for (const auto& v : vols) voxelTree.push_back(v);
for (const auto& s : slices) voxelTree.push_back(s);
for (const auto& a : anomalies) voxelTree.push_back(a);
if (auto* sec = drawer->analysisTab()->section("voxel")) sec->setDatasets(voxelTree);
@ -795,6 +804,17 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
}
}
});
// 三维体段右键删除切片→deleteSlice / 异常→deleteAnomaly删后刷新树。
QObject::connect(analysisTab, &geopro::app::CategoryAnalysisTab::deleteDatasetRequested, &window,
[scene3dRepo, refreshAnalysis](const QString& dsId, const QString& ddCode) {
const std::string id = dsId.toStdString();
auto ok = [refreshAnalysis]() { refreshAnalysis(); };
auto err = [](const std::string&) {};
if (ddCode == QStringLiteral("dd_slice"))
scene3dRepo->deleteSlice(id, ok, err);
else if (ddCode == QStringLiteral("dd_anomaly"))
scene3dRepo->deleteAnomaly(id, ok, err);
});
// O点位置/字体本期 stubTODO P4弹框
QObject::connect(c3, &geopro::app::Column3DDataset::oPointClicked, vtkWidget,
[]() { /* TODO P4: O点位置弹框 */ });

View File

@ -37,6 +37,8 @@ CategoryAnalysisTab::CategoryAnalysisTab(geopro::data::DatasetFieldDictionary* d
connect(sec, &CategorySection::generateVolumeRequested, this,
&CategoryAnalysisTab::generateVolumeRequested);
connect(sec, &CategorySection::detailRequested, this, &CategoryAnalysisTab::detailRequested);
connect(sec, &CategorySection::deleteDatasetRequested, this,
&CategoryAnalysisTab::deleteDatasetRequested);
col->addWidget(sec);
}
col->addStretch(1);

View File

@ -30,6 +30,7 @@ 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 deleteDatasetRequested(const QString& dsId, const QString& ddCode); // 右键删除切片/异常
private:
void recomputeCheckedUnion();

View File

@ -5,6 +5,8 @@
#include "panels/columns/DateRangeEdit.hpp"
#include <QHBoxLayout>
#include <QLabel>
#include <QMenu>
#include <QPoint>
#include <QPushButton>
#include <QSet>
#include <QSignalBlocker>
@ -85,6 +87,8 @@ CategorySection::CategorySection(const CategorySpec& spec, geopro::data::Dataset
if (id.isEmpty()) return;
emit detailRequested(id, it->data(0, kDsDdCodeRole).toString(), it->data(0, kDsNameRole).toString());
});
list_->setContextMenuPolicy(Qt::CustomContextMenu);
connect(list_, &QTreeWidget::customContextMenuRequested, this, &CategorySection::showContextMenu);
body->addWidget(list_, 1);
root->addWidget(body_, 1);
@ -166,10 +170,13 @@ void CategorySection::rebuildList() {
const QSignalBlocker block(list_);
populateDatasetList(list_, filtered, /*append=*/false);
for (QTreeWidgetItemIterator it(list_); *it; ++it) {
// 容器节点(项目根/GS/TM只作层级骨架不可勾选。
if ((*it)->data(0, kDsDdCodeRole).toString() == QStringLiteral("container")) continue;
(*it)->setFlags((*it)->flags() | Qt::ItemIsUserCheckable);
(*it)->setCheckState(0, Qt::Unchecked);
}
}
list_->expandAll(); // 展开容器层级(项目根/GS/TM让体/切片/异常可见
emitChecked(); // 重建后必为空选,清掉上次渲染勾选
}
@ -182,4 +189,22 @@ QStringList CategorySection::checkedDsIds() const {
void CategorySection::emitChecked() { emit checkedDatasetsChanged(checkedDsIds()); }
void CategorySection::showContextMenu(const QPoint& pos) {
QTreeWidgetItem* it = list_->itemAt(pos);
if (!it) return;
const QString id = it->data(0, kDsIdRole).toString();
const QString ddCode = it->data(0, kDsDdCodeRole).toString();
if (id.isEmpty() || ddCode == QStringLiteral("container")) return; // 容器节点无操作菜单
const QString name = it->data(0, kDsNameRole).toString();
QMenu menu(this);
menu.addAction(QStringLiteral("详情"), this,
[this, id, ddCode, name] { emit detailRequested(id, ddCode, name); });
// 切片 / 异常可删除(三维体段无 deleteVolume 端点)。
if (ddCode == QStringLiteral("dd_slice") || ddCode == QStringLiteral("dd_anomaly")) {
menu.addAction(QStringLiteral("删除"), this,
[this, id, ddCode] { emit deleteDatasetRequested(id, ddCode); });
}
menu.exec(list_->viewport()->mapToGlobal(pos));
}
} // namespace geopro::app

View File

@ -36,9 +36,11 @@ public:
signals:
void checkedDatasetsChanged(const QStringList& dsIds); // 数据行勾选=渲染
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 deleteDatasetRequested(const QString& dsId, const QString& ddCode); // 右键删除(切片/异常)
private:
void showContextMenu(const QPoint& pos); // 段体树右键菜单(详情 + 删除)
void rebuildList(); // 据 rows_经装置/日期筛选)重建段体树并复原勾选
void refreshArrayCombo(); // 据当前 rows_ 重填装置类型下拉项(经字典 value→中文
void emitChecked(); // 收集勾选 → checkedDatasetsChanged

View File

@ -129,6 +129,7 @@ std::vector<DsRow> Api3dRepository::volumeRows() const {
r.dsName = sv.name;
r.ddCode = "dd_voxel";
r.typeName = "三维体";
r.parentId = sv.request ? sv.request->structParentId : std::string(); // 挂生成位置(项目根/GS/TM)
rows.push_back(std::move(r));
}
return rows;