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/nmWxPebiResultControlBar.cpp

802 lines
31 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 "nmWxPebiResultControlBar.h"
#include "nmNumericDisplayFormat.h"
#include "nmFFmpegVideoWriter.h"
#include "nmPebiResultPlaybackController.h"
#include "nmPebiResultPropertyDisplayText.h"
#include "nmPebiResultSceneController.h"
#include "nmVTKMeshLayer.h"
#include "nmVolumeSceneController.h"
#include "nmWxPebiResultAnimationSettingsDlg.h"
#include "nmWxPebiResultColorScaleSettingsDlg.h"
#include <QAction>
#include <QComboBox>
#include <QCoreApplication>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QScopedPointer>
#include <QSize>
#include <QSizePolicy>
#include <QToolButton>
#include <QVariant>
namespace {
const int PROPERTY_COMBO_MINIMUM_WIDTH = 110;
const int PROPERTY_COMBO_MAXIMUM_WIDTH = 140;
const int TIME_COMBO_MINIMUM_WIDTH = 160;
const int TIME_COMBO_MAXIMUM_WIDTH = 240;
const int CONTROL_GROUP_GAP = 8;
// Qt 4 按 20 个“X”字符估算 SizeHint,常用字体下约为 190px。
const int TIME_COMBO_MINIMUM_CONTENT_LENGTH = 20;
QString resultControlIconPath(const QString& sFileName)
{
const QString sApplicationDir = QCoreApplication::applicationDirPath();
return sApplicationDir.section('/', 0, -2) +
"/Res/Icon/" + sFileName;
}
QToolButton* createControlButton(QWidget* pParent,
const QString& sIconFile,
const QString& sToolTip)
{
QToolButton* pButton = new QToolButton(pParent);
pButton->setAutoRaise(true);
pButton->setFixedSize(24, 24);
pButton->setIconSize(QSize(16, 16));
pButton->setIcon(QIcon(resultControlIconPath(sIconFile)));
pButton->setToolTip(sToolTip);
return pButton;
}
}
nmWxPebiResultControlBar::nmWxPebiResultControlBar(
nmPebiResultSceneController* pSceneController,
QWidget* pParent)
: QWidget(pParent),
m_pPlaybackController(nullptr),
m_pSceneController(pSceneController),
m_pVolumeSceneController(nullptr),
m_pDisplayModeCombo(nullptr),
m_pPropertyCombo(nullptr),
m_pColorSettingsButton(nullptr),
m_pFirstButton(nullptr),
m_pPreviousButton(nullptr),
m_pTimeCombo(nullptr),
m_pNextButton(nullptr),
m_pLastButton(nullptr),
m_pPlayPauseButton(nullptr),
m_pSettingsButton(nullptr),
m_pExportAction(nullptr),
m_pExportButton(nullptr),
m_bExportInProgress(false),
m_bExternalInteractionLocked(false),
m_nDisplayedBindingRevision(0),
m_nDisplayedTimeFrameCount(-1),
m_bDisplayedPlaying(false)
{
Q_ASSERT(pSceneController != nullptr);
createUi();
m_pPlaybackController = new nmPebiResultPlaybackController(
pSceneController, this);
connect(m_pPlaybackController, SIGNAL(stateChanged()),
this, SLOT(refreshFromController()));
connect(m_pPlaybackController, SIGNAL(operationFailed(QString)),
this, SLOT(forwardOperationFailure(QString)));
m_pPlaybackController->synchronizeFromScene();
}
nmWxPebiResultControlBar::~nmWxPebiResultControlBar()
{
// QObject 随后删除子控制器;先停表可保证析构期间不再排入超时请求。
if(m_pPlaybackController != nullptr) {
m_pPlaybackController->blockSignals(true);
m_pPlaybackController->pause();
}
}
void nmWxPebiResultControlBar::stopPlayback()
{
if(m_pPlaybackController != nullptr) {
m_pPlaybackController->pause();
}
}
void nmWxPebiResultControlBar::synchronizeFromScene()
{
if(m_pVolumeSceneController != nullptr) {
refreshFromVolumeController();
return;
}
if(m_pPlaybackController != nullptr) {
m_pPlaybackController->synchronizeFromScene();
}
}
void nmWxPebiResultControlBar::setVolumeSceneController(
nmVolumeSceneController* pController)
{
if(m_pVolumeSceneController != pController) {
if(pController != nullptr) stopPlayback();
m_pVolumeSceneController = pController;
// 两类数据可能拥有相同属性名,切换来源时必须使列表缓存失效。
m_sDisplayedVolumeDataId.clear();
m_sDisplayedSnapshotId.clear();
m_nDisplayedBindingRevision = 0;
m_listDisplayedPropertyIds.clear();
clearTimeItems();
// 两种数据源共用 createUi 的宽度与伸缩策略,切换时不重新分配底栏空间。
m_pPropertyCombo->setToolTip(tr("Display property"));
}
synchronizeFromScene();
}
nmPebiResultPlaybackController*
nmWxPebiResultControlBar::getPlaybackController() const
{
return m_pPlaybackController;
}
QAction* nmWxPebiResultControlBar::getExportAnimationAction() const
{
return m_pExportAction;
}
void nmWxPebiResultControlBar::setExportInProgress(bool bInProgress)
{
if(m_bExportInProgress == bInProgress) {
return;
}
m_bExportInProgress = bInProgress;
refreshFromController();
emit exportInProgressChanged(bInProgress);
}
bool nmWxPebiResultControlBar::isExportInProgress() const
{
return m_bExportInProgress;
}
void nmWxPebiResultControlBar::setExternalInteractionLocked(bool bLocked)
{
if(m_bExternalInteractionLocked == bLocked) {
return;
}
// 该锁只保护当前结果身份,不改变 SceneController 中已选属性和帧。
m_bExternalInteractionLocked = bLocked;
if(bLocked && m_pPlaybackController != nullptr) {
// 防御外部调用顺序错误,绘制事务绝不能与仍在推进的 Timer 并存。
m_pPlaybackController->pause();
}
refreshFromController();
}
bool nmWxPebiResultControlBar::isExternalInteractionLocked() const
{
return m_bExternalInteractionLocked;
}
void nmWxPebiResultControlBar::onDisplayModeIndexChanged(int nIndex)
{
if(m_pDisplayModeCombo == nullptr ||
!m_pDisplayModeCombo->isEnabled() ||
m_pPlaybackController == nullptr || m_pSceneController == nullptr ||
m_bExportInProgress || m_bExternalInteractionLocked ||
m_pPlaybackController->isPlaying() ||
(m_pVolumeSceneController == nullptr && !m_pSceneController->hasSnapshot()) || nIndex < 0 ||
nIndex >= m_pDisplayModeCombo->count()) {
return;
}
bool bConverted = false;
const int nMode = m_pDisplayModeCombo->itemData(nIndex).toInt(
&bConverted);
const nmVTKMeshLayer::DisplayMode eMode =
static_cast<nmVTKMeshLayer::DisplayMode>(
nMode);
if(!bConverted || !nmVTKMeshLayer::isValidDisplayMode(eMode)) {
refreshFromController();
return;
}
if(m_pVolumeSceneController != nullptr) {
if(!m_pVolumeSceneController->hasData() ||
eMode == m_pVolumeSceneController->getMeshDisplayMode()) return;
m_pVolumeSceneController->setMeshDisplayMode(eMode);
emit operationSucceeded();
return;
}
if(eMode == m_pSceneController->getMeshDisplayMode()) {
return;
}
m_pSceneController->setMeshDisplayMode(eMode);
emit operationSucceeded();
}
void nmWxPebiResultControlBar::onPropertyIndexChanged(int nIndex)
{
if(m_pPropertyCombo == nullptr || !m_pPropertyCombo->isEnabled() ||
nIndex >= m_pPropertyCombo->count() ||
m_pPlaybackController == nullptr || m_bExportInProgress ||
m_bExternalInteractionLocked ||
m_pPlaybackController->isPlaying() || nIndex < 0) {
return;
}
if(m_pVolumeSceneController != nullptr) {
QString sError;
const bool bApplied = m_pVolumeSceneController->applyProperty(
m_pPropertyCombo->itemData(nIndex).toString(), &sError);
// 失败时恢复到场景中仍在使用的属性,不能让下拉框与当前画面错位。
refreshFromVolumeController();
if(bApplied) emit operationSucceeded();
else emit operationFailed(sError);
return;
}
if(m_pPlaybackController->selectProperty(
m_pPropertyCombo->itemData(nIndex).toString())) {
emit operationSucceeded();
}
}
void nmWxPebiResultControlBar::onTimeIndexChanged(int nIndex)
{
if(m_pVolumeSceneController != nullptr) return;
if(m_pPlaybackController == nullptr || m_bExportInProgress ||
m_bExternalInteractionLocked ||
m_pPlaybackController->isPlaying() || nIndex < 0) {
return;
}
if(m_pPlaybackController->selectFrame(
m_pTimeCombo->itemData(nIndex).toInt())) {
emit operationSucceeded();
}
}
void nmWxPebiResultControlBar::onColorSettingsClicked()
{
if(m_pPlaybackController == nullptr || m_pSceneController == nullptr ||
m_bExportInProgress ||
m_bExternalInteractionLocked ||
m_pPlaybackController->isPlaying()) {
return;
}
// 同一底栏入口按数据源创建同一对话框,三维场景无需计算结果快照即可设置颜色。
QScopedPointer<nmWxPebiResultColorScaleSettingsDlg> pDialog;
if(m_pVolumeSceneController != nullptr) {
if(!m_pVolumeSceneController->hasScalarProperty()) return;
pDialog.reset(new nmWxPebiResultColorScaleSettingsDlg(m_pVolumeSceneController, this));
} else {
if(!m_pSceneController->hasSnapshot()) return;
pDialog.reset(new nmWxPebiResultColorScaleSettingsDlg(m_pSceneController, this));
}
connect(pDialog.data(), SIGNAL(displaySettingsApplied()),
this, SIGNAL(operationSucceeded()));
connect(pDialog.data(), SIGNAL(operationFailed(QString)),
this, SLOT(forwardOperationFailure(QString)));
pDialog->exec();
}
void nmWxPebiResultControlBar::onFirstFrameClicked()
{
if(m_pVolumeSceneController != nullptr) return;
if(m_pPlaybackController == nullptr || m_bExportInProgress ||
m_bExternalInteractionLocked ||
m_pPlaybackController->isPlaying()) {
return;
}
if(m_pPlaybackController->selectFirstFrame()) {
emit operationSucceeded();
}
}
void nmWxPebiResultControlBar::onPreviousFrameClicked()
{
if(m_pVolumeSceneController != nullptr) return;
if(m_pPlaybackController == nullptr || m_bExportInProgress ||
m_bExternalInteractionLocked ||
m_pPlaybackController->isPlaying()) {
return;
}
if(m_pPlaybackController->selectPreviousFrame()) {
emit operationSucceeded();
}
}
void nmWxPebiResultControlBar::onNextFrameClicked()
{
if(m_pVolumeSceneController != nullptr) return;
if(m_pPlaybackController == nullptr || m_bExportInProgress ||
m_bExternalInteractionLocked ||
m_pPlaybackController->isPlaying()) {
return;
}
if(m_pPlaybackController->selectNextFrame()) {
emit operationSucceeded();
}
}
void nmWxPebiResultControlBar::onLastFrameClicked()
{
if(m_pVolumeSceneController != nullptr) return;
if(m_pPlaybackController == nullptr || m_bExportInProgress ||
m_bExternalInteractionLocked ||
m_pPlaybackController->isPlaying()) {
return;
}
if(m_pPlaybackController->selectLastFrame()) {
emit operationSucceeded();
}
}
void nmWxPebiResultControlBar::onPlayPauseClicked()
{
if(m_pVolumeSceneController != nullptr) return;
if(m_pPlaybackController == nullptr || m_bExportInProgress ||
m_bExternalInteractionLocked) {
return;
}
if(m_pPlaybackController->togglePlayback()) {
emit operationSucceeded();
}
}
void nmWxPebiResultControlBar::onAnimationSettingsClicked()
{
if(m_pVolumeSceneController != nullptr) return;
if(m_pPlaybackController == nullptr || m_bExportInProgress ||
m_bExternalInteractionLocked ||
m_pPlaybackController->isPlaying()) {
return;
}
nmWxPebiResultAnimationSettingsDlg oDialog(
m_pPlaybackController->getPlaybackSpeed(),
m_pPlaybackController->isLoopEnabled(), this);
if(oDialog.exec() == QDialog::Accepted) {
if(m_pPlaybackController->setPlaybackSettings(
oDialog.getPlaybackSpeed(),
oDialog.isLoopEnabled())) {
emit operationSucceeded();
}
}
}
void nmWxPebiResultControlBar::onExportAnimationTriggered()
{
if(m_pVolumeSceneController != nullptr) return;
if(m_pPlaybackController == nullptr || m_bExportInProgress ||
m_bExternalInteractionLocked ||
m_pPlaybackController->isPlaying() ||
!m_pPlaybackController->isCurrentPropertyTimeDependent() ||
m_pPlaybackController->getCurrentPropertyFrameCount() <= 1) {
return;
}
emit exportAnimationRequested();
}
void nmWxPebiResultControlBar::refreshFromController()
{
// 即使旧回放控制器发来状态通知,也只能刷新当前正在查看的数据源。
if(m_pVolumeSceneController != nullptr) {
refreshFromVolumeController();
return;
}
if(m_pDisplayModeCombo != nullptr && m_pSceneController != nullptr) {
const int nDisplayModeIndex = findItemByData(
m_pDisplayModeCombo,
QVariant(static_cast<int>(
m_pSceneController->getMeshDisplayMode())));
if(nDisplayModeIndex >= 0 &&
m_pDisplayModeCombo->currentIndex() != nDisplayModeIndex) {
const bool bDisplayModeBlocked =
m_pDisplayModeCombo->blockSignals(true);
m_pDisplayModeCombo->setCurrentIndex(nDisplayModeIndex);
m_pDisplayModeCombo->blockSignals(bDisplayModeBlocked);
}
}
// 控件状态严格镜像四类 Scene 状态:无快照、静态、单帧动态、多帧动态。
const bool bAvailable = m_pPlaybackController != nullptr &&
m_pPlaybackController->isAvailable();
if(!bAvailable) {
const bool bPropertyBlocked = m_pPropertyCombo->blockSignals(true);
m_pPropertyCombo->clear();
m_pPropertyCombo->blockSignals(bPropertyBlocked);
clearTimeItems();
m_sDisplayedSnapshotId.clear();
m_nDisplayedBindingRevision = 0;
m_listDisplayedPropertyIds.clear();
m_pDisplayModeCombo->setEnabled(false);
m_pPropertyCombo->setEnabled(false);
m_pColorSettingsButton->setEnabled(false);
m_pFirstButton->setEnabled(false);
m_pPreviousButton->setEnabled(false);
m_pNextButton->setEnabled(false);
m_pLastButton->setEnabled(false);
m_pPlayPauseButton->setEnabled(false);
m_pSettingsButton->setEnabled(false);
m_pExportAction->setEnabled(false);
updatePlayPauseButton(false);
return;
}
const QString sSnapshotId =
m_pPlaybackController->getObservedSnapshotId();
const quint64 nBindingRevision =
m_pPlaybackController->getObservedBindingRevision();
const QStringList listPropertyIds =
m_pPlaybackController->getAvailablePropertyIds();
const bool bSnapshotChanged =
m_nDisplayedBindingRevision != nBindingRevision ||
m_sDisplayedSnapshotId != sSnapshotId;
if(bSnapshotChanged ||
m_listDisplayedPropertyIds != listPropertyIds) {
rebuildPropertyItems(listPropertyIds);
m_sDisplayedSnapshotId = sSnapshotId;
m_nDisplayedBindingRevision = nBindingRevision;
m_listDisplayedPropertyIds = listPropertyIds;
// 即使属性和帧数相同,新快照的真实时间值也可能不同,不能复用旧列表。
if(bSnapshotChanged) {
m_sDisplayedTimePropertyId.clear();
m_nDisplayedTimeFrameCount = -1;
}
}
const QString sPropertyId =
m_pPlaybackController->getCurrentPropertyId();
const int nPropertyIndex = findItemByData(
m_pPropertyCombo, QVariant(sPropertyId));
if(m_pPropertyCombo->currentIndex() != nPropertyIndex) {
const bool bPropertyBlocked = m_pPropertyCombo->blockSignals(true);
m_pPropertyCombo->setCurrentIndex(nPropertyIndex);
m_pPropertyCombo->blockSignals(bPropertyBlocked);
}
const bool bTimeDependent =
m_pPlaybackController->isPropertyTimeDependent(sPropertyId);
const int nFrameCount = bTimeDependent
? m_pPlaybackController->getPropertyFrameCount(sPropertyId) : 0;
if(!bTimeDependent) {
clearTimeItems();
} else if(m_sDisplayedTimePropertyId != sPropertyId ||
m_nDisplayedTimeFrameCount != nFrameCount) {
rebuildTimeItems(sPropertyId, nFrameCount);
}
const int nCurrentFrame =
m_pPlaybackController->getCurrentFrameIndex();
if(bTimeDependent) {
// rebuildTimeItems 按帧号完整构建,索引与帧号一致,无需每帧线性搜索。
const int nTimeIndex = nCurrentFrame >= 0 &&
nCurrentFrame < m_pTimeCombo->count() &&
m_pTimeCombo->itemData(nCurrentFrame) == QVariant(nCurrentFrame)
? nCurrentFrame : -1;
if(m_pTimeCombo->currentIndex() != nTimeIndex) {
const bool bTimeBlocked = m_pTimeCombo->blockSignals(true);
m_pTimeCombo->setCurrentIndex(nTimeIndex);
m_pTimeCombo->blockSignals(bTimeBlocked);
}
}
// 下拉列表和收起后的当前项都可悬停查看完整时间。
m_pTimeCombo->setToolTip(m_pTimeCombo->currentIndex() >= 0
? m_pTimeCombo->itemData(m_pTimeCombo->currentIndex(), Qt::ToolTipRole).toString()
: tr("Result time"));
const bool bHasCompleteTimeList = bTimeDependent &&
nFrameCount > 0 && m_pTimeCombo->count() == nFrameCount;
const bool bHasTime = bHasCompleteTimeList;
const bool bHasMultipleFrames = bHasCompleteTimeList &&
nFrameCount > 1;
const bool bPlaying = m_pPlaybackController->isPlaying();
// 回放、导出和外部绘制互斥,三者共用同一套控件可用性规则。
const bool bLocked = bPlaying || m_bExportInProgress ||
m_bExternalInteractionLocked;
m_pDisplayModeCombo->setEnabled(!bLocked);
m_pPropertyCombo->setEnabled(!bLocked);
m_pColorSettingsButton->setEnabled(!bLocked);
m_pTimeCombo->setEnabled(bHasTime && !bLocked);
m_pFirstButton->setEnabled(
bHasMultipleFrames && nCurrentFrame > 0 && !bLocked);
m_pPreviousButton->setEnabled(
bHasMultipleFrames && nCurrentFrame > 0 && !bLocked);
m_pNextButton->setEnabled(
bHasMultipleFrames &&
nCurrentFrame < nFrameCount - 1 && !bLocked);
m_pLastButton->setEnabled(
bHasMultipleFrames &&
nCurrentFrame < nFrameCount - 1 && !bLocked);
m_pPlayPauseButton->setEnabled(
bHasMultipleFrames && !m_bExportInProgress &&
!m_bExternalInteractionLocked);
m_pSettingsButton->setEnabled(bHasMultipleFrames && !bLocked);
QString sBackendError;
const bool bBackendAvailable =
nmFFmpegVideoWriter::isBackendAvailable(&sBackendError);
m_pExportAction->setEnabled(
bHasMultipleFrames && !bLocked && bBackendAvailable);
const QString sExportTip = bBackendAvailable
? tr("Export animation") : sBackendError;
m_pExportAction->setToolTip(sExportTip);
m_pExportAction->setStatusTip(sExportTip);
updatePlayPauseButton(bPlaying);
}
void nmWxPebiResultControlBar::refreshFromVolumeController()
{
if(m_pVolumeSceneController == nullptr) return;
const QSharedPointer<const nmVolumeData> pData = m_pVolumeSceneController->getData();
const QString sDataId = pData.isNull() ? QString() : pData->getDataId();
const bool bPropertyBlocked = m_pPropertyCombo->blockSignals(true);
if(pData.isNull() || m_sDisplayedVolumeDataId != sDataId) {
m_pPropertyCombo->clear();
if(!pData.isNull()) {
const QVector<nmVolumePropertyDescriptor>& vecProperties = pData->getProperties();
for(int nIndex = 0; nIndex < vecProperties.size(); ++nIndex) {
const nmVolumePropertyDescriptor& oProperty = vecProperties.at(nIndex);
// 仅显示原始属性名;同名单元、节点属性仍由不同 ID 区分,不合并条目。
m_pPropertyCombo->addItem(oProperty.m_sArrayName, oProperty.m_sPropertyId);
m_pPropertyCombo->setItemData(nIndex, oProperty.m_sArrayName, Qt::ToolTipRole);
}
if(vecProperties.isEmpty()) m_pPropertyCombo->addItem(tr("Solid Color"), QString());
}
m_sDisplayedVolumeDataId = sDataId;
}
m_pPropertyCombo->setCurrentIndex(findItemByData(m_pPropertyCombo,
m_pVolumeSceneController->getSliceSettings().m_sPropertyId));
m_pPropertyCombo->setToolTip(m_pPropertyCombo->currentText());
m_pPropertyCombo->blockSignals(bPropertyBlocked);
const bool bModeBlocked = m_pDisplayModeCombo->blockSignals(true);
m_pDisplayModeCombo->setCurrentIndex(findItemByData(m_pDisplayModeCombo,
static_cast<int>(m_pVolumeSceneController->getMeshDisplayMode())));
m_pDisplayModeCombo->blockSignals(bModeBlocked);
const bool bAvailable = !pData.isNull() && !m_bExportInProgress && !m_bExternalInteractionLocked;
m_pDisplayModeCombo->setEnabled(bAvailable);
m_pPropertyCombo->setEnabled(bAvailable && !pData->getProperties().isEmpty());
// 外部数据没有计算时间轴;所有旧按钮保留原位置,仅禁用不适用的操作。
clearTimeItems();
m_pColorSettingsButton->setEnabled(bAvailable && m_pVolumeSceneController->hasScalarProperty());
m_pFirstButton->setEnabled(false);
m_pPreviousButton->setEnabled(false);
m_pNextButton->setEnabled(false);
m_pLastButton->setEnabled(false);
m_pPlayPauseButton->setEnabled(false);
m_pSettingsButton->setEnabled(false);
m_pExportAction->setEnabled(false);
updatePlayPauseButton(false);
}
void nmWxPebiResultControlBar::updatePlayPauseButton(bool bPlaying)
{
if(m_bDisplayedPlaying == bPlaying) {
return;
}
m_bDisplayedPlaying = bPlaying;
m_pPlayPauseButton->setIcon(bPlaying ? m_oPauseIcon : m_oPlayIcon);
m_pPlayPauseButton->setToolTip(
bPlaying ? tr("Pause") : tr("Play"));
}
void nmWxPebiResultControlBar::forwardOperationFailure(
const QString& sError)
{
emit operationFailed(sError);
}
void nmWxPebiResultControlBar::createUi()
{
QHBoxLayout* pLayout = new QHBoxLayout(this);
pLayout->setContentsMargins(4, 3, 4, 3);
pLayout->setSpacing(4);
QLabel* pDisplayLabel = new QLabel(tr("Display"), this);
pDisplayLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_pDisplayModeCombo = new QComboBox(this);
m_pDisplayModeCombo->setObjectName(QLatin1String("resultDisplayModeCombo"));
m_pDisplayModeCombo->addItem(
tr("Point"),
QVariant(static_cast<int>(
nmVTKMeshLayer::DisplayMode_Point)));
m_pDisplayModeCombo->addItem(
tr("Wireframe"),
QVariant(static_cast<int>(
nmVTKMeshLayer::DisplayMode_Wireframe)));
m_pDisplayModeCombo->addItem(
tr("Surface"),
QVariant(static_cast<int>(
nmVTKMeshLayer::DisplayMode_Surface)));
m_pDisplayModeCombo->addItem(
tr("Contour"),
QVariant(static_cast<int>(
nmVTKMeshLayer::DisplayMode_SurfaceWithEdges)));
m_pDisplayModeCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
m_pDisplayModeCombo->setMinimumHeight(24);
m_pDisplayModeCombo->setSizePolicy(
QSizePolicy::Preferred, QSizePolicy::Fixed);
const int nInitialDisplayModeIndex = findItemByData(
m_pDisplayModeCombo,
QVariant(static_cast<int>(
m_pSceneController->getMeshDisplayMode())));
if(nInitialDisplayModeIndex >= 0) {
m_pDisplayModeCombo->setCurrentIndex(nInitialDisplayModeIndex);
}
QLabel* pPropertyLabel = new QLabel(tr("Property"), this);
pPropertyLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_pPropertyCombo = new QComboBox(this);
m_pPropertyCombo->setObjectName(QLatin1String("resultPropertyCombo"));
m_pPropertyCombo->setMinimumWidth(PROPERTY_COMBO_MINIMUM_WIDTH);
m_pPropertyCombo->setMaximumWidth(PROPERTY_COMBO_MAXIMUM_WIDTH);
m_pPropertyCombo->setMinimumHeight(24);
m_pPropertyCombo->setSizePolicy(
QSizePolicy::Preferred, QSizePolicy::Fixed);
m_pPropertyCombo->setToolTip(tr("Display property"));
m_pColorSettingsButton = createControlButton(
this, QString::fromLatin1("NmColorScaleSettings.png"),
tr("Color settings"));
m_pColorSettingsButton->setObjectName(QLatin1String("resultColorSettingsButton"));
m_pFirstButton = createControlButton(
this, QString::fromLatin1("NmFirstStep.png"),
tr("First frame"));
m_pPreviousButton = createControlButton(
this, QString::fromLatin1("NmPreviousStep.png"),
tr("Previous frame"));
m_pTimeCombo = new QComboBox(this);
m_pTimeCombo->setObjectName(QLatin1String("resultTimeCombo"));
m_pTimeCombo->setMinimumWidth(TIME_COMBO_MINIMUM_WIDTH);
m_pTimeCombo->setMaximumWidth(TIME_COMBO_MAXIMUM_WIDTH);
m_pTimeCombo->setMinimumContentsLength(
TIME_COMBO_MINIMUM_CONTENT_LENGTH);
m_pTimeCombo->setSizeAdjustPolicy(
QComboBox::AdjustToMinimumContentsLengthWithIcon);
m_pTimeCombo->setMinimumHeight(24);
m_pTimeCombo->setSizePolicy(
QSizePolicy::Preferred, QSizePolicy::Fixed);
m_pTimeCombo->setToolTip(tr("Result time"));
m_pNextButton = createControlButton(
this, QString::fromLatin1("NmNextStep.png"),
tr("Next frame"));
m_pLastButton = createControlButton(
this, QString::fromLatin1("NmLastStep.png"),
tr("Last frame"));
m_pPlayPauseButton = createControlButton(
this, QString::fromLatin1("NmStartAnimation.png"),
tr("Play"));
m_oPlayIcon = m_pPlayPauseButton->icon();
m_oPauseIcon = QIcon(resultControlIconPath(
QString::fromLatin1("NmStopAnimation.png")));
m_pSettingsButton = createControlButton(
this, QString::fromLatin1("NmAnimationSettings.png"),
tr("Animation settings"));
m_pExportAction = new QAction(
QIcon(resultControlIconPath("Export.png")),
tr("Export animation"), this);
m_pExportAction->setToolTip(tr("Export animation"));
m_pExportButton = new QToolButton(this);
m_pExportButton->setAutoRaise(true);
m_pExportButton->setFixedSize(24, 24);
m_pExportButton->setIconSize(QSize(16, 16));
m_pExportButton->setDefaultAction(m_pExportAction);
pLayout->addWidget(pDisplayLabel, 0, Qt::AlignVCenter);
pLayout->addWidget(m_pDisplayModeCombo);
pLayout->addSpacing(CONTROL_GROUP_GAP);
pLayout->addWidget(pPropertyLabel, 0, Qt::AlignVCenter);
pLayout->addWidget(m_pPropertyCombo);
pLayout->addWidget(m_pColorSettingsButton);
pLayout->addSpacing(CONTROL_GROUP_GAP);
pLayout->addWidget(m_pFirstButton);
pLayout->addWidget(m_pPreviousButton);
pLayout->addWidget(m_pTimeCombo);
pLayout->addWidget(m_pNextButton);
pLayout->addWidget(m_pLastButton);
pLayout->addWidget(m_pPlayPauseButton);
pLayout->addWidget(m_pSettingsButton);
pLayout->addWidget(m_pExportButton);
pLayout->addStretch(1);
connect(m_pDisplayModeCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(onDisplayModeIndexChanged(int)));
connect(m_pPropertyCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(onPropertyIndexChanged(int)));
connect(m_pColorSettingsButton, SIGNAL(clicked()),
this, SLOT(onColorSettingsClicked()));
connect(m_pTimeCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(onTimeIndexChanged(int)));
connect(m_pFirstButton, SIGNAL(clicked()),
this, SLOT(onFirstFrameClicked()));
connect(m_pPreviousButton, SIGNAL(clicked()),
this, SLOT(onPreviousFrameClicked()));
connect(m_pNextButton, SIGNAL(clicked()),
this, SLOT(onNextFrameClicked()));
connect(m_pLastButton, SIGNAL(clicked()),
this, SLOT(onLastFrameClicked()));
connect(m_pPlayPauseButton, SIGNAL(clicked()),
this, SLOT(onPlayPauseClicked()));
connect(m_pSettingsButton, SIGNAL(clicked()),
this, SLOT(onAnimationSettingsClicked()));
connect(m_pExportAction, SIGNAL(triggered()),
this, SLOT(onExportAnimationTriggered()));
}
void nmWxPebiResultControlBar::rebuildPropertyItems(
const QStringList& listPropertyIds)
{
const bool bBlocked = m_pPropertyCombo->blockSignals(true);
m_pPropertyCombo->clear();
for(int nIndex = 0; nIndex < listPropertyIds.size(); ++nIndex) {
const QString& sPropertyId = listPropertyIds[nIndex];
QString sDisplayName =
nmPebiResultPropertyDisplayText::getDisplayName(sPropertyId);
if(sDisplayName.isEmpty()) {
sDisplayName = sPropertyId;
}
m_pPropertyCombo->addItem(sDisplayName, sPropertyId);
}
m_pPropertyCombo->blockSignals(bBlocked);
}
void nmWxPebiResultControlBar::rebuildTimeItems(
const QString& sPropertyId,
int nFrameCount)
{
const bool bBlocked = m_pTimeCombo->blockSignals(true);
m_pTimeCombo->clear();
bool bComplete = true;
QVector<double> vecTimes;
for(int nFrameIndex = 0; nFrameIndex < nFrameCount; ++nFrameIndex) {
double dTimeHours = 0.0;
if(!m_pPlaybackController->getPropertyTimeAt(sPropertyId, nFrameIndex, dTimeHours)) {
bComplete = false;
break;
}
vecTimes.append(dTimeHours);
}
// 先完整取得数值再成组格式化;时间很接近时只增加必要精度,仍用帧索引绑定。
if(bComplete) {
const QStringList listTexts = nmNumericDisplayFormat::formatDistinct(vecTimes);
for(int nFrameIndex = 0; nFrameIndex < vecTimes.size(); ++nFrameIndex) {
m_pTimeCombo->addItem(tr("t = %1 h").arg(listTexts[nFrameIndex]), nFrameIndex);
m_pTimeCombo->setItemData(nFrameIndex,
tr("t = %1 h").arg(nmNumericDisplayFormat::fullPrecision(vecTimes[nFrameIndex])), Qt::ToolTipRole);
}
}
m_pTimeCombo->blockSignals(bBlocked);
m_sDisplayedTimePropertyId = bComplete ? sPropertyId : QString();
m_nDisplayedTimeFrameCount = bComplete ? nFrameCount : -1;
}
void nmWxPebiResultControlBar::clearTimeItems()
{
const bool bBlocked = m_pTimeCombo->blockSignals(true);
m_pTimeCombo->clear();
m_pTimeCombo->blockSignals(bBlocked);
m_pTimeCombo->setEnabled(false);
m_pTimeCombo->setToolTip(tr("Result time"));
m_sDisplayedTimePropertyId.clear();
m_nDisplayedTimeFrameCount = -1;
}
int nmWxPebiResultControlBar::findItemByData(
QComboBox* pComboBox,
const QVariant& oData) const
{
if(pComboBox == nullptr) {
return -1;
}
for(int nIndex = 0; nIndex < pComboBox->count(); ++nIndex) {
if(pComboBox->itemData(nIndex) == oData) {
return nIndex;
}
}
return -1;
}