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; } ");