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

453 lines
14 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 "nmVTKScene.h"
#include "nmVTKViewCommandController.h"
#include <QCoreApplication>
#include <QEvent>
#include <QFileDialog>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
#include <QMouseEvent>
#include <QStackedWidget>
#include <QThread>
#include <QVBoxLayout>
#include <QVTKWidget.h>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL)
VTK_MODULE_INIT(vtkInteractionStyle)
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
nmWxVTKRenderContainerWidget::nmWxVTKRenderContainerWidget(QWidget* pParent)
: QWidget(pParent),
m_pMainLayout(NULL),
m_pStateStack(NULL),
m_pStatePage(NULL),
m_pStateLabel(NULL),
m_pVtkWidget(NULL),
m_pRenderer(NULL),
m_pScene(NULL),
m_pCameraController(NULL),
m_pInteractionManager(NULL),
m_pImageExporter(NULL),
m_pViewCommandController(NULL)
{
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 != NULL && m_pRenderer != NULL) {
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. 管理对象、图片服务和命令对象均只服务当前容器实例。
m_pScene = new nmVTKScene(m_pRenderer);
m_pCameraController = new nmVTKCameraController(m_pRenderer);
m_pInteractionManager = new nmVTKInteractionManager();
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*)));
const bool bInteractionReady =
m_pInteractionManager->bindInteractor(pInteractor);
// 6. 只接管右键菜单,左键、中键和滚轮继续交给 VTK。
m_pVtkWidget->installEventFilter(this);
// 7. 基础 VTK 对象完整时进入就绪页,否则保留明确错误状态。
if(pRenderWindow != NULL && m_pRenderer != NULL && bInteractionReady) {
showReadyState();
} else {
showErrorState(tr("Unable to initialize the render view."));
}
}
nmWxVTKRenderContainerWidget::~nmWxVTKRenderContainerWidget()
{
Q_ASSERT(isGuiThread());
// 1. 先阻止新的右键菜单回调进入销毁中的对象。
if(m_pVtkWidget != NULL) {
m_pVtkWidget->removeEventFilter(this);
}
// 先销毁 QAction避免后续服务拆除期间再触发容器槽函数。
if(m_pViewCommandController != NULL) {
delete m_pViewCommandController;
m_pViewCommandController = NULL;
}
// 图片服务不拥有 RenderWindow先解除引用再销毁服务。
if(m_pImageExporter != NULL) {
m_pImageExporter->setRenderWindow(NULL);
delete m_pImageExporter;
m_pImageExporter = NULL;
}
// 2. Interactor 必须先释放对默认 Style 的 VTK 引用。
if(m_pInteractionManager != NULL) {
m_pInteractionManager->unbindInteractor();
delete m_pInteractionManager;
m_pInteractionManager = NULL;
}
// 3. Scene 在 Renderer 移除前清空所有 Layer 和 Prop。
if(m_pScene != NULL) {
m_pScene->clear();
delete m_pScene;
m_pScene = NULL;
}
// 4. 相机控制器仅解除 Renderer 非所有权引用。
if(m_pCameraController != NULL) {
m_pCameraController->setRenderer(NULL);
delete m_pCameraController;
m_pCameraController = NULL;
}
// 5、6. 最后从 RenderWindow 移除并释放当前实例 Renderer。
vtkRenderWindow* pRenderWindow = getRenderWindow();
if(pRenderWindow != NULL && m_pRenderer != NULL) {
pRenderWindow->RemoveRenderer(m_pRenderer);
}
m_pRenderer = NULL;
// 7. QVTKWidget、状态页和布局随后由 Qt 父子关系自动销毁。
}
QVTKWidget* nmWxVTKRenderContainerWidget::getVtkWidget() const
{
return m_pVtkWidget;
}
vtkRenderer* nmWxVTKRenderContainerWidget::getRenderer() const
{
return m_pRenderer;
}
vtkRenderWindow* nmWxVTKRenderContainerWidget::getRenderWindow() const
{
return m_pVtkWidget != NULL ? m_pVtkWidget->GetRenderWindow() : NULL;
}
vtkRenderWindowInteractor*
nmWxVTKRenderContainerWidget::getInteractor() const
{
vtkRenderWindow* pRenderWindow = getRenderWindow();
return pRenderWindow != NULL ? pRenderWindow->GetInteractor() : NULL;
}
nmVTKScene* nmWxVTKRenderContainerWidget::getScene() const
{
return m_pScene;
}
nmVTKViewCommandController*
nmWxVTKRenderContainerWidget::getViewCommandController() const
{
return m_pViewCommandController;
}
void nmWxVTKRenderContainerWidget::setViewCommandCapabilities(
nmVTKViewCommandController::ViewCommands eCommands)
{
if(m_pViewCommandController != NULL) {
m_pViewCommandController->setCapabilities(eCommands);
}
}
bool nmWxVTKRenderContainerWidget::installInteractionStyle(
vtkInteractorStyle* pStyle)
{
Q_ASSERT(isGuiThread());
return isGuiThread() && m_pInteractionManager != NULL &&
m_pInteractionManager->installInteractionStyle(pStyle);
}
bool nmWxVTKRenderContainerWidget::restoreDefaultInteraction()
{
Q_ASSERT(isGuiThread());
return isGuiThread() && m_pInteractionManager != NULL &&
m_pInteractionManager->restoreDefaultInteraction();
}
void nmWxVTKRenderContainerWidget::clearInteractionStyle()
{
Q_ASSERT(isGuiThread());
if(isGuiThread() && m_pInteractionManager != NULL) {
m_pInteractionManager->clearInteractionStyle();
}
}
void nmWxVTKRenderContainerWidget::renderView()
{
Q_ASSERT(isGuiThread());
vtkRenderWindow* pRenderWindow = getRenderWindow();
// Scene、Layer 和相机均不隐式渲染,批量更新最终汇聚到此入口。
if(isGuiThread() && pRenderWindow != NULL) {
pRenderWindow->Render();
}
}
bool nmWxVTKRenderContainerWidget::fitTopViewWithoutRender()
{
Q_ASSERT(isGuiThread());
// 相机控制器本身不触发 Render便于业务图层完成整批更新后只渲染一次。
return isGuiThread() && m_pCameraController != NULL &&
m_pCameraController->setTopView();
}
void nmWxVTKRenderContainerWidget::showReadyState()
{
Q_ASSERT(isGuiThread());
if(!isGuiThread()) {
return;
}
// 状态页与 VTK 页尺寸稳定,切换页面不会重建 RenderWindow 或 Renderer。
m_pStateStack->setCurrentWidget(m_pVtkWidget);
if(m_pViewCommandController != NULL) {
m_pViewCommandController->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()
{
if(m_pCameraController != NULL && m_pCameraController->zoomIn()) {
renderView();
}
}
void nmWxVTKRenderContainerWidget::zoomOut()
{
if(m_pCameraController != NULL && m_pCameraController->zoomOut()) {
renderView();
}
}
void nmWxVTKRenderContainerWidget::fitView()
{
if(m_pCameraController != NULL && m_pCameraController->fitView()) {
renderView();
}
}
void nmWxVTKRenderContainerWidget::resetCamera()
{
// 控制器用返回值表示相机是否确实改变,成功时只提交一次渲染。
if(m_pCameraController != NULL && m_pCameraController->resetCamera()) {
renderView();
}
}
void nmWxVTKRenderContainerWidget::setTopView()
{
if(m_pCameraController != NULL && m_pCameraController->setTopView()) {
renderView();
}
}
void nmWxVTKRenderContainerWidget::setFrontView()
{
if(m_pCameraController != NULL && m_pCameraController->setFrontView()) {
renderView();
}
}
void nmWxVTKRenderContainerWidget::setRightView()
{
if(m_pCameraController != NULL && m_pCameraController->setRightView()) {
renderView();
}
}
void nmWxVTKRenderContainerWidget::setIsometricView()
{
if(m_pCameraController != NULL &&
m_pCameraController->setIsometricView()) {
renderView();
}
}
void nmWxVTKRenderContainerWidget::setRotationCenter(
double dX,
double dY,
double dZ)
{
if(m_pCameraController != NULL &&
m_pCameraController->setRotationCenter(dX, dY, dZ)) {
renderView();
}
}
void nmWxVTKRenderContainerWidget::resetRotationCenter()
{
if(m_pCameraController != NULL &&
m_pCameraController->resetRotationCenter()) {
renderView();
}
}
bool nmWxVTKRenderContainerWidget::eventFilter(
QObject* pObject,
QEvent* pEvent)
{
if(pObject == m_pVtkWidget && pEvent != NULL &&
pEvent->type() == QEvent::MouseButtonPress) {
QMouseEvent* pMouseEvent = static_cast<QMouseEvent*>(pEvent);
if(pMouseEvent->button() == Qt::RightButton) {
// 菜单引用命令控制器长期持有的 QAction不在每次右键时重复创建。
QMenu oMenu(this);
if(m_pViewCommandController != NULL) {
m_pViewCommandController->populateContextMenu(&oMenu);
}
if(!oMenu.isEmpty()) {
oMenu.exec(pMouseEvent->globalPos());
}
// 返回 true 只消费右键按下,避免 VTK 同时启动一次交互动作。
return true;
}
return false;
}
return QWidget::eventFilter(pObject, pEvent);
}
void nmWxVTKRenderContainerWidget::saveImage()
{
if(m_pImageExporter == NULL) {
return;
}
// 实际编码格式由 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()
{
if(m_pImageExporter != NULL &&
!m_pImageExporter->copyImageToClipboard()) {
QMessageBox::warning(this,
tr("Copy Failed"),
tr("Unable to capture the image."));
}
}
void nmWxVTKRenderContainerWidget::printImage()
{
if(m_pImageExporter != NULL) {
m_pImageExporter->printImage(this);
}
}
void nmWxVTKRenderContainerWidget::printPreview()
{
if(m_pImageExporter != NULL) {
m_pImageExporter->printPreview(this);
}
}
void nmWxVTKRenderContainerWidget::showMessageState(
ViewState eState,
const QString& sMessage)
{
Q_ASSERT(isGuiThread());
if(!isGuiThread() || m_pStateStack == NULL ||
m_pStatePage == NULL || m_pStateLabel == NULL) {
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);
if(m_pViewCommandController != NULL) {
m_pViewCommandController->setViewAvailable(false);
}
}
bool nmWxVTKRenderContainerWidget::isGuiThread() const
{
QCoreApplication* pApplication = QCoreApplication::instance();
return pApplication == NULL ||
pApplication->thread() == QThread::currentThread();
}