From d7ab7705c96bc87a25d3f7bca62a375142d13dd6 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Fri, 26 Jun 2026 15:17:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(3d):=20=E5=88=87=E7=89=87=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E5=90=8E=E5=AE=9A=E7=A8=BF=E9=94=81=E5=AE=9A(?= =?UTF-8?q?=E4=B8=8D=E5=8F=AF=E7=A7=BB=E5=8A=A8/=E6=97=8B=E8=BD=AC)+=20?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E5=8E=BB=E4=BF=9D=E5=AD=98=C2=B7=E5=8F=A6?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按用户口径:切片保存后即定稿,不可再改。 - SliceTool::setInteractive(false) 关 widget 鼠标交互(锁移动/旋转,纹理仍显示;拾取选中/右键由 PickInteractorStyle 独立处理不受影响)。tagSelectedSlice(临时→保存)与 showSavedSlice(重显已保存)均锁定。 - VTK 右键「保存」仅对未保存(临时)切片显示;已保存切片无保存项。 - 数据列表切片右键去掉「保存/另存」(列表中切片均为已保存=锁定),留 详情/导出/色阶/删除。 测试:439/439 通过 --- src/app/main.cpp | 7 +++++-- src/app/panels/columns/CategorySection.cpp | 4 +--- src/render/interact/InteractionManager.cpp | 2 ++ src/render/interact/SliceTool.cpp | 4 ++++ src/render/interact/SliceTool.hpp | 3 +++ 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/app/main.cpp b/src/app/main.cpp index 9f6e6e5..6bfc25b 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -510,7 +510,10 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re QAction* aAnoPoint = anomMenu->addAction(QStringLiteral("点")); QAction* aAnoLine = anomMenu->addAction(QStringLiteral("线")); QAction* aAnoFace = anomMenu->addAction(QStringLiteral("面")); - QAction* aSave = menu.addAction(QStringLiteral("保存")); + // 「保存」仅对未保存(临时)切片显示——已保存切片定稿锁定、不可再改/再存(用户要求)。 + QAction* aSave = interactionMgr->selectedSliceDsId().empty() + ? menu.addAction(QStringLiteral("保存")) + : nullptr; QMenu* expMenu = menu.addMenu(QStringLiteral("导出")); QAction* aImg = expMenu->addAction(QStringLiteral("图片")); QAction* aDat = expMenu->addAction(QStringLiteral("dat")); @@ -630,7 +633,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re []() { /* onCancel:放弃,无需处理 */ }); return; } - if (chosen == aSave) { + if (aSave != nullptr && chosen == aSave) { int axis = 3; geopro::render::interact::Vec3 o{}, p1{}, p2{}; if (!interactionMgr->selectedSlicePlane(axis, o, p1, p2)) return; diff --git a/src/app/panels/columns/CategorySection.cpp b/src/app/panels/columns/CategorySection.cpp index e6e8a58..a5f8ace 100644 --- a/src/app/panels/columns/CategorySection.cpp +++ b/src/app/panels/columns/CategorySection.cpp @@ -344,9 +344,7 @@ void CategorySection::showContextMenu(const QPoint& pos) { sl->addAction(QStringLiteral("左右"), this, [this, id] { emit sliceRequested(SliceAxis::LeftRight, id); }); sl->addAction(QStringLiteral("任意"), this, [this, id] { emit sliceRequested(SliceAxis::Oblique, id); }); menu.addAction(QStringLiteral("色阶"), this, [this, id] { emit colorScaleRequested(id); }); - } else if (ddCode == QStringLiteral("dd_slice")) { // 切片 - menu.addAction(QStringLiteral("保存"), this, [this, id] { emit sliceSaveRequested(id); }); - menu.addAction(QStringLiteral("另存为"), this, [this, id] { emit sliceSaveAsRequested(id); }); + } else if (ddCode == QStringLiteral("dd_slice")) { // 切片(列表中均为已保存=定稿锁定,无保存/另存) QMenu* ex = menu.addMenu(QStringLiteral("导出")); ex->addAction(QStringLiteral("图片"), this, [this, id] { emit sliceExportImageRequested(id); }); ex->addAction(QStringLiteral("dat"), this, [this, id] { emit sliceExportDatRequested(id); }); diff --git a/src/render/interact/InteractionManager.cpp b/src/render/interact/InteractionManager.cpp index 5291f8c..1b6f63a 100644 --- a/src/render/interact/InteractionManager.cpp +++ b/src/render/interact/InteractionManager.cpp @@ -168,6 +168,7 @@ void InteractionManager::showSavedSlice(const std::string& dsId, int axis, const tool->setVolumeDsId(volumeDsId); SliceTool* tp = tool.get(); tool->onInteract = [this, tp]() { selectByTool(tp); }; + tool->setInteractive(false); // 已保存切片定稿锁定:不可移动/旋转(用户要求);仍可拾取选中/右键 slices_.push_back(std::move(tool)); selected_ = static_cast(slices_.size()) - 1; updateSelectionVisual(); // 程序化显示(syncSlices):不发 onSliceSelectionChanged,避免列表选中被刷 @@ -289,6 +290,7 @@ std::string InteractionManager::selectedSliceVolumeDsId() const { void InteractionManager::tagSelectedSlice(const std::string& dsId) { if (selected_ < 0 || selected_ >= static_cast(slices_.size())) return; slices_[static_cast(selected_)]->setDsId(dsId); + slices_[static_cast(selected_)]->setInteractive(false); // 保存即定稿锁定(不可改) } vtkImageData* InteractionManager::selectedSliceImage() const { diff --git a/src/render/interact/SliceTool.cpp b/src/render/interact/SliceTool.cpp index 26052f2..45b1b19 100644 --- a/src/render/interact/SliceTool.cpp +++ b/src/render/interact/SliceTool.cpp @@ -171,6 +171,10 @@ vtkImageData* SliceTool::reslicedOutput() const { return widget_ ? widget_->GetResliceOutput() : nullptr; } +void SliceTool::setInteractive(bool on) { + if (widget_) widget_->SetInteraction(on ? 1 : 0); // 关=锁移动/旋转/光标,纹理仍显示 +} + vtkSmartPointer SliceTool::coloredResliceImage() const { if (!widget_) return nullptr; vtkImageMapToColors* cm = widget_->GetColorMap(); // widget 内部把 reslice 经 LUT 上色 → 纹理 diff --git a/src/render/interact/SliceTool.hpp b/src/render/interact/SliceTool.hpp index 277c9b4..dfcbd56 100644 --- a/src/render/interact/SliceTool.hpp +++ b/src/render/interact/SliceTool.hpp @@ -80,6 +80,9 @@ public: // 与屏幕切片纹理同源的着色输出(widget 自己的 ColorMap 输出, RGBA, 逐像素一致, 外区透明)。 // 异常截图/导出用它而非另建 LUT,避免与屏幕配色不一致(用户实测差异大)。 vtkSmartPointer coloredResliceImage() const; + // 开/关 widget 鼠标交互(移动/旋转/光标)。关=锁定但仍显示(已保存切片定稿不可改); + // 拾取选中/右键菜单由 PickInteractorStyle 独立处理,不受此影响。 + void setInteractive(bool on); // 关闭:Off() 并解除 interactor 绑定(幂等)。 void close();