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

377 lines
14 KiB
C++

#include "nmWxPebiGridViewWidget.h"
#include "nmPebiGridSceneController.h"
#include "nmPebiGridViewData.h"
#include "nmVTKViewCommandController.h"
#include "nmWxVTKRenderContainerWidget.h"
#include <QAction>
#include <QComboBox>
#include <QCoreApplication>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QMenu>
#include <QSize>
#include <QToolBar>
#include <QVBoxLayout>
#include <QVariant>
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_pGridLinesAction(nullptr),
m_pScalarBarAction(nullptr),
m_pBoundsGridAction(nullptr),
m_pPropertyComboBox(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);
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_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(20, 20));
m_pToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
m_pToolBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
nmVTKViewCommandController* pCommandController =
m_pRenderContainer->getViewCommandController();
// 三个业务 QAction 与公共命令一样长期持有,并同时用于工具栏和右键菜单。
m_pGridLinesAction = new QAction(
QIcon(gridViewIconPath("Grids.png")),
tr("Show Grid Lines"), this);
m_pGridLinesAction->setToolTip(tr("Show Grid Lines"));
m_pGridLinesAction->setCheckable(true);
m_pScalarBarAction = new QAction(
QIcon(gridViewIconPath("Eyes.png")),
tr("Show Scalar Bar"), this);
m_pScalarBarAction->setToolTip(tr("Show Scalar Bar"));
m_pScalarBarAction->setCheckable(true);
m_pBoundsGridAction = new QAction(
QIcon(gridViewIconPath("CalGrid.png")),
tr("Coordinate Grid"), this);
m_pBoundsGridAction->setToolTip(tr("Coordinate 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* pCopyAction = pCommandController->getAction(
nmVTKViewCommandController::ViewCommand_CopyImage);
if(pZoomOutAction != nullptr) {
m_pToolBar->addAction(pZoomOutAction);
}
if(pFitAction != nullptr) {
m_pToolBar->addAction(pFitAction);
}
if(pZoomInAction != nullptr) {
m_pToolBar->addAction(pZoomInAction);
}
if(pTopAction != nullptr) {
m_pToolBar->addAction(pTopAction);
}
m_pToolBar->addSeparator();
m_pToolBar->addAction(m_pGridLinesAction);
m_pToolBar->addAction(m_pScalarBarAction);
m_pToolBar->addAction(m_pBoundsGridAction);
m_pToolBar->addSeparator();
if(pCopyAction != nullptr) {
m_pToolBar->addAction(pCopyAction);
}
} else {
// 理论上不会进入;仍保留业务动作,避免容器初始化异常时布局跳动。
m_pToolBar->addAction(m_pGridLinesAction);
m_pToolBar->addAction(m_pScalarBarAction);
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);
QLabel* pPropertyLabel = new QLabel(tr("Property:"), pControlBar);
m_pPropertyComboBox = new QComboBox(pControlBar);
m_pPropertyComboBox->setMinimumContentsLength(9);
pControlLayout->addWidget(pPropertyLabel);
pControlLayout->addWidget(m_pPropertyComboBox);
pControlLayout->addStretch(1);
m_pGridInfoLabel = new QLabel(pControlBar);
m_pGridInfoLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
pControlLayout->addWidget(m_pGridInfoLabel);
pMainLayout->addWidget(pControlBar);
connect(m_pPropertyComboBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(onPropertyChanged(int)));
connect(m_pGridLinesAction, SIGNAL(toggled(bool)),
this, SLOT(onGridLinesToggled(bool)));
connect(m_pScalarBarAction, SIGNAL(toggled(bool)),
this, SLOT(onScalarBarToggled(bool)));
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;
}
if(!m_pSceneController->setViewData(oViewData, pError)) {
// 控制器回滚失败时同步清除外层标志,避免把空场景误当作旧成功网格。
m_bHasViewData = m_pSceneController->hasViewData();
return false;
}
// 属性列表只登记 Builder 已确认与 CellData 单元数一致的 Material。
const bool bOldBlockState = m_pPropertyComboBox->blockSignals(true);
m_pPropertyComboBox->clear();
m_pPropertyComboBox->addItem(tr("No property"), QString());
if(oViewData.hasMaterial()) {
m_pPropertyComboBox->addItem(tr("Material"), QString("Material"));
}
const QString sActiveProperty = m_pSceneController->getProperty();
for(int nIndex = 0; nIndex < m_pPropertyComboBox->count(); ++nIndex) {
if(m_pPropertyComboBox->itemData(nIndex).toString() ==
sActiveProperty) {
m_pPropertyComboBox->setCurrentIndex(nIndex);
break;
}
}
m_pPropertyComboBox->blockSignals(bOldBlockState);
m_pGridInfoLabel->setText(
tr("%1 cells | %2")
.arg(static_cast<qlonglong>(
oViewData.getValidCellCount()))
.arg(gridTypeText(static_cast<int>(oViewData.getGridType()))));
m_pStatusLabel->hide();
m_bHasViewData = true;
setControlsEnabled(true);
updateScalarBarActionState();
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_pStatusLabel->setText(sMessage);
m_pStatusLabel->show();
setControlsEnabled(true);
updateScalarBarActionState();
m_pRenderContainer->showReadyState();
} else {
m_pStatusLabel->hide();
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()
{
bool bBlocked = m_pPropertyComboBox->blockSignals(true);
m_pPropertyComboBox->clear();
m_pPropertyComboBox->addItem(tr("No property"), QString());
m_pPropertyComboBox->blockSignals(bBlocked);
bBlocked = m_pGridLinesAction->blockSignals(true);
m_pGridLinesAction->setChecked(true);
m_pGridLinesAction->blockSignals(bBlocked);
bBlocked = m_pScalarBarAction->blockSignals(true);
m_pScalarBarAction->setChecked(true);
m_pScalarBarAction->blockSignals(bBlocked);
bBlocked = m_pBoundsGridAction->blockSignals(true);
m_pBoundsGridAction->setChecked(false);
m_pBoundsGridAction->blockSignals(bBlocked);
m_pGridInfoLabel->setText(tr("0 cells | Unknown"));
}
void nmWxPebiGridViewWidget::setControlsEnabled(bool bEnabled)
{
m_pPropertyComboBox->setEnabled(bEnabled);
m_pGridLinesAction->setEnabled(bEnabled);
m_pBoundsGridAction->setEnabled(bEnabled);
m_pScalarBarAction->setEnabled(
bEnabled && m_pSceneController != nullptr &&
!m_pSceneController->getProperty().isEmpty());
}
void nmWxPebiGridViewWidget::updateScalarBarActionState()
{
if(m_pSceneController == nullptr) {
return;
}
const bool bBlocked = m_pScalarBarAction->blockSignals(true);
m_pScalarBarAction->setChecked(
m_pSceneController->isScalarBarVisible());
m_pScalarBarAction->blockSignals(bBlocked);
m_pScalarBarAction->setEnabled(
m_bHasViewData &&
!m_pSceneController->getProperty().isEmpty());
}
QString nmWxPebiGridViewWidget::gridTypeText(int nGridType) const
{
return nGridType == static_cast<int>(NM_Grid_PEBI)
? QString("PEBI") : tr("Unknown");
}
void nmWxPebiGridViewWidget::onPropertyChanged(int nIndex)
{
if(!m_bHasViewData || nIndex < 0 || m_pSceneController == nullptr) {
return;
}
const QString sPropertyName =
m_pPropertyComboBox->itemData(nIndex).toString();
if(!m_pSceneController->setProperty(sPropertyName)) {
const bool bBlocked = m_pPropertyComboBox->blockSignals(true);
m_pPropertyComboBox->setCurrentIndex(0);
m_pPropertyComboBox->blockSignals(bBlocked);
}
updateScalarBarActionState();
}
void nmWxPebiGridViewWidget::onGridLinesToggled(bool bVisible)
{
if(m_bHasViewData && m_pSceneController != nullptr) {
m_pSceneController->setGridLinesVisible(bVisible);
}
}
void nmWxPebiGridViewWidget::onScalarBarToggled(bool bVisible)
{
if(m_bHasViewData && m_pSceneController != nullptr) {
m_pSceneController->setScalarBarVisible(bVisible);
}
}
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_pGridLinesAction);
pMenu->addAction(m_pScalarBarAction);
pMenu->addAction(m_pBoundsGridAction);
}