You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmRender/nmWxVTKRenderContainerWidge...

827 lines
27 KiB
C++

#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 <QCoreApplication>
#include <QEvent>
#include <QFileDialog>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
#include <QMouseEvent>
#include <QStackedWidget>
#include <QThread>
#include <QTimer>
#include <QVBoxLayout>
#include <QVTKWidget.h>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL)
VTK_MODULE_INIT(vtkRenderingFreeType)
VTK_MODULE_INIT(vtkInteractionStyle)
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
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 属于 QVTKWidgetRenderer 由本容器智能指针独占。
vtkRenderWindow* pRenderWindow = getRenderWindow();
vtkRenderWindowInteractor* pInteractor = getInteractor();
m_pRenderer = vtkSmartPointer<vtkRenderer>::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();
return resetRotationCenterStateWithoutRender();
}
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()) {
resetRotationCenterStateWithoutRender();
renderView();
}
}
void nmWxVTKRenderContainerWidget::setTopView()
{
cancelRotationCenterPicking();
if(m_pCameraController != nullptr && m_pCameraController->setTopView()) {
resetRotationCenterStateWithoutRender();
renderView();
}
}
void nmWxVTKRenderContainerWidget::setFrontView()
{
cancelRotationCenterPicking();
if(m_pCameraController != nullptr && m_pCameraController->setFrontView()) {
resetRotationCenterStateWithoutRender();
renderView();
}
}
void nmWxVTKRenderContainerWidget::setRightView()
{
cancelRotationCenterPicking();
if(m_pCameraController != nullptr && m_pCameraController->setRightView()) {
resetRotationCenterStateWithoutRender();
renderView();
}
}
void nmWxVTKRenderContainerWidget::setIsometricView()
{
cancelRotationCenterPicking();
if(m_pCameraController != nullptr &&
m_pCameraController->setIsometricView()) {
resetRotationCenterStateWithoutRender();
renderView();
}
}
void nmWxVTKRenderContainerWidget::setRotationCenter(
double dX,
double dY,
double dZ)
{
cancelRotationCenterPicking();
if(applyRotationCenter(dX, dY, dZ, true)) {
renderView();
}
}
void nmWxVTKRenderContainerWidget::resetRotationCenter()
{
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;
}
bool nmWxVTKRenderContainerWidget::resetRotationCenterStateWithoutRender()
{
Q_ASSERT(isGuiThread());
if(!isGuiThread() || m_pCameraController == nullptr ||
!m_pCameraController->resetRotationCenter()) {
hideRotationCenterMarker();
return false;
}
double dCenter[3];
nmVTKRotationCenterMarkerLayer* pMarkerLayer =
getRotationCenterMarkerLayer();
if(!m_pCameraController->getRotationCenter(dCenter) ||
pMarkerLayer == nullptr ||
!pMarkerLayer->setWorldCenter(
dCenter[0], dCenter[1], dCenter[2])) {
// 逻辑中心与显示副本无法确认一致时,不保留陈旧标记。
hideRotationCenterMarker();
return true;
}
// 由 Ready、能力和 Pick Target 共同决定显隐,不在中心事务中强制显示。
updateRotationCenterMarkerVisibility();
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<QMouseEvent*>(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();
}