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...

806 lines
26 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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();
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<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();
}