#include "nmWxVTKRenderContainerWidget.h" #include "nmVTKCameraController.h" #include "nmVTKImageExporter.h" #include "nmVTKInteractionManager.h" #include "nmVTKOrientationMarkerController.h" #include "nmVTKRotationCenterMarkerLayer.h" #include "nmVTKRotationCenterPicker.h" #include "nmVTKScene.h" #include "nmVTKViewCommandController.h" #include #include #include #include #include #include #include #include #include #include #include #include #include VTK_MODULE_INIT(vtkRenderingOpenGL) VTK_MODULE_INIT(vtkRenderingFreeType) VTK_MODULE_INIT(vtkInteractionStyle) #include #include #include namespace { const char* const ROTATION_CENTER_MARKER_LAYER_ID = "nm.render.rotation_center_marker"; } nmWxVTKRenderContainerWidget::nmWxVTKRenderContainerWidget(QWidget* pParent) : QWidget(pParent), m_pMainLayout(nullptr), m_pStateStack(nullptr), m_pStatePage(nullptr), m_pStateLabel(nullptr), m_pVtkWidget(nullptr), m_pRenderer(nullptr), m_pScene(nullptr), m_pSystemScene(nullptr), m_pCameraController(nullptr), m_pInteractionManager(nullptr), m_pImageExporter(nullptr), m_pViewCommandController(nullptr), m_pOrientationMarkerController(nullptr), m_pRotationCenterPicker(nullptr), m_pRotationCenterMarkerLayer(nullptr) { Q_ASSERT(isGuiThread()); // 1. 先创建稳定布局和状态页,避免无数据时改变容器尺寸。 m_pMainLayout = new QVBoxLayout(this); m_pMainLayout->setContentsMargins(0, 0, 0, 0); m_pMainLayout->setSpacing(0); m_pStateStack = new QStackedWidget(this); m_pStatePage = new QWidget(m_pStateStack); m_pStateLabel = new QLabel(m_pStatePage); QVBoxLayout* pStateLayout = new QVBoxLayout(m_pStatePage); pStateLayout->setContentsMargins(24, 24, 24, 24); pStateLayout->addStretch(); m_pStateLabel->setAlignment(Qt::AlignCenter); m_pStateLabel->setWordWrap(true); pStateLayout->addWidget(m_pStateLabel); pStateLayout->addStretch(); // 2. QVTKWidget 由 Qt 父子关系拥有,并作为就绪状态页面。 m_pVtkWidget = new QVTKWidget(m_pStateStack); m_pStateStack->addWidget(m_pVtkWidget); m_pStateStack->addWidget(m_pStatePage); m_pMainLayout->addWidget(m_pStateStack); // 3、4. RenderWindow 属于 QVTKWidget;Renderer 由本容器智能指针独占。 vtkRenderWindow* pRenderWindow = getRenderWindow(); vtkRenderWindowInteractor* pInteractor = getInteractor(); m_pRenderer = vtkSmartPointer::New(); if(pRenderWindow != nullptr && m_pRenderer != nullptr) { pRenderWindow->AddRenderer(m_pRenderer); m_pRenderer->SetGradientBackground(true); m_pRenderer->SetBackground(0.67, 0.82, 0.94); m_pRenderer->SetBackground2(0.0, 0.5, 1.0); } // 5. 管理对象、图片服务和命令对象均只服务当前容器实例。 // 业务图层和容器内部装饰分别管理;公开业务清场不会删除系统 Marker。 m_pScene = new nmVTKScene(m_pRenderer); m_pSystemScene = new nmVTKScene(m_pRenderer); m_pCameraController = new nmVTKCameraController(m_pRenderer); m_pInteractionManager = new nmVTKInteractionManager( m_pCameraController); nmVTKRotationCenterMarkerLayer* pRotationCenterMarkerLayer = new nmVTKRotationCenterMarkerLayer( ROTATION_CENTER_MARKER_LAYER_ID); if(m_pSystemScene != nullptr && m_pSystemScene->addLayer(pRotationCenterMarkerLayer)) { if(pRotationCenterMarkerLayer->bindRenderer(m_pRenderer)) { m_pRotationCenterMarkerLayer = pRotationCenterMarkerLayer; } else { m_pSystemScene->removeLayer(ROTATION_CENTER_MARKER_LAYER_ID); } } else { delete pRotationCenterMarkerLayer; } m_pRotationCenterPicker = new nmVTKRotationCenterPicker(); m_pImageExporter = new nmVTKImageExporter(); m_pImageExporter->setRenderWindow(pRenderWindow); m_pViewCommandController = new nmVTKViewCommandController(this, this); m_pViewCommandController->setCapabilities( nmVTKViewCommandController::ViewCommand_ZoomIn | nmVTKViewCommandController::ViewCommand_ZoomOut | nmVTKViewCommandController::ViewCommand_Fit | nmVTKViewCommandController::ViewCommand_Top | nmVTKViewCommandController::ViewCommand_Front | nmVTKViewCommandController::ViewCommand_Right | nmVTKViewCommandController::ViewCommand_Isometric | nmVTKViewCommandController::ViewCommand_ResetRotationCenter | nmVTKViewCommandController::ViewCommand_SaveImage | nmVTKViewCommandController::ViewCommand_CopyImage | nmVTKViewCommandController::ViewCommand_Print | nmVTKViewCommandController::ViewCommand_PrintPreview); // 命令控制器负责菜单顺序,容器继续向业务层转发同一个扩展时机。 connect(m_pViewCommandController, SIGNAL(contextMenuAboutToShow(QMenu*)), this, SIGNAL(contextMenuAboutToShow(QMenu*))); connect(this, SIGNAL(rotationCenterPickingChanged(bool)), m_pViewCommandController, SLOT(setRotationCenterPicking(bool))); const bool bInteractionReady = m_pInteractionManager->bindInteractor( pInteractor, m_pRenderer); updateRotationCenterCommandAvailability(); // 方向轴是独立辅助 Renderer;绑定失败只隐藏方向轴,不影响主视图初始化。 m_pOrientationMarkerController = new nmVTKOrientationMarkerController(); m_pOrientationMarkerController->bindInteractor( pInteractor, m_pRenderer); // 6. 只接管右键菜单,左键、中键和滚轮继续交给 VTK。 m_pVtkWidget->installEventFilter(this); // 7. 基础 VTK 对象完整时进入就绪页,否则保留明确错误状态。 if(pRenderWindow != nullptr && m_pRenderer != nullptr && bInteractionReady) { showReadyState(); } else { showErrorState(tr("Unable to initialize the render view.")); } } nmWxVTKRenderContainerWidget::~nmWxVTKRenderContainerWidget() { Q_ASSERT(isGuiThread()); // 1. 先阻止新的右键菜单回调进入销毁中的对象。 if(m_pVtkWidget != nullptr) { m_pVtkWidget->removeEventFilter(this); } // 2. 先销毁 QAction,避免后续服务拆除期间再触发容器槽函数。 if(m_pViewCommandController != nullptr) { delete m_pViewCommandController; m_pViewCommandController = nullptr; } // 3. 拾取器先恢复光标;场景标记必须在 Camera 和 Renderer 前解除观察关系。 if(m_pRotationCenterPicker != nullptr) { m_pRotationCenterPicker->reset(); delete m_pRotationCenterPicker; m_pRotationCenterPicker = nullptr; } nmVTKRotationCenterMarkerLayer* pRotationCenterMarkerLayer = getRotationCenterMarkerLayer(); if(pRotationCenterMarkerLayer != nullptr) { pRotationCenterMarkerLayer->unbindRenderer(); } // 4. MarkerWidget 持有 Interactor 和辅助 Renderer 引用,必须优先解绑。 if(m_pOrientationMarkerController != nullptr) { m_pOrientationMarkerController->unbindInteractor(); delete m_pOrientationMarkerController; m_pOrientationMarkerController = nullptr; } // 5. 图片服务不拥有 RenderWindow,先解除引用再销毁服务。 if(m_pImageExporter != nullptr) { m_pImageExporter->setRenderWindow(nullptr); delete m_pImageExporter; m_pImageExporter = nullptr; } // 6. Interactor 必须先释放对默认 Style 的 VTK 引用。 if(m_pInteractionManager != nullptr) { m_pInteractionManager->unbindInteractor(); delete m_pInteractionManager; m_pInteractionManager = nullptr; } // 7. 两个 Scene 均在 Camera 和 Renderer 前清空;系统装饰先解除所有权。 if(m_pSystemScene != nullptr) { m_pRotationCenterMarkerLayer = nullptr; m_pSystemScene->clear(); delete m_pSystemScene; m_pSystemScene = nullptr; } if(m_pScene != nullptr) { m_pScene->clear(); delete m_pScene; m_pScene = nullptr; } // 8. 相机控制器仅解除 Renderer 非所有权引用。 if(m_pCameraController != nullptr) { m_pCameraController->setRenderer(nullptr); delete m_pCameraController; m_pCameraController = nullptr; } // 9. 最后从 RenderWindow 移除并释放当前实例 Renderer。 vtkRenderWindow* pRenderWindow = getRenderWindow(); if(pRenderWindow != nullptr && m_pRenderer != nullptr) { pRenderWindow->RemoveRenderer(m_pRenderer); } m_pRenderer = nullptr; // QVTKWidget、状态页和布局随后由 Qt 父子关系自动销毁。 } QVTKWidget* nmWxVTKRenderContainerWidget::getVtkWidget() const { return m_pVtkWidget; } vtkRenderer* nmWxVTKRenderContainerWidget::getRenderer() const { return m_pRenderer; } vtkRenderWindow* nmWxVTKRenderContainerWidget::getRenderWindow() const { return m_pVtkWidget != nullptr ? m_pVtkWidget->GetRenderWindow() : nullptr; } vtkRenderWindowInteractor* nmWxVTKRenderContainerWidget::getInteractor() const { vtkRenderWindow* pRenderWindow = getRenderWindow(); return pRenderWindow != nullptr ? pRenderWindow->GetInteractor() : nullptr; } nmVTKScene* nmWxVTKRenderContainerWidget::getScene() const { return m_pScene; } nmVTKViewCommandController* nmWxVTKRenderContainerWidget::getViewCommandController() const { return m_pViewCommandController; } nmVTKOrientationMarkerController* nmWxVTKRenderContainerWidget::getOrientationMarkerController() const { return m_pOrientationMarkerController; } void nmWxVTKRenderContainerWidget::setViewCommandCapabilities( nmVTKViewCommandController::ViewCommands eCommands) { if(!eCommands.testFlag( nmVTKViewCommandController::ViewCommand_SetRotationCenter)) { // 能力移除前先结束模式,QAction 仅在随后同步隐藏和禁用。 cancelRotationCenterPicking(); } if(m_pViewCommandController != nullptr) { m_pViewCommandController->setCapabilities(eCommands); } updateRotationCenterMarkerVisibility(); } void nmWxVTKRenderContainerWidget::setRotationCenterPickTarget( vtkProp* pTarget) { Q_ASSERT(isGuiThread()); if(!isGuiThread() || m_pRotationCenterPicker == nullptr) { return; } if(pTarget == nullptr || m_pRotationCenterPicker->getPickTarget() != pTarget) { cancelRotationCenterPicking(); m_pRotationCenterPicker->setPickTarget(pTarget); } if(pTarget == nullptr) { hideRotationCenterMarker(); } updateRotationCenterCommandAvailability(); updateRotationCenterMarkerVisibility(); } bool nmWxVTKRenderContainerWidget::isRotationCenterPicking() const { return m_pRotationCenterPicker != nullptr && m_pRotationCenterPicker->isActive(); } void nmWxVTKRenderContainerWidget::hideRotationCenterMarker() { Q_ASSERT(isGuiThread()); nmVTKRotationCenterMarkerLayer* pMarkerLayer = getRotationCenterMarkerLayer(); if(isGuiThread() && pMarkerLayer != nullptr) { pMarkerLayer->setMarkerVisible(false); } } bool nmWxVTKRenderContainerWidget::installInteractionStyle( vtkInteractorStyle* pStyle) { Q_ASSERT(isGuiThread()); cancelRotationCenterPicking(); const bool bSucceeded = isGuiThread() && m_pInteractionManager != nullptr && m_pInteractionManager->installInteractionStyle(pStyle); updateRotationCenterCommandAvailability(); return bSucceeded; } bool nmWxVTKRenderContainerWidget::restoreDefaultInteraction() { Q_ASSERT(isGuiThread()); cancelRotationCenterPicking(); const bool bSucceeded = isGuiThread() && m_pInteractionManager != nullptr && m_pInteractionManager->restoreDefaultInteraction(); updateRotationCenterCommandAvailability(); return bSucceeded; } void nmWxVTKRenderContainerWidget::clearInteractionStyle() { Q_ASSERT(isGuiThread()); cancelRotationCenterPicking(); if(isGuiThread() && m_pInteractionManager != nullptr) { m_pInteractionManager->clearInteractionStyle(); } updateRotationCenterCommandAvailability(); } void nmWxVTKRenderContainerWidget::renderView() { Q_ASSERT(isGuiThread()); vtkRenderWindow* pRenderWindow = getRenderWindow(); // Scene、Layer 和相机均不隐式渲染,批量更新最终汇聚到此入口。 if(isGuiThread() && pRenderWindow != nullptr) { synchronizeRotationCenterMarker(); pRenderWindow->Render(); } } bool nmWxVTKRenderContainerWidget::fitTopViewWithoutRender() { Q_ASSERT(isGuiThread()); cancelRotationCenterPicking(); // 相机控制器本身不触发 Render,便于业务图层完成整批更新后只渲染一次。 return isGuiThread() && m_pCameraController != nullptr && m_pCameraController->setTopView(); } bool nmWxVTKRenderContainerWidget::resetRotationCenterWithoutRender() { Q_ASSERT(isGuiThread()); cancelRotationCenterPicking(); if(!isGuiThread() || m_pCameraController == nullptr || !m_pCameraController->resetRotationCenter()) { return false; } double dCenter[3]; if(!m_pCameraController->getRotationCenter(dCenter)) { return false; } return applyRotationCenter( dCenter[0], dCenter[1], dCenter[2], true); } void nmWxVTKRenderContainerWidget::showReadyState() { Q_ASSERT(isGuiThread()); if(!isGuiThread()) { return; } // 状态页与 VTK 页尺寸稳定,切换页面不会重建 RenderWindow 或 Renderer。 m_pStateStack->setCurrentWidget(m_pVtkWidget); if(m_pViewCommandController != nullptr) { m_pViewCommandController->setViewAvailable(true); } updateRotationCenterMarkerVisibility(); if(m_pOrientationMarkerController != nullptr) { m_pOrientationMarkerController->setViewAvailable(true); } renderView(); } void nmWxVTKRenderContainerWidget::showEmptyState(const QString& sMessage) { showMessageState(ViewState_Empty, sMessage); } void nmWxVTKRenderContainerWidget::showErrorState(const QString& sMessage) { showMessageState(ViewState_Error, sMessage); } void nmWxVTKRenderContainerWidget::showRenderingState( const QString& sMessage) { showMessageState(ViewState_Rendering, sMessage); } void nmWxVTKRenderContainerWidget::zoomIn() { cancelRotationCenterPicking(); if(m_pCameraController != nullptr && m_pCameraController->zoomIn()) { renderView(); } } void nmWxVTKRenderContainerWidget::zoomOut() { cancelRotationCenterPicking(); if(m_pCameraController != nullptr && m_pCameraController->zoomOut()) { renderView(); } } void nmWxVTKRenderContainerWidget::fitView() { cancelRotationCenterPicking(); if(m_pCameraController != nullptr && m_pCameraController->fitView()) { renderView(); } } void nmWxVTKRenderContainerWidget::resetCamera() { cancelRotationCenterPicking(); // 控制器用返回值表示相机是否确实改变,成功时只提交一次渲染。 if(m_pCameraController != nullptr && m_pCameraController->resetCamera()) { renderView(); } } void nmWxVTKRenderContainerWidget::setTopView() { cancelRotationCenterPicking(); if(m_pCameraController != nullptr && m_pCameraController->setTopView()) { renderView(); } } void nmWxVTKRenderContainerWidget::setFrontView() { cancelRotationCenterPicking(); if(m_pCameraController != nullptr && m_pCameraController->setFrontView()) { renderView(); } } void nmWxVTKRenderContainerWidget::setRightView() { cancelRotationCenterPicking(); if(m_pCameraController != nullptr && m_pCameraController->setRightView()) { renderView(); } } void nmWxVTKRenderContainerWidget::setIsometricView() { cancelRotationCenterPicking(); if(m_pCameraController != nullptr && m_pCameraController->setIsometricView()) { renderView(); } } void nmWxVTKRenderContainerWidget::setRotationCenter( double dX, double dY, double dZ) { cancelRotationCenterPicking(); if(applyRotationCenter(dX, dY, dZ, true)) { renderView(); } } void nmWxVTKRenderContainerWidget::resetRotationCenter() { if(resetRotationCenterWithoutRender()) { renderView(); } } bool nmWxVTKRenderContainerWidget::applyRotationCenter( double dX, double dY, double dZ, bool bShowMarker) { Q_ASSERT(isGuiThread()); if(!isGuiThread() || m_pCameraController == nullptr || !m_pCameraController->setRotationCenter(dX, dY, dZ)) { return false; } // CameraController 是独立枢轴的唯一逻辑状态源;Marker 只保存显示副本。 nmVTKRotationCenterMarkerLayer* pMarkerLayer = getRotationCenterMarkerLayer(); if(pMarkerLayer != nullptr) { if(!pMarkerLayer->setWorldCenter(dX, dY, dZ)) { return false; } if(bShowMarker) { pMarkerLayer->setMarkerVisible(true); } } synchronizeRotationCenterMarker(); return true; } void nmWxVTKRenderContainerWidget::cancelRotationCenterPicking() { Q_ASSERT(isGuiThread()); if(!isGuiThread()) { return; } if(m_pRotationCenterPicker != nullptr) { // reset 同时清除待消费 Release,并按进入模式前状态恢复光标。 m_pRotationCenterPicker->reset(); } emit rotationCenterPickingChanged(false); } bool nmWxVTKRenderContainerWidget::handleRotationCenterPickEvent( QEvent* pEvent) { if(m_pRotationCenterPicker == nullptr || pEvent == nullptr) { return false; } const nmVTKRotationCenterPicker::PickEventResult oResult = m_pRotationCenterPicker->handleEvent(pEvent); if(oResult.eOutcome == nmVTKRotationCenterPicker::PickOutcome_Hit) { double dPosition[3]; if(m_pRotationCenterPicker->getPickedPosition(dPosition) && applyRotationCenter(dPosition[0], dPosition[1], dPosition[2], true)) { // 独立旋转中心和 Marker 已作为一批提交,只渲染一次。 renderView(); } } if(oResult.eOutcome != nmVTKRotationCenterPicker::PickOutcome_None) { emit rotationCenterPickingChanged(false); } return oResult.bConsumed; } void nmWxVTKRenderContainerWidget::synchronizeRotationCenterMarker() { nmVTKRotationCenterMarkerLayer* pMarkerLayer = getRotationCenterMarkerLayer(); if(pMarkerLayer != nullptr && pMarkerLayer->isMarkerVisible()) { // synchronize 只更新世界几何,绝不从图层内部触发 Render。 pMarkerLayer->synchronize(); } } void nmWxVTKRenderContainerWidget::updateRotationCenterMarkerVisibility() { nmVTKRotationCenterMarkerLayer* pMarkerLayer = getRotationCenterMarkerLayer(); if(pMarkerLayer == nullptr) { return; } const bool bVisible = m_pViewCommandController != nullptr && m_pViewCommandController->isViewAvailable() && m_pViewCommandController->getCapabilities().testFlag( nmVTKViewCommandController::ViewCommand_SetRotationCenter) && m_pRotationCenterPicker != nullptr && m_pRotationCenterPicker->hasPickTarget() && pMarkerLayer->hasWorldCenter(); pMarkerLayer->setMarkerVisible(bVisible); } void nmWxVTKRenderContainerWidget::updateRotationCenterCommandAvailability() { if(m_pViewCommandController == nullptr) { return; } const bool bAvailable = m_pRotationCenterPicker != nullptr && m_pRotationCenterPicker->hasPickTarget() && getRotationCenterMarkerLayer() != nullptr && m_pInteractionManager != nullptr && m_pInteractionManager->isDefaultInteractionActive(); m_pViewCommandController->setRotationCenterPickingAvailable( bAvailable); } nmVTKRotationCenterMarkerLayer* nmWxVTKRenderContainerWidget::getRotationCenterMarkerLayer() const { if(m_pSystemScene == nullptr || m_pRotationCenterMarkerLayer == nullptr || m_pSystemScene->getLayer(ROTATION_CENTER_MARKER_LAYER_ID) != m_pRotationCenterMarkerLayer) { return nullptr; } return m_pRotationCenterMarkerLayer; } void nmWxVTKRenderContainerWidget::setRotationCenterPickingEnabled( bool bEnabled) { Q_ASSERT(isGuiThread()); if(!isGuiThread()) { return; } if(!bEnabled) { cancelRotationCenterPicking(); return; } const bool bCommandAvailable = m_pViewCommandController != nullptr && m_pViewCommandController->isViewAvailable() && m_pViewCommandController->getCapabilities().testFlag( nmVTKViewCommandController::ViewCommand_SetRotationCenter); const bool bCanStart = bCommandAvailable && m_pRotationCenterPicker != nullptr && m_pRotationCenterPicker->getPickTarget() != nullptr && getRotationCenterMarkerLayer() != nullptr && m_pInteractionManager != nullptr && m_pInteractionManager->isDefaultInteractionActive(); if(!bCanStart || !m_pRotationCenterPicker->start(m_pVtkWidget, m_pRenderer)) { if(m_pRotationCenterPicker != nullptr) { m_pRotationCenterPicker->reset(); } updateRotationCenterCommandAvailability(); emit rotationCenterPickingChanged(false); return; } emit rotationCenterPickingChanged(true); } bool nmWxVTKRenderContainerWidget::eventFilter( QObject* pObject, QEvent* pEvent) { if(pObject != m_pVtkWidget || pEvent == nullptr) { return QWidget::eventFilter(pObject, pEvent); } // 一次性拾取优先于 VTK 相机交互和右键菜单,并继续吞掉对应 Release。 if(m_pRotationCenterPicker != nullptr && (m_pRotationCenterPicker->isActive() || m_pRotationCenterPicker->hasPendingRelease()) && handleRotationCenterPickEvent(pEvent)) { return true; } if(pEvent->type() == QEvent::FocusOut) { // 延后到本轮焦点切换结束,避免点击工具按钮启动模式时被旧 FocusOut 取消。 QTimer::singleShot( 0, this, SLOT(cancelRotationCenterPickingAfterFocusOut())); } if(pEvent->type() == QEvent::MouseButtonPress) { QMouseEvent* pMouseEvent = static_cast(pEvent); if(pMouseEvent->button() == Qt::RightButton) { // 菜单引用命令控制器长期持有的 QAction,不在每次右键时重复创建。 QMenu oMenu(this); if(m_pViewCommandController != nullptr) { m_pViewCommandController->populateContextMenu(&oMenu); } if(!oMenu.isEmpty()) { oMenu.exec(pMouseEvent->globalPos()); } // 返回 true 只消费右键按下,避免 VTK 同时启动一次交互动作。 return true; } return false; } return QWidget::eventFilter(pObject, pEvent); } void nmWxVTKRenderContainerWidget::cancelRotationCenterPickingAfterFocusOut() { if(m_pVtkWidget != nullptr && !m_pVtkWidget->hasFocus() && m_pRotationCenterPicker != nullptr && (m_pRotationCenterPicker->isActive() || m_pRotationCenterPicker->hasPendingRelease())) { cancelRotationCenterPicking(); } } void nmWxVTKRenderContainerWidget::saveImage() { if(m_pImageExporter == nullptr) { return; } synchronizeRotationCenterMarker(); // 实际编码格式由 QImage 按调用方选择的扩展名决定。 const QString sFileName = QFileDialog::getSaveFileName( this, tr("Save Image"), QString(), tr("PNG Image (*.png);;JPEG Image (*.jpg);;BMP Image (*.bmp)")); if(!sFileName.isEmpty() && !m_pImageExporter->saveImage(sFileName)) { QMessageBox::warning(this, tr("Save Failed"), tr("Unable to save the image.")); } } void nmWxVTKRenderContainerWidget::copyImage() { synchronizeRotationCenterMarker(); if(m_pImageExporter != nullptr && !m_pImageExporter->copyImageToClipboard()) { QMessageBox::warning(this, tr("Copy Failed"), tr("Unable to capture the image.")); } } void nmWxVTKRenderContainerWidget::printImage() { if(m_pImageExporter != nullptr) { synchronizeRotationCenterMarker(); m_pImageExporter->printImage(this); } } void nmWxVTKRenderContainerWidget::printPreview() { if(m_pImageExporter != nullptr) { synchronizeRotationCenterMarker(); m_pImageExporter->printPreview(this); } } void nmWxVTKRenderContainerWidget::showMessageState( ViewState eState, const QString& sMessage) { Q_ASSERT(isGuiThread()); if(!isGuiThread() || m_pStateStack == nullptr || m_pStatePage == nullptr || m_pStateLabel == nullptr) { return; } // 所有非就绪状态共用同一页面,仅错误状态使用警示颜色。 if(eState == ViewState_Error) { m_pStateLabel->setStyleSheet("QLabel { color: #a32626; }"); } else { m_pStateLabel->setStyleSheet("QLabel { color: #4a4a4a; }"); } m_pStateLabel->setText(sMessage); m_pStateStack->setCurrentWidget(m_pStatePage); // 非就绪状态先结束临时工具并清除手工标记,再禁用和取消 QAction。 cancelRotationCenterPicking(); hideRotationCenterMarker(); if(m_pViewCommandController != nullptr) { m_pViewCommandController->setViewAvailable(false); } if(m_pOrientationMarkerController != nullptr) { m_pOrientationMarkerController->setViewAvailable(false); } } bool nmWxVTKRenderContainerWidget::isGuiThread() const { QCoreApplication* pApplication = QCoreApplication::instance(); return pApplication == nullptr || pApplication->thread() == QThread::currentThread(); }