#include "nmWxPebiGridViewWidget.h" #include "nmPebiGridSceneController.h" #include "nmPebiGridViewData.h" #include "nmVTKViewCommandController.h" #include "nmWxVTKRenderContainerWidget.h" #include #include #include #include #include #include #include #include #include namespace { QString gridViewIconPath(const QString& sFileName) { const QString sApplicationDir = QCoreApplication::applicationDirPath(); return sApplicationDir.section('/', 0, -2) + "/Res/Icon/" + sFileName; } } nmWxPebiGridViewWidget::nmWxPebiGridViewWidget(QWidget* pParent) : QWidget(pParent), m_pRenderContainer(nullptr), m_pSceneController(nullptr), m_pToolBar(nullptr), m_pBoundsGridAction(nullptr), m_pGridInfoLabel(nullptr), m_pStatusLabel(nullptr), m_bHasViewData(false) { createUi(); // 控制器必须在容器之后创建,以便使用当前实例独占的 Scene 和相机。 m_pSceneController = new nmPebiGridSceneController(m_pRenderContainer); resetControls(); setControlsEnabled(false); m_pRenderContainer->showEmptyState(tr("No grid data.")); } nmWxPebiGridViewWidget::~nmWxPebiGridViewWidget() { // 控制器先解除 Mapper 输入;Scene 和 Layer 随后由容器析构统一清理。 delete m_pSceneController; m_pSceneController = nullptr; } void nmWxPebiGridViewWidget::createUi() { QVBoxLayout* pMainLayout = new QVBoxLayout(this); pMainLayout->setContentsMargins(0, 0, 0, 0); pMainLayout->setSpacing(0); // 绘图区单独使用水平布局,渲染容器伸展,竖向工具栏固定在右侧。 QWidget* pRenderArea = new QWidget(this); QHBoxLayout* pRenderLayout = new QHBoxLayout(pRenderArea); pRenderLayout->setContentsMargins(0, 0, 0, 0); pRenderLayout->setSpacing(0); // 公共容器独占 Renderer、相机和交互对象;本类只裁剪可见命令能力。 m_pRenderContainer = new nmWxVTKRenderContainerWidget(pRenderArea); m_pRenderContainer->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding); m_pRenderContainer->setViewCommandCapabilities( nmVTKViewCommandController::ViewCommand_ZoomIn | nmVTKViewCommandController::ViewCommand_ZoomOut | nmVTKViewCommandController::ViewCommand_Fit | nmVTKViewCommandController::ViewCommand_Top | nmVTKViewCommandController::ViewCommand_Front | nmVTKViewCommandController::ViewCommand_SetRotationCenter | nmVTKViewCommandController::ViewCommand_SaveImage | nmVTKViewCommandController::ViewCommand_CopyImage | nmVTKViewCommandController::ViewCommand_Print | nmVTKViewCommandController::ViewCommand_PrintPreview); m_pToolBar = new QToolBar(pRenderArea); m_pToolBar->setOrientation(Qt::Vertical); m_pToolBar->setMovable(false); m_pToolBar->setFloatable(false); m_pToolBar->setIconSize(QSize(32, 32)); m_pToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); m_pToolBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); nmVTKViewCommandController* pCommandController = m_pRenderContainer->getViewCommandController(); // 比例网格 QAction 长期持有,并同时用于工具栏和右键菜单。 m_pBoundsGridAction = new QAction( QIcon(gridViewIconPath("NmScaleGrid.png")), tr("Scale Grid"), this); m_pBoundsGridAction->setToolTip(tr("Scale Grid")); m_pBoundsGridAction->setCheckable(true); if(pCommandController != nullptr) { // 网格视图按自身工作流排序,但仍引用命令控制器持有的公共 QAction。 QAction* pZoomOutAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_ZoomOut); QAction* pFitAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_Fit); QAction* pZoomInAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_ZoomIn); QAction* pTopAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_Top); QAction* pFrontAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_Front); QAction* pSetRotationCenterAction = pCommandController->getAction( nmVTKViewCommandController:: ViewCommand_SetRotationCenter); QAction* pCopyAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_CopyImage); // 按“缩放、标准视角、显示、输出”分组,空组不会留下多余分隔线。 bool bHasPreviousGroup = false; if(pZoomOutAction != nullptr) { m_pToolBar->addAction(pZoomOutAction); bHasPreviousGroup = true; } if(pFitAction != nullptr) { m_pToolBar->addAction(pFitAction); bHasPreviousGroup = true; } if(pZoomInAction != nullptr) { m_pToolBar->addAction(pZoomInAction); bHasPreviousGroup = true; } const bool bHasViewGroup = pTopAction != nullptr || pFrontAction != nullptr || pSetRotationCenterAction != nullptr; if(bHasViewGroup && bHasPreviousGroup) { m_pToolBar->addSeparator(); } if(pTopAction != nullptr) { m_pToolBar->addAction(pTopAction); bHasPreviousGroup = true; } if(pFrontAction != nullptr) { m_pToolBar->addAction(pFrontAction); bHasPreviousGroup = true; } if(pSetRotationCenterAction != nullptr) { m_pToolBar->addAction(pSetRotationCenterAction); bHasPreviousGroup = true; } if(bHasPreviousGroup) { m_pToolBar->addSeparator(); } m_pToolBar->addAction(m_pBoundsGridAction); if(pCopyAction != nullptr) { m_pToolBar->addSeparator(); m_pToolBar->addAction(pCopyAction); } } else { // 理论上不会进入;仍保留业务动作,避免容器初始化异常时布局跳动。 m_pToolBar->addAction(m_pBoundsGridAction); } pRenderLayout->addWidget(m_pRenderContainer, 1); pRenderLayout->addWidget(m_pToolBar); pMainLayout->addWidget(pRenderArea, 1); m_pStatusLabel = new QLabel(this); m_pStatusLabel->setWordWrap(true); m_pStatusLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); m_pStatusLabel->setStyleSheet( "QLabel { color: rgb(126, 76, 0); " "background-color: rgb(255, 244, 204); " "border-top: 1px solid rgb(220, 190, 105); " "padding: 4px 8px; }"); 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); m_pGridInfoLabel = new QLabel(pControlBar); m_pGridInfoLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); pControlLayout->addWidget(m_pGridInfoLabel); pControlLayout->addStretch(1); pMainLayout->addWidget(pControlBar); connect(m_pBoundsGridAction, SIGNAL(toggled(bool)), this, SLOT(onBoundsGridToggled(bool))); connect(m_pRenderContainer, SIGNAL(contextMenuAboutToShow(QMenu*)), this, SLOT(appendContextMenuActions(QMenu*))); } bool nmWxPebiGridViewWidget::setViewData( const nmPebiGridViewData& oViewData, QString* pError) { if(m_pSceneController == nullptr || !oViewData.isValid()) { if(pError != nullptr) { *pError = tr("The grid preview data is invalid."); } return false; } // SceneController 负责事务式绑定;失败时这里按其回滚结果同步外层状态。 if(!m_pSceneController->setViewData(oViewData, pError)) { // 控制器回滚失败时同步清除外层标志,避免把空场景误当作旧成功网格。 m_bHasViewData = m_pSceneController->hasViewData(); return false; } // Builder 只接受已提交的 PEBI 网格,因此底栏无需再显示“未知类型”。 m_pGridInfoLabel->setText( tr("PEBI | %1 valid cells | XY planar grid") .arg(static_cast( oViewData.getValidCellCount()))); m_pStatusLabel->hide(); m_bHasViewData = true; setControlsEnabled(true); return true; } void nmWxPebiGridViewWidget::showLoading() { m_pStatusLabel->hide(); setControlsEnabled(false); m_pRenderContainer->showRenderingState(tr("Generating grid...")); } void nmWxPebiGridViewWidget::showFailure(const QString& sMessage) { if(m_bHasViewData) { // 失败时旧网格引用保持不变,状态条明确说明当前画面并非本次成果。 m_pRenderContainer->setRotationCenterPickingEnabled(false); m_pStatusLabel->setText(sMessage); m_pStatusLabel->show(); setControlsEnabled(true); m_pRenderContainer->showReadyState(); } else { m_pStatusLabel->hide(); resetControls(); setControlsEnabled(false); m_pRenderContainer->showErrorState(sMessage); } } void nmWxPebiGridViewWidget::clearView() { if(m_pSceneController != nullptr) { m_pSceneController->clear(); } m_bHasViewData = false; m_pStatusLabel->hide(); resetControls(); setControlsEnabled(false); m_pRenderContainer->showEmptyState(tr("No grid data.")); } bool nmWxPebiGridViewWidget::hasViewData() const { return m_bHasViewData; } void nmWxPebiGridViewWidget::resetControls() { const bool bBlocked = m_pBoundsGridAction->blockSignals(true); m_pBoundsGridAction->setChecked(false); m_pBoundsGridAction->blockSignals(bBlocked); m_pGridInfoLabel->setText( tr("PEBI | %1 valid cells | XY planar grid").arg(0)); } void nmWxPebiGridViewWidget::setControlsEnabled(bool bEnabled) { m_pBoundsGridAction->setEnabled(bEnabled); } void nmWxPebiGridViewWidget::onBoundsGridToggled(bool bVisible) { if(m_bHasViewData && m_pSceneController != nullptr) { m_pSceneController->setBoundsGridVisible(bVisible); } } void nmWxPebiGridViewWidget::appendContextMenuActions(QMenu* pMenu) { if(pMenu == nullptr) { return; } // 右键菜单与工具栏引用同一个比例网格 QAction,勾选状态不会发生分叉。 pMenu->addAction(m_pBoundsGridAction); }