From f230ca8dd1429b21f80b12ac82a68f18db0aafea Mon Sep 17 00:00:00 2001 From: gaozheng Date: Fri, 26 Jun 2026 16:06:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(3d):=20=E5=BC=82=E5=B8=B8=E7=BB=98=E5=88=B6?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E4=B8=AD=E6=96=87=E4=B9=B1=E7=A0=81(?= =?UTF-8?q?=E6=94=B9=20QLabel=20=E6=B5=AE=E5=B1=82)=20+=20=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E5=88=87=E5=88=B0=E5=88=AB=E5=AF=B9=E8=B1=A1=E6=B8=85?= =?UTF-8?q?=E5=88=87=E7=89=87=E9=80=89=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) 提示"乱码":vtkTextActor 用 VTK 内置字体不含中文字形 → 中文渲染不出(只剩 ASCII)。 移除 VTK 文本提示,改 app 层右上角 QLabel 浮层:Qt 渲染中文 + QSS(深底/accent描边/圆角), 绘制开始按形态显示结束方式、结束/取消隐藏;不挡画布鼠标。 2) 列表选中切片后切到别的对象(三维体/异常),VTK 切片仍高亮:datasetSelected 选非切片对象时 未清切片选中。加 InteractionManager::deselectSlice();选异常/其它对象均清切片高亮(异常↔切片互斥)。 测试:439/439 通过 --- src/app/main.cpp | 40 ++++++++++++++++++---- src/render/interact/AnomalyDrawTool.cpp | 33 ++---------------- src/render/interact/AnomalyDrawTool.hpp | 2 -- src/render/interact/InteractionManager.cpp | 7 ++++ src/render/interact/InteractionManager.hpp | 2 ++ 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/app/main.cpp b/src/app/main.cpp index 47a8940..9c8e59b 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -398,6 +398,17 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re viewToolbar->move(12, 12); viewToolbar->raise(); viewToolbar->show(); + // 异常绘制操作提示:右上角 QLabel 浮层(VTK 内置字体不含中文字形,故用 Qt 渲染中文 + QSS 美化)。 + // 深底 + accent 描边;不挡画布鼠标事件;绘制开始显示、结束/取消隐藏(见 onSliceContextMenuRequested)。 + auto* anomalyHint = new QLabel(vtkWidget); + anomalyHint->setObjectName(QStringLiteral("anomalyHint")); + anomalyHint->setAttribute(Qt::WA_TransparentForMouseEvents); + geopro::app::applyTokenizedStyleSheet( + anomalyHint, + QStringLiteral("QLabel#anomalyHint{background:rgba(10,18,30,0.85);color:#E6ECF5;" + "border:1px solid {{accent/primary}};border-radius:8px;padding:8px 12px;}")); + anomalyHint->hide(); + // 坐标轴设置抽屉面板:叠加 vtkWidget、工具条右侧滑出,默认隐藏(点设置 toggle)。 auto* axesPanel = new geopro::app::AxesSettingsPanel(vtkWidget); axesPanel->hide(); @@ -506,7 +517,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re // 正视/翻转/关闭=接现有交互(关闭已保存切片→onSliceClosed 取消列表勾选);创建异常=占位(#4)。 interactionMgr->onSliceContextMenuRequested = [&window, &cmdRepo, &nav, interactionMgr, sceneView, scene3dRepo, refreshAnalysis, - refreshAnomalies, drawer, anomalyDrawTool, renderWindowPtr]() { + refreshAnomalies, drawer, anomalyDrawTool, renderWindowPtr, anomalyHint, vtkWidget]() { QMenu menu(&window); QMenu* anomMenu = menu.addMenu(QStringLiteral("创建异常")); // → 点/线/面 子菜单 QAction* aAnoPoint = anomMenu->addAction(QStringLiteral("点")); @@ -543,6 +554,16 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re const ri::Vec3 e1{{p1[0] - o[0], p1[1] - o[1], p1[2] - o[2]}}; const ri::Vec3 e2{{p2[0] - o[0], p2[1] - o[1], p2[2] - o[2]}}; const ri::Vec3 normal = ri::normalize(ri::cross(e1, e2)); + // 操作提示浮层(右上角):按形态显示结束方式;绘制结束/取消隐藏。 + anomalyHint->setText( + shape == 1 ? QStringLiteral("标注点\n左键单击落点即完成\nEsc 取消") + : shape == 2 + ? QStringLiteral("标注线\n左键逐点 · 双击结束\nBackspace 撤点 · Esc 取消") + : QStringLiteral("标注面\n左键逐点 · 点回起点闭合\nBackspace 撤点 · Esc 取消")); + anomalyHint->adjustSize(); + anomalyHint->move(vtkWidget->width() - anomalyHint->width() - 12, 12); // 右上角 + anomalyHint->show(); + anomalyHint->raise(); // 多体并发:异常挂到"选中切片所属体"(非 currentVolume),无选中切片回退当前体。 std::string volId = interactionMgr->selectedSliceVolumeDsId(); if (volId.empty()) volId = sceneView->currentVolumeDsId(); @@ -551,8 +572,9 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re anomalyDrawTool->start( mode, o, normal, [&window, &cmdRepo, &nav, interactionMgr, sceneView, scene3dRepo, renderWindowPtr, - refreshAnomalies, refreshAnalysis, volId, savedSliceId, normal, o, p1, p2, - shape](const std::vector& worldPts) { + refreshAnomalies, refreshAnalysis, volId, savedSliceId, normal, o, p1, p2, shape, + anomalyHint](const std::vector& worldPts) { + anomalyHint->hide(); // 绘制结束 → 隐藏操作提示 // 草稿异常:先临时渲染(让用户在对话框前看到所画,且截图含异常)。 geopro::core::Anomaly a; a.markType = static_cast(shape); @@ -632,7 +654,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re QString::fromStdString(m)); }); }, - []() { /* onCancel:放弃,无需处理 */ }); + [anomalyHint]() { anomalyHint->hide(); }); // 取消(Esc)→隐藏提示 return; } if (aSave != nullptr && chosen == aSave) { @@ -991,12 +1013,16 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re [sceneView, interactionMgr, renderWindowPtr](const QString& dsId, const QString& ddCode) { const std::string id = dsId.toStdString(); + // 选中项决定高亮:异常↔切片互斥,选其它对象两者都清(否则切到别的对象后切片/异常仍高亮)。 if (ddCode == QStringLiteral("dd_anomaly")) { sceneView->setSelectedAnomaly(id); + interactionMgr->deselectSlice(); + } else if (ddCode == QStringLiteral("dd_slice")) { + sceneView->setSelectedAnomaly(std::string{}); + interactionMgr->selectSavedSlice(id); // 选中已渲染的该切片(高亮) } else { - sceneView->setSelectedAnomaly(std::string{}); // 选中非异常对象→清异常高亮 - if (ddCode == QStringLiteral("dd_slice")) - interactionMgr->selectSavedSlice(id); // 选中已渲染的该切片(高亮) + sceneView->setSelectedAnomaly(std::string{}); + interactionMgr->deselectSlice(); } renderWindowPtr->Render(); }); diff --git a/src/render/interact/AnomalyDrawTool.cpp b/src/render/interact/AnomalyDrawTool.cpp index 8b7c7a8..fce7147 100644 --- a/src/render/interact/AnomalyDrawTool.cpp +++ b/src/render/interact/AnomalyDrawTool.cpp @@ -17,8 +17,6 @@ #include #include #include -#include -#include namespace geopro::render::interact { @@ -55,33 +53,8 @@ void AnomalyDrawTool::start(DrawMode mode, const Vec3& planeOrigin, const Vec3& hasCursor_ = false; active_ = true; installObservers(); - - // 屏幕操作提示(右上角,避开左侧工具条):标题 + 当前形态的结束方式 + 取消,分行排版。 - if (renderer_) { - const char* tip = - mode_ == DrawMode::Point - ? "标注点\n左键单击落点即完成\nEsc 取消" - : (mode_ == DrawMode::Line - ? "标注线\n左键逐点 · 双击结束\nBackspace 撤点 · Esc 取消" - : "标注面\n左键逐点 · 点回起点闭合\nBackspace 撤点 · Esc 取消"); - hint_ = vtkSmartPointer::New(); - hint_->SetInput(tip); - auto* tp = hint_->GetTextProperty(); - tp->SetFontSize(15); - tp->SetLineSpacing(1.3); - tp->SetColor(0.90, 0.94, 1.0); // 近白(canvas-text) - tp->SetJustificationToRight(); // 右对齐,贴右上 - tp->SetVerticalJustificationToTop(); - tp->SetBackgroundColor(0.04, 0.07, 0.12); // 深底 - tp->SetBackgroundOpacity(0.66); - tp->SetFrame(1); - tp->SetFrameColor(0.37, 0.55, 0.96); // accent 描边 - tp->SetFrameWidth(1); - hint_->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport(); - hint_->GetPositionCoordinate()->SetValue(0.985, 0.975); // 右上角 - renderer_->AddViewProp(hint_); - if (interactor_) interactor_->Render(); - } + // 操作提示由 app 层 QLabel 浮层承担(VTK 内置字体不含中文字形 → vtkTextActor 渲染不出中文)。 + if (interactor_) interactor_->Render(); } void AnomalyDrawTool::cancel() { @@ -97,11 +70,9 @@ void AnomalyDrawTool::teardownActive() { if (renderer_) { if (preview_) renderer_->RemoveViewProp(preview_); if (rubber_) renderer_->RemoveViewProp(rubber_); - if (hint_) renderer_->RemoveViewProp(hint_); } preview_ = nullptr; rubber_ = nullptr; - hint_ = nullptr; active_ = false; hasCursor_ = false; pts_.clear(); diff --git a/src/render/interact/AnomalyDrawTool.hpp b/src/render/interact/AnomalyDrawTool.hpp index 7be9472..60a95c5 100644 --- a/src/render/interact/AnomalyDrawTool.hpp +++ b/src/render/interact/AnomalyDrawTool.hpp @@ -9,7 +9,6 @@ class vtkRenderWindowInteractor; class vtkRenderer; class vtkActor; -class vtkTextActor; class vtkCallbackCommand; namespace geopro::render::interact { @@ -61,7 +60,6 @@ private: vtkSmartPointer preview_; // 已点几何(顶点圆点 + 实线折线) vtkSmartPointer rubber_; // 末点→光标 虚线橡皮筋 - vtkSmartPointer hint_; // 屏幕操作提示 Vec3 cursorPt_{{0, 0, 0}}; // 当前鼠标在切面上的投影点 bool hasCursor_ = false; bool cursorNearStart_ = false; // 面模式光标邻近起点 → 橡皮筋指向起点预览闭合 diff --git a/src/render/interact/InteractionManager.cpp b/src/render/interact/InteractionManager.cpp index 1b6f63a..df09dab 100644 --- a/src/render/interact/InteractionManager.cpp +++ b/src/render/interact/InteractionManager.cpp @@ -206,6 +206,13 @@ bool InteractionManager::selectSavedSlice(const std::string& dsId) { return false; } +void InteractionManager::deselectSlice() { + if (selected_ < 0) return; + selected_ = -1; + updateSelectionVisual(); // 清高亮(无选中切片) + safeRender(); +} + void InteractionManager::selectByTool(const SliceTool* tool) { int idx = -1; for (std::size_t i = 0; i < slices_.size(); ++i) diff --git a/src/render/interact/InteractionManager.hpp b/src/render/interact/InteractionManager.hpp index c350d7b..f4d5733 100644 --- a/src/render/interact/InteractionManager.hpp +++ b/src/render/interact/InteractionManager.hpp @@ -60,6 +60,8 @@ public: std::vector shownSavedSliceIds() const; // 当前已显示的已保存切片 dsId 列表 // 选中已显示的某 dsId 切片(列表操作定位到对应渲染切片);找到返回 true。 bool selectSavedSlice(const std::string& dsId); + // 清除切片选中(列表选中切到别的对象/异常时调用,否则 VTK 切片仍高亮,用户反馈)。 + void deselectSlice(); // 关闭选中切片(E56)。无选中则忽略。 void closeSelected();