diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index 2df2b120..5662c541 100644 Binary files a/Bin/Config/Lang/cn/nmNum_cn.qm and b/Bin/Config/Lang/cn/nmNum_cn.qm differ diff --git a/Bin/Config/Lang/cn/nmNum_cn.ts b/Bin/Config/Lang/cn/nmNum_cn.ts index 91c8795c..01ed3097 100644 --- a/Bin/Config/Lang/cn/nmNum_cn.ts +++ b/Bin/Config/Lang/cn/nmNum_cn.ts @@ -3346,6 +3346,11 @@ No valid error was returned; keep the last accepted result and reduce the step.< nmWxPebiResultControlBar + Display显示 + Point点 + Wireframe线框 + Surface表面 + Contour轮廓 Property属性 Display property显示属性 Color settings颜色设置 @@ -3400,7 +3405,6 @@ No valid error was returned; keep the last accepted result and reduce the step.< Well Names井名 Unable to display the latest result.无法显示最新结果。 Unable to display the latest result: %1无法显示最新结果:%1 - Current inputs have changed. Showing the last successful result.当前输入已修改,正在显示上一次成功求解的结果。 Export Animation导出动画 Another result animation export is already in progress.已有结果动画正在导出。 Unable to read the result animation timeline.无法读取结果动画时间轴。 @@ -3415,6 +3419,11 @@ No valid error was returned; keep the last accepted result and reduce the step.< nmWxPebiGridViewWidget + Display显示 + Point点 + Wireframe线框 + Surface表面 + Contour轮廓 No grid data.暂无网格数据。 Scale Grid比例网格 The grid preview data is invalid.网格预览数据无效。 diff --git a/Include/nmNum/nmRender/nmVTKMeshLayer.h b/Include/nmNum/nmRender/nmVTKMeshLayer.h index e747d2bc..23e4ce77 100644 --- a/Include/nmNum/nmRender/nmVTKMeshLayer.h +++ b/Include/nmNum/nmRender/nmVTKMeshLayer.h @@ -25,7 +25,9 @@ public: enum DisplayMode { DisplayMode_Surface = 0, ///< 只显示填充表面。 - DisplayMode_SurfaceWithEdges ///< 同时显示表面和单元边线。 + DisplayMode_SurfaceWithEdges = 1, ///< 同时显示表面和单元边线。 + DisplayMode_Point = 2, ///< 显示 Mapper 实际可渲染的原始顶点。 + DisplayMode_Wireframe = 3 ///< 以线框方式显示网格单元。 }; enum ScalarAssociation @@ -75,7 +77,9 @@ public: /** @brief 返回当前直接数据或管线输出数据集的非所有权指针。 */ vtkDataSet* getDataSet() const; - /** @brief 设置表面或表面加边线显示模式。 */ + /** @brief 判断枚举值是否为受支持的网格显示模式。 */ + static bool isValidDisplayMode(DisplayMode eMode); + /** @brief 设置点、线框、表面或表面加边线显示模式。 */ void setDisplayMode(DisplayMode eMode); /** @brief 返回当前显示模式。 */ DisplayMode getDisplayMode() const; diff --git a/Include/nmNum/nmSubWxs/nmPebiGridSceneController.h b/Include/nmNum/nmSubWxs/nmPebiGridSceneController.h index 4b5961a7..a3224a22 100644 --- a/Include/nmNum/nmSubWxs/nmPebiGridSceneController.h +++ b/Include/nmNum/nmSubWxs/nmPebiGridSceneController.h @@ -2,6 +2,7 @@ #define NMPEBIGRIDSCENECONTROLLER_H #include "nmSubWxs_global.h" +#include "nmVTKMeshLayer.h" #include @@ -29,6 +30,11 @@ public: /** @brief 解除网格引用并隐藏图层,不销毁 Scene 或 Renderer。 */ void clear(); + /** @brief 设置本窗口网格显示模式;无有效数据时只保存偏好。 */ + void setMeshDisplayMode(nmVTKMeshLayer::DisplayMode eMode); + /** @brief 返回本窗口网格显示模式偏好。 */ + nmVTKMeshLayer::DisplayMode getMeshDisplayMode() const; + /** @brief 切换 XY 坐标比例网格。 */ void setBoundsGridVisible(bool bVisible); diff --git a/Include/nmNum/nmSubWxs/nmPebiResultAnimationExporter.h b/Include/nmNum/nmSubWxs/nmPebiResultAnimationExporter.h index 08c5fc9d..25f20ec1 100644 --- a/Include/nmNum/nmSubWxs/nmPebiResultAnimationExporter.h +++ b/Include/nmNum/nmSubWxs/nmPebiResultAnimationExporter.h @@ -4,6 +4,7 @@ #include "nmSubWxs_global.h" #include "nmPebiResultAnimationExportSettings.h" #include "nmPebiResultDisplaySettings.h" +#include "nmVTKMeshLayer.h" #include #include @@ -60,6 +61,8 @@ public: bool m_bGradientBackground; /** @brief 当前屏幕相机的深拷贝,导出器不会修改屏幕相机。 */ vtkSmartPointer m_pCamera; + /** @brief 开始导出前冻结的网格显示模式。 */ + nmVTKMeshLayer::DisplayMode m_eMeshDisplayMode; }; /** @param pParent Qt父对象,不参与VTK资源所有权。 */ diff --git a/Include/nmNum/nmSubWxs/nmPebiResultSceneController.h b/Include/nmNum/nmSubWxs/nmPebiResultSceneController.h index a85c8464..c8324ddb 100644 --- a/Include/nmNum/nmSubWxs/nmPebiResultSceneController.h +++ b/Include/nmNum/nmSubWxs/nmPebiResultSceneController.h @@ -3,6 +3,7 @@ #include "nmSubWxs_global.h" #include "nmPebiResultDisplaySettings.h" +#include "nmVTKMeshLayer.h" #include #include @@ -71,6 +72,11 @@ public: const nmPebiResultDisplaySettings& oSettings, QString* pError = nullptr); + /** @brief 设置本窗口网格显示模式;无有效快照时只保存偏好。 */ + void setMeshDisplayMode(nmVTKMeshLayer::DisplayMode eMode); + /** @brief 返回本窗口网格显示模式偏好。 */ + nmVTKMeshLayer::DisplayMode getMeshDisplayMode() const; + /** @brief 设置色标显隐;默认显示,偏好跨快照保留。 */ void setScalarBarVisible(bool bVisible); /** @brief 设置平面比例网格显隐;偏好跨快照保留。 */ diff --git a/Include/nmNum/nmSubWxs/nmWxPebiGridViewWidget.h b/Include/nmNum/nmSubWxs/nmWxPebiGridViewWidget.h index ba110cbd..9da66ff5 100644 --- a/Include/nmNum/nmSubWxs/nmWxPebiGridViewWidget.h +++ b/Include/nmNum/nmSubWxs/nmWxPebiGridViewWidget.h @@ -6,6 +6,7 @@ #include class QAction; +class QComboBox; class QLabel; class QMenu; class QToolBar; @@ -17,7 +18,7 @@ class nmWxVTKRenderContainerWidget; /** * @brief 二维 PEBI 网格预览业务组合控件。 * - * 本类定位为网格几何检查窗口,只显示固定灰色表面、黑色单元边线和 + * 本类定位为网格几何检查窗口,支持点、线框、表面和轮廓四种模式及 * 可选的 XY 比例网格,不解释输入数据中的历史属性数组。右侧工具栏和 * 右键菜单复用同一批 QAction;通用导航、旋转中心、截图和打印能力由 * 内部 nmRender 容器提供,不继承业务视图基类。 @@ -48,6 +49,8 @@ public: bool hasViewData() const; private slots: + /** @brief 从 itemData 读取并提交当前窗口的网格显示模式。 */ + void onDisplayModeIndexChanged(int nIndex); /** @brief 切换世界坐标中的 XY 比例网格,不改变相机。 */ void onBoundsGridToggled(bool bVisible); /** @brief 向通用右键菜单追加本窗口独占的比例网格动作。 */ @@ -60,6 +63,8 @@ private: void createUi(); /** @brief 恢复无数据时的比例网格偏好和底栏文字。 */ void resetControls(); + /** @brief 从 SceneController 镜像显示模式,且不触发用户选择槽。 */ + void synchronizeDisplayModeFromScene(); /** @brief 统一启停依赖有效网格的业务动作。 */ void setControlsEnabled(bool bEnabled); @@ -67,6 +72,7 @@ private: nmPebiGridSceneController* m_pSceneController; ///< 本类独占,必须先于容器销毁。 QToolBar* m_pToolBar; ///< Qt 父对象拥有的右侧竖向工具栏。 QAction* m_pBoundsGridAction; ///< 工具栏和右键菜单共享的比例网格动作。 + QComboBox* m_pDisplayModeCombo; ///< 显示模式框,itemData 保存 DisplayMode 枚举值。 QLabel* m_pGridInfoLabel; ///< 底栏左侧的网格类型、单元数和平面信息。 QLabel* m_pStatusLabel; ///< 建网失败且保留旧网格时使用的黄色提示栏。 bool m_bHasViewData; ///< 与场景控制器的成功数据状态保持同步。 diff --git a/Include/nmNum/nmSubWxs/nmWxPebiResultControlBar.h b/Include/nmNum/nmSubWxs/nmWxPebiResultControlBar.h index 9b4c92e1..13a93a4a 100644 --- a/Include/nmNum/nmSubWxs/nmWxPebiResultControlBar.h +++ b/Include/nmNum/nmSubWxs/nmWxPebiResultControlBar.h @@ -56,6 +56,8 @@ signals: void exportAnimationRequested(); private slots: + /** @brief 用户选择显示模式时提交 itemData 中的 DisplayMode 枚举值。 */ + void onDisplayModeIndexChanged(int nIndex); /** @brief 用户选择属性时提交稳定属性 ID。 */ void onPropertyIndexChanged(int nIndex); /** @brief 用户选择时间时提交 itemData 中的零基帧。 */ @@ -100,6 +102,8 @@ private: nmPebiResultPlaybackController* m_pPlaybackController; /** @brief 已渲染属性、帧和显示设置的唯一真源;本控件不拥有。 */ nmPebiResultSceneController* m_pSceneController; + /** @brief 显示模式选择框,条目数据保存 DisplayMode 枚举值。 */ + QComboBox* m_pDisplayModeCombo; /** @brief 属性选择框,条目数据保存稳定英文属性 ID。 */ QComboBox* m_pPropertyCombo; /** @brief 使用固定色带图标打开颜色设置对话框。 */ diff --git a/Include/nmNum/nmSubWxs/nmWxPebiResultViewWidget.h b/Include/nmNum/nmSubWxs/nmWxPebiResultViewWidget.h index c4fe2585..1feeebb1 100644 --- a/Include/nmNum/nmSubWxs/nmWxPebiResultViewWidget.h +++ b/Include/nmNum/nmSubWxs/nmWxPebiResultViewWidget.h @@ -58,9 +58,9 @@ private slots: void onDataManagerDestroyed(QObject* pObject); /** @brief 将业务显隐 QAction 追加到通用右键菜单。 */ void appendContextMenuActions(QMenu* pMenu); - /** @brief 转发非阻塞操作错误,但不覆盖绑定失败或过期提示。 */ + /** @brief 转发非阻塞操作错误,但不覆盖绑定失败提示。 */ void onControlOperationFailed(const QString& sError); - /** @brief 明确操作成功后清除旧普通错误,不影响绑定失败或过期状态。 */ + /** @brief 明确操作成功后清除旧普通错误,不影响绑定失败状态。 */ void onControlOperationSucceeded(); /** @brief 将 QVTK 视口尺寸同步给结果场景,不主动触发额外渲染。 */ void onViewportSizeChanged(int nWidth, int nHeight); @@ -90,7 +90,7 @@ private: bool bCurrent); /** @brief 从 SceneController 镜像三个业务 QAction 的勾选和可用状态。 */ void synchronizeBusinessActions(); - /** @brief 根据“绑定失败、过期、操作错误”的固定优先级刷新提示栏。 */ + /** @brief 按绑定失败、操作错误的优先级刷新提示栏,无错误时隐藏。 */ void refreshStatusBar(); /** @brief 断开当前 Manager 的两个连接,不访问已经析构的对象。 */ void disconnectDataManager(); @@ -100,7 +100,7 @@ private: nmPebiResultSceneController* m_pSceneController; ///< 唯一结果场景控制器。 nmWxPebiResultControlBar* m_pControlBar; ///< 独占回放控制器和 Timer 的底栏。 QToolBar* m_pToolBar; ///< Qt 父对象拥有的右侧竖向工具栏。 - QLabel* m_pStatusLabel; ///< 过期或错误的非阻塞提示栏。 + QLabel* m_pStatusLabel; ///< 绑定失败或操作错误的非阻塞提示栏。 QAction* m_pScalarBarAction; ///< 色标显隐动作。 QAction* m_pScaleGridAction; ///< 比例网格显隐动作。 diff --git a/Src/nmNum/nmRender/nmVTKMeshLayer.cpp b/Src/nmNum/nmRender/nmVTKMeshLayer.cpp index b6d7bbcf..44d943cd 100644 --- a/Src/nmNum/nmRender/nmVTKMeshLayer.cpp +++ b/Src/nmNum/nmRender/nmVTKMeshLayer.cpp @@ -52,6 +52,8 @@ nmVTKMeshLayer::nmVTKMeshLayer(const QString& sLayerId) m_pActor->SetMapper(m_pMapper); m_pActor->GetProperty()->SetRepresentationToSurface(); m_pActor->GetProperty()->EdgeVisibilityOff(); + m_pActor->GetProperty()->SetPointSize(3.0f); + m_pActor->GetProperty()->SetLineWidth(1.0f); setRenderProp(m_pActor); } @@ -208,6 +210,14 @@ vtkDataSet* nmVTKMeshLayer::getDataSet() const return getInputDataSet(false); } +bool nmVTKMeshLayer::isValidDisplayMode(DisplayMode eMode) +{ + return eMode == DisplayMode_Surface || + eMode == DisplayMode_SurfaceWithEdges || + eMode == DisplayMode_Point || + eMode == DisplayMode_Wireframe; +} + void nmVTKMeshLayer::setDisplayMode(DisplayMode eMode) { Q_ASSERT(isMeshLayerGuiThread()); @@ -215,13 +225,30 @@ void nmVTKMeshLayer::setDisplayMode(DisplayMode eMode) return; } - // 未识别枚举值按纯表面处理,避免把无效值带入 VTK Property。 - m_eDisplayMode = eMode == DisplayMode_SurfaceWithEdges - ? DisplayMode_SurfaceWithEdges : DisplayMode_Surface; + // 未识别枚举值仍按纯表面处理,保持旧接口的降级语义。 + m_eDisplayMode = isValidDisplayMode(eMode) + ? eMode : DisplayMode_Surface; vtkProperty* pProperty = m_pActor->GetProperty(); - pProperty->SetRepresentationToSurface(); - pProperty->SetEdgeVisibility( - m_eDisplayMode == DisplayMode_SurfaceWithEdges ? 1 : 0); + pProperty->SetPointSize(3.0f); + pProperty->SetLineWidth(1.0f); + pProperty->EdgeVisibilityOff(); + + switch(m_eDisplayMode) { + case DisplayMode_Point: + pProperty->SetRepresentationToPoints(); + break; + case DisplayMode_Wireframe: + pProperty->SetRepresentationToWireframe(); + break; + case DisplayMode_SurfaceWithEdges: + pProperty->SetRepresentationToSurface(); + pProperty->EdgeVisibilityOn(); + break; + case DisplayMode_Surface: + default: + pProperty->SetRepresentationToSurface(); + break; + } } nmVTKMeshLayer::DisplayMode nmVTKMeshLayer::getDisplayMode() const diff --git a/Src/nmNum/nmSubWxs/nmPebiGridSceneController.cpp b/Src/nmNum/nmSubWxs/nmPebiGridSceneController.cpp index 28cb766d..ff59a18f 100644 --- a/Src/nmNum/nmSubWxs/nmPebiGridSceneController.cpp +++ b/Src/nmNum/nmSubWxs/nmPebiGridSceneController.cpp @@ -52,11 +52,31 @@ public: : m_pContainer(pRenderContainer), m_pMeshLayer(nullptr), m_pPlanarScaleGridLayer(nullptr), + m_eMeshDisplayMode( + nmVTKMeshLayer::DisplayMode_SurfaceWithEdges), m_bBoundsGridVisible(false), m_bHasData(false) { } + /** @brief 将窗口偏好应用到现有网格 Actor,不触发渲染。 */ + void applyMeshDisplayModeWithoutRender() + { + if(m_pMeshLayer == nullptr) { + return; + } + + m_pMeshLayer->setDisplayMode(m_eMeshDisplayMode); + vtkProperty* pProperty = m_pMeshLayer->getActor()->GetProperty(); + const bool bUseBlackColor = + m_eMeshDisplayMode == nmVTKMeshLayer::DisplayMode_Point || + m_eMeshDisplayMode == + nmVTKMeshLayer::DisplayMode_Wireframe; + const double dColor = bUseBlackColor ? 0.0 : 211.0 / 255.0; + pProperty->SetColor(dColor, dColor, dColor); + pProperty->SetEdgeColor(0.0, 0.0, 0.0); + } + bool ensureBaseLayers() { nmVTKScene* pScene = m_pContainer != nullptr @@ -76,14 +96,9 @@ public: return false; } m_pMeshLayer = pLayer; - // PEBI 预览固定使用稳定灰色表面和黑色单元边线。 - m_pMeshLayer->getActor()->GetProperty()->SetColor( - 211.0 / 255.0, 211.0 / 255.0, - 211.0 / 255.0); - m_pMeshLayer->getActor()->GetProperty()->SetEdgeColor( - 0.0, 0.0, 0.0); - // 仅关闭当前 PEBI 预览 Actor 的光照,避免固定面色随视角变化。 + // 关闭光照,保证各显示模式的固定黑色或灰色不随视角变化。 m_pMeshLayer->getActor()->GetProperty()->LightingOff(); + applyMeshDisplayModeWithoutRender(); } if(m_pPlanarScaleGridLayer == nullptr) { @@ -113,10 +128,9 @@ public: m_oViewData.clear(); return false; } - // 输入可能包含历史数组,网格预览始终关闭标量着色并显示单元边线。 + // 输入可能包含历史数组,网格预览始终关闭标量着色。 m_pMeshLayer->setScalarVisibility(false); - m_pMeshLayer->setDisplayMode( - nmVTKMeshLayer::DisplayMode_SurfaceWithEdges); + applyMeshDisplayModeWithoutRender(); m_pMeshLayer->setVisible(true); if(!m_pPlanarScaleGridLayer->setBounds( @@ -161,6 +175,7 @@ public: nmVTKMeshLayer* m_pMeshLayer; ///< Scene 所有,控制器只观察。 nmVTKPlanarScaleGridLayer* m_pPlanarScaleGridLayer; ///< Scene 所有。 nmPebiGridViewData m_oViewData; + nmVTKMeshLayer::DisplayMode m_eMeshDisplayMode; ///< 本窗口显示偏好。 bool m_bBoundsGridVisible; bool m_bHasData; }; @@ -253,6 +268,31 @@ void nmPebiGridSceneController::clear() m_pPrivate->m_bBoundsGridVisible = false; } +void nmPebiGridSceneController::setMeshDisplayMode( + nmVTKMeshLayer::DisplayMode eMode) +{ + if(m_pPrivate == nullptr || + !nmVTKMeshLayer::isValidDisplayMode(eMode) || + m_pPrivate->m_eMeshDisplayMode == eMode) { + return; + } + + m_pPrivate->m_eMeshDisplayMode = eMode; + m_pPrivate->applyMeshDisplayModeWithoutRender(); + if(m_pPrivate->m_bHasData && + m_pPrivate->m_pContainer != nullptr) { + m_pPrivate->m_pContainer->renderView(); + } +} + +nmVTKMeshLayer::DisplayMode +nmPebiGridSceneController::getMeshDisplayMode() const +{ + return m_pPrivate != nullptr + ? m_pPrivate->m_eMeshDisplayMode + : nmVTKMeshLayer::DisplayMode_SurfaceWithEdges; +} + void nmPebiGridSceneController::setBoundsGridVisible(bool bVisible) { if(m_pPrivate == nullptr) { @@ -264,6 +304,7 @@ void nmPebiGridSceneController::setBoundsGridVisible(bool bVisible) bVisible && m_pPrivate->m_bHasData); } if(m_pPrivate->m_bHasData && m_pPrivate->m_pContainer != nullptr) { + m_pPrivate->applyMeshDisplayModeWithoutRender(); m_pPrivate->m_pContainer->renderView(); } } diff --git a/Src/nmNum/nmSubWxs/nmPebiResultAnimationExporter.cpp b/Src/nmNum/nmSubWxs/nmPebiResultAnimationExporter.cpp index 71a7a8c6..3b257928 100644 --- a/Src/nmNum/nmSubWxs/nmPebiResultAnimationExporter.cpp +++ b/Src/nmNum/nmSubWxs/nmPebiResultAnimationExporter.cpp @@ -255,7 +255,9 @@ nmPebiResultAnimationExporter::Request::Request() m_bScaleGridVisible(false), m_bWellNamesVisible(true), m_bGradientBackground(false), - m_pCamera(nullptr) + m_pCamera(nullptr), + m_eMeshDisplayMode( + nmVTKMeshLayer::DisplayMode_SurfaceWithEdges) { m_dBackground[0] = m_dBackground[1] = m_dBackground[2] = 0.0; m_dBackground2[0] = m_dBackground2[1] = m_dBackground2[2] = 0.0; @@ -291,7 +293,9 @@ nmPebiResultAnimationExporter::exportAnimation( oRequest.m_sPropertyId, oDescriptor) || oDescriptor.m_eTimeType != NM_PebiResultProperty_Dynamic || oDescriptor.m_eAssociation != - NM_PebiResultPropertyAssociation_CellData || + NM_PebiResultPropertyAssociation_CellData || + !nmVTKMeshLayer::isValidDisplayMode( + oRequest.m_eMeshDisplayMode) || !oRequest.m_oExportSettings.isValid(nAvailableFrameCount) || oRequest.m_sTargetFilePath.isEmpty() || oRequest.m_pCamera == nullptr) { @@ -370,8 +374,7 @@ nmPebiResultAnimationExporter::exportAnimation( nmVTKScene oScene(pRenderer); nmVTKMeshLayer* pMeshLayer = new nmVTKMeshLayer( QString::fromLatin1(EXPORT_MESH_LAYER_ID)); - pMeshLayer->setDisplayMode( - nmVTKMeshLayer::DisplayMode_SurfaceWithEdges); + pMeshLayer->setDisplayMode(oRequest.m_eMeshDisplayMode); pMeshLayer->getActor()->GetProperty()->SetEdgeColor(0.0, 0.0, 0.0); pMeshLayer->getActor()->GetProperty()->LightingOff(); if(!pMeshLayer->setInputConnection( diff --git a/Src/nmNum/nmSubWxs/nmPebiResultSceneController.cpp b/Src/nmNum/nmSubWxs/nmPebiResultSceneController.cpp index 29ece80f..981e3807 100644 --- a/Src/nmNum/nmSubWxs/nmPebiResultSceneController.cpp +++ b/Src/nmNum/nmSubWxs/nmPebiResultSceneController.cpp @@ -189,6 +189,8 @@ public: m_pScaleGridLayer(nullptr), m_pWellLayer(nullptr), m_pAdapter(nullptr), + m_eMeshDisplayMode( + nmVTKMeshLayer::DisplayMode_SurfaceWithEdges), m_nCurrentFrameIndex(-1), m_nSnapshotBindingRevision(0), m_nViewportWidth(0), @@ -214,6 +216,18 @@ public: return m_pContainer != nullptr ? m_pContainer->getScene() : nullptr; } + /** @brief 将窗口偏好应用到现有结果网格,不触发渲染。 */ + void applyMeshDisplayModeWithoutRender() + { + if(m_pMeshLayer == nullptr) { + return; + } + m_pMeshLayer->setDisplayMode(m_eMeshDisplayMode); + // Contour 使用黑色单元边线,其余模式关闭边线但保留该颜色偏好。 + m_pMeshLayer->getActor()->GetProperty()->SetEdgeColor( + 0.0, 0.0, 0.0); + } + bool synchronizeLayerPointers() { nmVTKScene* pScene = getScene(); @@ -335,6 +349,7 @@ public: m_pMeshLayer->getActor()->GetProperty()->SetEdgeColor( 0.0, 0.0, 0.0); m_pMeshLayer->getActor()->GetProperty()->LightingOff(); + applyMeshDisplayModeWithoutRender(); } if(m_pScalarBarLayer == nullptr) { @@ -471,11 +486,15 @@ public: } // MeshLayer 在触碰旧 Mapper 前完成全部校验,失败不会破坏旧画面。 - return m_pMeshLayer->setInputConnection( - pAdapter->getOutputPort(), - pAdapter->getCurrentScalarArrayName(), - nmVTKMeshLayer::ScalarAssociation_CellData, - pLookupTable); + if(!m_pMeshLayer->setInputConnection( + pAdapter->getOutputPort(), + pAdapter->getCurrentScalarArrayName(), + nmVTKMeshLayer::ScalarAssociation_CellData, + pLookupTable)) { + return false; + } + applyMeshDisplayModeWithoutRender(); + return true; } void applyScalarBarLayout() @@ -513,9 +532,7 @@ public: { synchronizeLayerPointers(); if(m_pMeshLayer != nullptr) { - // 结果网格固定显示彩色表面和黑色单元边线,不保存可关闭的偏好。 - m_pMeshLayer->setDisplayMode( - nmVTKMeshLayer::DisplayMode_SurfaceWithEdges); + applyMeshDisplayModeWithoutRender(); m_pMeshLayer->setScalarVisibility(bHasData); // 彩色结果网格是有效快照的主内容,不提供独立隐藏状态。 m_pMeshLayer->setVisible(bHasData); @@ -677,6 +694,7 @@ public: QString m_sCurrentPropertyId; /** @brief 按稳定属性 ID 保存的窗口级样式;clear 不删除。 */ QMap m_mapDisplaySettings; + nmVTKMeshLayer::DisplayMode m_eMeshDisplayMode; ///< 本窗口显示偏好。 int m_nCurrentFrameIndex; /** @brief 不同快照对象完整提交成功后的单调序号,clear 和失败均不回退。 */ quint64 m_nSnapshotBindingRevision; @@ -1139,6 +1157,35 @@ bool nmPebiResultSceneController::applyCurrentDisplaySettings( return true; } +void nmPebiResultSceneController::setMeshDisplayMode( + nmVTKMeshLayer::DisplayMode eMode) +{ + Q_ASSERT(isResultSceneGuiThread()); + if(!isResultSceneGuiThread() || m_pPrivate == nullptr || + !nmVTKMeshLayer::isValidDisplayMode(eMode)) { + return; + } + + m_pPrivate->discardActiveDataIfLayersLost(); + if(m_pPrivate->m_eMeshDisplayMode == eMode) { + return; + } + + m_pPrivate->m_eMeshDisplayMode = eMode; + m_pPrivate->applyMeshDisplayModeWithoutRender(); + if(hasSnapshot() && m_pPrivate->m_pContainer != nullptr) { + m_pPrivate->m_pContainer->renderView(); + } +} + +nmVTKMeshLayer::DisplayMode +nmPebiResultSceneController::getMeshDisplayMode() const +{ + return m_pPrivate != nullptr + ? m_pPrivate->m_eMeshDisplayMode + : nmVTKMeshLayer::DisplayMode_SurfaceWithEdges; +} + void nmPebiResultSceneController::setScalarBarVisible(bool bVisible) { Q_ASSERT(isResultSceneGuiThread()); diff --git a/Src/nmNum/nmSubWxs/nmWxPebiGridViewWidget.cpp b/Src/nmNum/nmSubWxs/nmWxPebiGridViewWidget.cpp index 3dd3dbf5..3a9665b0 100644 --- a/Src/nmNum/nmSubWxs/nmWxPebiGridViewWidget.cpp +++ b/Src/nmNum/nmSubWxs/nmWxPebiGridViewWidget.cpp @@ -3,10 +3,12 @@ #include "nmPebiGridSceneController.h" #include "nmPebiGridViewData.h" +#include "nmVTKMeshLayer.h" #include "nmVTKViewCommandController.h" #include "nmWxVTKRenderContainerWidget.h" #include +#include #include #include #include @@ -14,6 +16,7 @@ #include #include #include +#include #include namespace { @@ -33,6 +36,7 @@ nmWxPebiGridViewWidget::nmWxPebiGridViewWidget(QWidget* pParent) m_pSceneController(nullptr), m_pToolBar(nullptr), m_pBoundsGridAction(nullptr), + m_pDisplayModeCombo(nullptr), m_pGridInfoLabel(nullptr), m_pStatusLabel(nullptr), m_bHasViewData(false) @@ -41,6 +45,7 @@ nmWxPebiGridViewWidget::nmWxPebiGridViewWidget(QWidget* pParent) // 控制器必须在容器之后创建,以便使用当前实例独占的 Scene 和相机。 m_pSceneController = new nmPebiGridSceneController(m_pRenderContainer); + synchronizeDisplayModeFromScene(); resetControls(); setControlsEnabled(false); m_pRenderContainer->showEmptyState(tr("No grid data.")); @@ -176,18 +181,47 @@ void nmWxPebiGridViewWidget::createUi() m_pStatusLabel->hide(); pMainLayout->addWidget(m_pStatusLabel); - // 底栏只承担几何信息展示,不放置属性或渲染模式控件。 + // 显示模式属于当前预览窗口,业务控件不下沉到通用渲染容器。 QWidget* pControlBar = new QWidget(this); QHBoxLayout* pControlLayout = new QHBoxLayout(pControlBar); pControlLayout->setContentsMargins(8, 5, 8, 5); pControlLayout->setSpacing(6); + QLabel* pDisplayLabel = new QLabel(tr("Display"), pControlBar); + pDisplayLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + m_pDisplayModeCombo = new QComboBox(pControlBar); + m_pDisplayModeCombo->addItem( + tr("Point"), + QVariant(static_cast( + nmVTKMeshLayer::DisplayMode_Point))); + m_pDisplayModeCombo->addItem( + tr("Wireframe"), + QVariant(static_cast( + nmVTKMeshLayer::DisplayMode_Wireframe))); + m_pDisplayModeCombo->addItem( + tr("Surface"), + QVariant(static_cast( + nmVTKMeshLayer::DisplayMode_Surface))); + m_pDisplayModeCombo->addItem( + tr("Contour"), + QVariant(static_cast( + nmVTKMeshLayer::DisplayMode_SurfaceWithEdges))); + m_pDisplayModeCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); + m_pDisplayModeCombo->setFixedHeight(24); + m_pDisplayModeCombo->setSizePolicy( + QSizePolicy::Preferred, QSizePolicy::Fixed); + m_pGridInfoLabel = new QLabel(pControlBar); m_pGridInfoLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + pControlLayout->addWidget(pDisplayLabel, 0, Qt::AlignVCenter); + pControlLayout->addWidget(m_pDisplayModeCombo); + pControlLayout->addSpacing(2); pControlLayout->addWidget(m_pGridInfoLabel); pControlLayout->addStretch(1); pMainLayout->addWidget(pControlBar); + connect(m_pDisplayModeCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(onDisplayModeIndexChanged(int))); connect(m_pBoundsGridAction, SIGNAL(toggled(bool)), this, SLOT(onBoundsGridToggled(bool))); connect(m_pRenderContainer, SIGNAL(contextMenuAboutToShow(QMenu*)), @@ -218,6 +252,7 @@ bool nmWxPebiGridViewWidget::setViewData( oViewData.getValidCellCount()))); m_pStatusLabel->hide(); m_bHasViewData = true; + synchronizeDisplayModeFromScene(); setControlsEnabled(true); return true; } @@ -275,6 +310,50 @@ void nmWxPebiGridViewWidget::resetControls() void nmWxPebiGridViewWidget::setControlsEnabled(bool bEnabled) { m_pBoundsGridAction->setEnabled(bEnabled); + m_pDisplayModeCombo->setEnabled(bEnabled); +} + +void nmWxPebiGridViewWidget::synchronizeDisplayModeFromScene() +{ + if(m_pDisplayModeCombo == nullptr || m_pSceneController == nullptr) { + return; + } + const int nMode = static_cast( + m_pSceneController->getMeshDisplayMode()); + for(int nIndex = 0; + nIndex < m_pDisplayModeCombo->count(); ++nIndex) { + if(m_pDisplayModeCombo->itemData(nIndex).toInt() == nMode) { + const bool bBlocked = + m_pDisplayModeCombo->blockSignals(true); + m_pDisplayModeCombo->setCurrentIndex(nIndex); + m_pDisplayModeCombo->blockSignals(bBlocked); + return; + } + } +} + +void nmWxPebiGridViewWidget::onDisplayModeIndexChanged(int nIndex) +{ + if(m_pDisplayModeCombo == nullptr || + !m_pDisplayModeCombo->isEnabled() || + m_pSceneController == nullptr || !m_bHasViewData || + !m_pSceneController->hasViewData() || nIndex < 0 || + nIndex >= m_pDisplayModeCombo->count()) { + return; + } + + bool bConverted = false; + const int nMode = m_pDisplayModeCombo->itemData(nIndex).toInt( + &bConverted); + const nmVTKMeshLayer::DisplayMode eMode = + static_cast( + nMode); + if(!bConverted || !nmVTKMeshLayer::isValidDisplayMode(eMode)) { + synchronizeDisplayModeFromScene(); + return; + } + m_pSceneController->setMeshDisplayMode(eMode); + synchronizeDisplayModeFromScene(); } void nmWxPebiGridViewWidget::onBoundsGridToggled(bool bVisible) diff --git a/Src/nmNum/nmSubWxs/nmWxPebiResultControlBar.cpp b/Src/nmNum/nmSubWxs/nmWxPebiResultControlBar.cpp index 93bbc9c5..9fa2d4e6 100644 --- a/Src/nmNum/nmSubWxs/nmWxPebiResultControlBar.cpp +++ b/Src/nmNum/nmSubWxs/nmWxPebiResultControlBar.cpp @@ -4,6 +4,7 @@ #include "nmPebiResultPlaybackController.h" #include "nmPebiResultPropertyDisplayText.h" #include "nmPebiResultSceneController.h" +#include "nmVTKMeshLayer.h" #include "nmWxPebiResultAnimationSettingsDlg.h" #include "nmWxPebiResultColorScaleSettingsDlg.h" @@ -56,6 +57,7 @@ nmWxPebiResultControlBar::nmWxPebiResultControlBar( : QWidget(pParent), m_pPlaybackController(nullptr), m_pSceneController(pSceneController), + m_pDisplayModeCombo(nullptr), m_pPropertyCombo(nullptr), m_pColorSettingsButton(nullptr), m_pFirstButton(nullptr), @@ -125,6 +127,34 @@ void nmWxPebiResultControlBar::setExportInProgress(bool bInProgress) refreshFromController(); } +void nmWxPebiResultControlBar::onDisplayModeIndexChanged(int nIndex) +{ + if(m_pDisplayModeCombo == nullptr || + !m_pDisplayModeCombo->isEnabled() || + m_pPlaybackController == nullptr || m_pSceneController == nullptr || + m_bExportInProgress || m_pPlaybackController->isPlaying() || + !m_pSceneController->hasSnapshot() || nIndex < 0 || + nIndex >= m_pDisplayModeCombo->count()) { + return; + } + + bool bConverted = false; + const int nMode = m_pDisplayModeCombo->itemData(nIndex).toInt( + &bConverted); + const nmVTKMeshLayer::DisplayMode eMode = + static_cast( + nMode); + if(!bConverted || !nmVTKMeshLayer::isValidDisplayMode(eMode)) { + refreshFromController(); + return; + } + if(eMode == m_pSceneController->getMeshDisplayMode()) { + return; + } + m_pSceneController->setMeshDisplayMode(eMode); + emit operationSucceeded(); +} + void nmWxPebiResultControlBar::onPropertyIndexChanged(int nIndex) { if(m_pPlaybackController == nullptr || m_bExportInProgress || @@ -252,6 +282,19 @@ void nmWxPebiResultControlBar::onExportAnimationTriggered() void nmWxPebiResultControlBar::refreshFromController() { + if(m_pDisplayModeCombo != nullptr && m_pSceneController != nullptr) { + const int nDisplayModeIndex = findItemByData( + m_pDisplayModeCombo, + QVariant(static_cast( + m_pSceneController->getMeshDisplayMode()))); + if(nDisplayModeIndex >= 0) { + const bool bDisplayModeBlocked = + m_pDisplayModeCombo->blockSignals(true); + m_pDisplayModeCombo->setCurrentIndex(nDisplayModeIndex); + m_pDisplayModeCombo->blockSignals(bDisplayModeBlocked); + } + } + // 控件状态严格镜像四类 Scene 状态:无快照、静态、单帧动态、多帧动态。 const bool bAvailable = m_pPlaybackController != nullptr && m_pPlaybackController->isAvailable(); @@ -263,6 +306,7 @@ void nmWxPebiResultControlBar::refreshFromController() m_sDisplayedSnapshotId.clear(); m_nDisplayedBindingRevision = 0; m_listDisplayedPropertyIds.clear(); + m_pDisplayModeCombo->setEnabled(false); m_pPropertyCombo->setEnabled(false); m_pColorSettingsButton->setEnabled(false); m_pFirstButton->setEnabled(false); @@ -336,6 +380,7 @@ void nmWxPebiResultControlBar::refreshFromController() nFrameCount > 1; const bool bPlaying = m_pPlaybackController->isPlaying(); const bool bLocked = bPlaying || m_bExportInProgress; + m_pDisplayModeCombo->setEnabled(!bLocked); m_pPropertyCombo->setEnabled(!bLocked); m_pColorSettingsButton->setEnabled(!bLocked); m_pTimeCombo->setEnabled(bHasTime && !bLocked); @@ -382,6 +427,37 @@ void nmWxPebiResultControlBar::createUi() pLayout->setContentsMargins(4, 3, 4, 3); pLayout->setSpacing(4); + QLabel* pDisplayLabel = new QLabel(tr("Display"), this); + pDisplayLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + m_pDisplayModeCombo = new QComboBox(this); + m_pDisplayModeCombo->addItem( + tr("Point"), + QVariant(static_cast( + nmVTKMeshLayer::DisplayMode_Point))); + m_pDisplayModeCombo->addItem( + tr("Wireframe"), + QVariant(static_cast( + nmVTKMeshLayer::DisplayMode_Wireframe))); + m_pDisplayModeCombo->addItem( + tr("Surface"), + QVariant(static_cast( + nmVTKMeshLayer::DisplayMode_Surface))); + m_pDisplayModeCombo->addItem( + tr("Contour"), + QVariant(static_cast( + nmVTKMeshLayer::DisplayMode_SurfaceWithEdges))); + m_pDisplayModeCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); + m_pDisplayModeCombo->setFixedHeight(24); + m_pDisplayModeCombo->setSizePolicy( + QSizePolicy::Preferred, QSizePolicy::Fixed); + const int nInitialDisplayModeIndex = findItemByData( + m_pDisplayModeCombo, + QVariant(static_cast( + m_pSceneController->getMeshDisplayMode()))); + if(nInitialDisplayModeIndex >= 0) { + m_pDisplayModeCombo->setCurrentIndex(nInitialDisplayModeIndex); + } + QLabel* pPropertyLabel = new QLabel(tr("Property"), this); pPropertyLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); @@ -436,6 +512,9 @@ void nmWxPebiResultControlBar::createUi() m_pExportButton->setIconSize(QSize(16, 16)); m_pExportButton->setDefaultAction(m_pExportAction); + pLayout->addWidget(pDisplayLabel, 0, Qt::AlignVCenter); + pLayout->addWidget(m_pDisplayModeCombo); + pLayout->addSpacing(CONTROL_GROUP_GAP); pLayout->addWidget(pPropertyLabel, 0, Qt::AlignVCenter); pLayout->addWidget(m_pPropertyCombo); pLayout->addWidget(m_pColorSettingsButton); @@ -450,6 +529,8 @@ void nmWxPebiResultControlBar::createUi() pLayout->addWidget(m_pExportButton); pLayout->addStretch(1); + connect(m_pDisplayModeCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(onDisplayModeIndexChanged(int))); connect(m_pPropertyCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onPropertyIndexChanged(int))); connect(m_pColorSettingsButton, SIGNAL(clicked()), diff --git a/Src/nmNum/nmSubWxs/nmWxPebiResultViewWidget.cpp b/Src/nmNum/nmSubWxs/nmWxPebiResultViewWidget.cpp index 88a3417d..0c0d15a0 100644 --- a/Src/nmNum/nmSubWxs/nmWxPebiResultViewWidget.cpp +++ b/Src/nmNum/nmSubWxs/nmWxPebiResultViewWidget.cpp @@ -7,6 +7,7 @@ #include "nmPebiResultPlaybackController.h" #include "nmPebiResultSceneController.h" #include "nmPebiResultSnapshot.h" +#include "nmVTKMeshLayer.h" #include "nmVTKViewCommandController.h" #include "nmWxPebiResultControlBar.h" #include "nmWxPebiResultAnimationExportDlg.h" @@ -237,9 +238,8 @@ void nmWxPebiResultViewWidget::appendContextMenuActions(QMenu* pMenu) void nmWxPebiResultViewWidget::onControlOperationFailed( const QString& sError) { - // 高优先级状态存在时丢弃普通错误,避免其在过期提示解除后突然出现。 - if(!m_sBindingError.isEmpty() || - (m_bObservedStateValid && !m_bObservedSnapshotCurrent)) { + // 绑定失败优先显示;输入修改不再提示,也不屏蔽后续真实操作错误。 + if(!m_sBindingError.isEmpty()) { return; } m_sOperationError = sError; @@ -251,7 +251,7 @@ void nmWxPebiResultViewWidget::onControlOperationSucceeded() if(m_sOperationError.isEmpty()) { return; } - // 这里只清除最低优先级的普通操作错误;刷新函数仍优先显示绑定失败和过期。 + // 这里只清除普通操作错误;刷新函数仍优先显示绑定失败。 m_sOperationError.clear(); refreshStatusBar(); } @@ -300,10 +300,13 @@ void nmWxPebiResultViewWidget::onExportAnimationRequested() m_pSceneController->getCurrentSnapshotId(); const QString sFrozenPropertyId = m_pSceneController->getCurrentPropertyId(); + const nmVTKMeshLayer::DisplayMode eFrozenMeshDisplayMode = + m_pSceneController->getMeshDisplayMode(); const int nFrameCount = pFrozenSnapshot->getPropertyFrameCount( sFrozenPropertyId); if(nFrozenBindingRevision == 0 || sFrozenSnapshotId.isEmpty() || sFrozenPropertyId.isEmpty() || nFrameCount <= 1 || + !nmVTKMeshLayer::isValidDisplayMode(eFrozenMeshDisplayMode) || !m_pSceneController->isPropertyTimeDependent(sFrozenPropertyId)) { return; } @@ -333,7 +336,9 @@ void nmWxPebiResultViewWidget::onExportAnimationRequested() m_pSceneController->getSnapshotBindingRevision() != nFrozenBindingRevision || m_pSceneController->getCurrentSnapshotId() != sFrozenSnapshotId || - m_pSceneController->getCurrentPropertyId() != sFrozenPropertyId) { + m_pSceneController->getCurrentPropertyId() != sFrozenPropertyId || + m_pSceneController->getMeshDisplayMode() != + eFrozenMeshDisplayMode) { QMessageBox::warning( this, tr("Export Animation"), tr("The displayed result changed before export started.")); @@ -354,6 +359,7 @@ void nmWxPebiResultViewWidget::onExportAnimationRequested() oRequest.m_pSnapshot = pFrozenSnapshot; oRequest.m_sPropertyId = sFrozenPropertyId; oRequest.m_oDisplaySettings = oDisplaySettings; + oRequest.m_eMeshDisplayMode = eFrozenMeshDisplayMode; oRequest.m_oExportSettings = oSettingsDialog.getSettings(); oRequest.m_sTargetFilePath = oSettingsDialog.getTargetFilePath(); oRequest.m_bScalarBarVisible = @@ -713,14 +719,6 @@ void nmWxPebiResultViewWidget::refreshStatusBar() m_pStatusLabel->show(); return; } - if(m_bObservedStateValid && !m_bObservedSnapshotCurrent) { - m_pStatusLabel->setStyleSheet( - "QLabel { color: #6b5200; background: #fff4bf; } "); - m_pStatusLabel->setText(tr( - "Current inputs have changed. Showing the last successful result.")); - m_pStatusLabel->show(); - return; - } if(!m_sOperationError.isEmpty()) { m_pStatusLabel->setStyleSheet( "QLabel { color: #a32626; background: #fdecec; } ");