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/nmSubWxs/nmWxPebiGridViewWidget.cpp

295 lines
11 KiB
C++

#include "nmWxPebiGridViewWidget.h"
#include "nmPebiGridSceneController.h"
#include "nmPebiGridViewData.h"
#include "nmVTKViewCommandController.h"
#include "nmWxVTKRenderContainerWidget.h"
#include <QAction>
#include <QCoreApplication>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QMenu>
#include <QSize>
#include <QToolBar>
#include <QVBoxLayout>
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(nullptr),
m_pSceneController(nullptr),
m_pToolBar(nullptr),
m_pBoundsGridAction(nullptr),
m_pGridInfoLabel(nullptr),
m_pStatusLabel(nullptr),
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 = nullptr;
}
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);
// 公共容器独占 Renderer、相机和交互对象本类只裁剪可见命令能力。
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_Front |
nmVTKViewCommandController::ViewCommand_SetRotationCenter |
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(32, 32));
m_pToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
m_pToolBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
nmVTKViewCommandController* pCommandController =
m_pRenderContainer->getViewCommandController();
// 比例网格 QAction 长期持有,并同时用于工具栏和右键菜单。
m_pBoundsGridAction = new QAction(
QIcon(gridViewIconPath("NmScaleGrid.png")),
tr("Scale Grid"), this);
m_pBoundsGridAction->setToolTip(tr("Scale Grid"));
m_pBoundsGridAction->setCheckable(true);
if(pCommandController != nullptr) {
// 网格视图按自身工作流排序,但仍引用命令控制器持有的公共 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* pFrontAction = pCommandController->getAction(
nmVTKViewCommandController::ViewCommand_Front);
QAction* pSetRotationCenterAction = pCommandController->getAction(
nmVTKViewCommandController::
ViewCommand_SetRotationCenter);
QAction* pCopyAction = pCommandController->getAction(
nmVTKViewCommandController::ViewCommand_CopyImage);
// 按“缩放、标准视角、显示、输出”分组,空组不会留下多余分隔线。
bool bHasPreviousGroup = false;
if(pZoomOutAction != nullptr) {
m_pToolBar->addAction(pZoomOutAction);
bHasPreviousGroup = true;
}
if(pFitAction != nullptr) {
m_pToolBar->addAction(pFitAction);
bHasPreviousGroup = true;
}
if(pZoomInAction != nullptr) {
m_pToolBar->addAction(pZoomInAction);
bHasPreviousGroup = true;
}
const bool bHasViewGroup = pTopAction != nullptr ||
pFrontAction != nullptr ||
pSetRotationCenterAction != nullptr;
if(bHasViewGroup && bHasPreviousGroup) {
m_pToolBar->addSeparator();
}
if(pTopAction != nullptr) {
m_pToolBar->addAction(pTopAction);
bHasPreviousGroup = true;
}
if(pFrontAction != nullptr) {
m_pToolBar->addAction(pFrontAction);
bHasPreviousGroup = true;
}
if(pSetRotationCenterAction != nullptr) {
m_pToolBar->addAction(pSetRotationCenterAction);
bHasPreviousGroup = true;
}
if(bHasPreviousGroup) {
m_pToolBar->addSeparator();
}
m_pToolBar->addAction(m_pBoundsGridAction);
if(pCopyAction != nullptr) {
m_pToolBar->addSeparator();
m_pToolBar->addAction(pCopyAction);
}
} else {
// 理论上不会进入;仍保留业务动作,避免容器初始化异常时布局跳动。
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);
m_pGridInfoLabel = new QLabel(pControlBar);
m_pGridInfoLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
pControlLayout->addWidget(m_pGridInfoLabel);
pControlLayout->addStretch(1);
pMainLayout->addWidget(pControlBar);
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 == nullptr || !oViewData.isValid()) {
if(pError != nullptr) {
*pError = tr("The grid preview data is invalid.");
}
return false;
}
// SceneController 负责事务式绑定;失败时这里按其回滚结果同步外层状态。
if(!m_pSceneController->setViewData(oViewData, pError)) {
// 控制器回滚失败时同步清除外层标志,避免把空场景误当作旧成功网格。
m_bHasViewData = m_pSceneController->hasViewData();
return false;
}
// Builder 只接受已提交的 PEBI 网格,因此底栏无需再显示“未知类型”。
m_pGridInfoLabel->setText(
tr("PEBI | %1 valid cells | XY planar grid")
.arg(static_cast<qlonglong>(
oViewData.getValidCellCount())));
m_pStatusLabel->hide();
m_bHasViewData = true;
setControlsEnabled(true);
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_pRenderContainer->setRotationCenterPickingEnabled(false);
m_pStatusLabel->setText(sMessage);
m_pStatusLabel->show();
setControlsEnabled(true);
m_pRenderContainer->showReadyState();
} else {
m_pStatusLabel->hide();
resetControls();
setControlsEnabled(false);
m_pRenderContainer->showErrorState(sMessage);
}
}
void nmWxPebiGridViewWidget::clearView()
{
if(m_pSceneController != nullptr) {
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()
{
const bool bBlocked = m_pBoundsGridAction->blockSignals(true);
m_pBoundsGridAction->setChecked(false);
m_pBoundsGridAction->blockSignals(bBlocked);
m_pGridInfoLabel->setText(
tr("PEBI | %1 valid cells | XY planar grid").arg(0));
}
void nmWxPebiGridViewWidget::setControlsEnabled(bool bEnabled)
{
m_pBoundsGridAction->setEnabled(bEnabled);
}
void nmWxPebiGridViewWidget::onBoundsGridToggled(bool bVisible)
{
if(m_bHasViewData && m_pSceneController != nullptr) {
m_pSceneController->setBoundsGridVisible(bVisible);
}
}
void nmWxPebiGridViewWidget::appendContextMenuActions(QMenu* pMenu)
{
if(pMenu == nullptr) {
return;
}
// 右键菜单与工具栏引用同一个比例网格 QAction勾选状态不会发生分叉。
pMenu->addAction(m_pBoundsGridAction);
}