#include "nmWxPebiGridViewWidget.h" #include "nmPebiGridSceneController.h" #include "nmPebiGridViewData.h" #include "nmVTKViewCommandController.h" #include "nmWxVTKRenderContainerWidget.h" #include #include #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(NULL), m_pSceneController(NULL), m_pToolBar(NULL), m_pGridLinesAction(NULL), m_pScalarBarAction(NULL), m_pBoundsGridAction(NULL), m_pPropertyComboBox(NULL), m_pGridInfoLabel(NULL), m_pStatusLabel(NULL), 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 = NULL; } 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); 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_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(20, 20)); m_pToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); m_pToolBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); nmVTKViewCommandController* pCommandController = m_pRenderContainer->getViewCommandController(); // 三个业务 QAction 与公共命令一样长期持有,并同时用于工具栏和右键菜单。 m_pGridLinesAction = new QAction( QIcon(gridViewIconPath("Grids.png")), tr("Show Grid Lines"), this); m_pGridLinesAction->setToolTip(tr("Show Grid Lines")); m_pGridLinesAction->setCheckable(true); m_pScalarBarAction = new QAction( QIcon(gridViewIconPath("Eyes.png")), tr("Show Scalar Bar"), this); m_pScalarBarAction->setToolTip(tr("Show Scalar Bar")); m_pScalarBarAction->setCheckable(true); m_pBoundsGridAction = new QAction( QIcon(gridViewIconPath("CalGrid.png")), tr("Coordinate Grid"), this); m_pBoundsGridAction->setToolTip(tr("Coordinate Grid")); m_pBoundsGridAction->setCheckable(true); if(pCommandController != NULL) { // 网格视图按自身工作流排序,但仍引用命令控制器持有的公共 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* pCopyAction = pCommandController->getAction( nmVTKViewCommandController::ViewCommand_CopyImage); if(pZoomOutAction != NULL) { m_pToolBar->addAction(pZoomOutAction); } if(pFitAction != NULL) { m_pToolBar->addAction(pFitAction); } if(pZoomInAction != NULL) { m_pToolBar->addAction(pZoomInAction); } if(pTopAction != NULL) { m_pToolBar->addAction(pTopAction); } m_pToolBar->addSeparator(); m_pToolBar->addAction(m_pGridLinesAction); m_pToolBar->addAction(m_pScalarBarAction); m_pToolBar->addAction(m_pBoundsGridAction); m_pToolBar->addSeparator(); if(pCopyAction != NULL) { m_pToolBar->addAction(pCopyAction); } } else { // 理论上不会进入;仍保留业务动作,避免容器初始化异常时布局跳动。 m_pToolBar->addAction(m_pGridLinesAction); m_pToolBar->addAction(m_pScalarBarAction); 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); QLabel* pPropertyLabel = new QLabel(tr("Property:"), pControlBar); m_pPropertyComboBox = new QComboBox(pControlBar); m_pPropertyComboBox->setMinimumContentsLength(9); pControlLayout->addWidget(pPropertyLabel); pControlLayout->addWidget(m_pPropertyComboBox); pControlLayout->addStretch(1); m_pGridInfoLabel = new QLabel(pControlBar); m_pGridInfoLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); pControlLayout->addWidget(m_pGridInfoLabel); pMainLayout->addWidget(pControlBar); connect(m_pPropertyComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onPropertyChanged(int))); connect(m_pGridLinesAction, SIGNAL(toggled(bool)), this, SLOT(onGridLinesToggled(bool))); connect(m_pScalarBarAction, SIGNAL(toggled(bool)), this, SLOT(onScalarBarToggled(bool))); 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 == NULL || !oViewData.isValid()) { if(pError != NULL) { *pError = tr("The grid preview data is invalid."); } return false; } if(!m_pSceneController->setViewData(oViewData, pError)) { // 控制器回滚失败时同步清除外层标志,避免把空场景误当作旧成功网格。 m_bHasViewData = m_pSceneController->hasViewData(); return false; } // 属性列表只登记 Builder 已确认与 CellData 单元数一致的 Material。 const bool bOldBlockState = m_pPropertyComboBox->blockSignals(true); m_pPropertyComboBox->clear(); m_pPropertyComboBox->addItem(tr("No property"), QString()); if(oViewData.hasMaterial()) { m_pPropertyComboBox->addItem(tr("Material"), QString("Material")); } const QString sActiveProperty = m_pSceneController->getProperty(); for(int nIndex = 0; nIndex < m_pPropertyComboBox->count(); ++nIndex) { if(m_pPropertyComboBox->itemData(nIndex).toString() == sActiveProperty) { m_pPropertyComboBox->setCurrentIndex(nIndex); break; } } m_pPropertyComboBox->blockSignals(bOldBlockState); m_pGridInfoLabel->setText( tr("%1 cells | %2") .arg(static_cast( oViewData.getValidCellCount())) .arg(gridTypeText(static_cast(oViewData.getGridType())))); m_pStatusLabel->hide(); m_bHasViewData = true; setControlsEnabled(true); updateScalarBarActionState(); 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_pStatusLabel->setText(sMessage); m_pStatusLabel->show(); setControlsEnabled(true); updateScalarBarActionState(); m_pRenderContainer->showReadyState(); } else { m_pStatusLabel->hide(); setControlsEnabled(false); m_pRenderContainer->showErrorState(sMessage); } } void nmWxPebiGridViewWidget::clearView() { if(m_pSceneController != NULL) { 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() { bool bBlocked = m_pPropertyComboBox->blockSignals(true); m_pPropertyComboBox->clear(); m_pPropertyComboBox->addItem(tr("No property"), QString()); m_pPropertyComboBox->blockSignals(bBlocked); bBlocked = m_pGridLinesAction->blockSignals(true); m_pGridLinesAction->setChecked(true); m_pGridLinesAction->blockSignals(bBlocked); bBlocked = m_pScalarBarAction->blockSignals(true); m_pScalarBarAction->setChecked(true); m_pScalarBarAction->blockSignals(bBlocked); bBlocked = m_pBoundsGridAction->blockSignals(true); m_pBoundsGridAction->setChecked(false); m_pBoundsGridAction->blockSignals(bBlocked); m_pGridInfoLabel->setText(tr("0 cells | Unknown")); } void nmWxPebiGridViewWidget::setControlsEnabled(bool bEnabled) { m_pPropertyComboBox->setEnabled(bEnabled); m_pGridLinesAction->setEnabled(bEnabled); m_pBoundsGridAction->setEnabled(bEnabled); m_pScalarBarAction->setEnabled( bEnabled && m_pSceneController != NULL && !m_pSceneController->getProperty().isEmpty()); } void nmWxPebiGridViewWidget::updateScalarBarActionState() { if(m_pSceneController == NULL) { return; } const bool bBlocked = m_pScalarBarAction->blockSignals(true); m_pScalarBarAction->setChecked( m_pSceneController->isScalarBarVisible()); m_pScalarBarAction->blockSignals(bBlocked); m_pScalarBarAction->setEnabled( m_bHasViewData && !m_pSceneController->getProperty().isEmpty()); } QString nmWxPebiGridViewWidget::gridTypeText(int nGridType) const { return nGridType == static_cast(NM_Grid_PEBI) ? QString("PEBI") : tr("Unknown"); } void nmWxPebiGridViewWidget::onPropertyChanged(int nIndex) { if(!m_bHasViewData || nIndex < 0 || m_pSceneController == NULL) { return; } const QString sPropertyName = m_pPropertyComboBox->itemData(nIndex).toString(); if(!m_pSceneController->setProperty(sPropertyName)) { const bool bBlocked = m_pPropertyComboBox->blockSignals(true); m_pPropertyComboBox->setCurrentIndex(0); m_pPropertyComboBox->blockSignals(bBlocked); } updateScalarBarActionState(); } void nmWxPebiGridViewWidget::onGridLinesToggled(bool bVisible) { if(m_bHasViewData && m_pSceneController != NULL) { m_pSceneController->setGridLinesVisible(bVisible); } } void nmWxPebiGridViewWidget::onScalarBarToggled(bool bVisible) { if(m_bHasViewData && m_pSceneController != NULL) { m_pSceneController->setScalarBarVisible(bVisible); } } void nmWxPebiGridViewWidget::onBoundsGridToggled(bool bVisible) { if(m_bHasViewData && m_pSceneController != NULL) { m_pSceneController->setBoundsGridVisible(bVisible); } } void nmWxPebiGridViewWidget::appendContextMenuActions(QMenu* pMenu) { if(pMenu == NULL) { return; } // 右键菜单与工具栏引用同三个业务 QAction,勾选状态不会发生分叉。 pMenu->addAction(m_pGridLinesAction); pMenu->addAction(m_pScalarBarAction); pMenu->addAction(m_pBoundsGridAction); }