#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 #include #include #include #include #include #include #include #include #include #include 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( 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 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( 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 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& 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(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( nmVTKMeshLayer::DisplayMode_Point))); m_pDisplayModeCombo->addItem( tr("Wireframe"), QVariant(static_cast( nmVTKMeshLayer::DisplayMode_Wireframe))); m_pDisplayModeCombo->addItem( tr("Surface"), QVariant(static_cast( nmVTKMeshLayer::DisplayMode_Surface))); m_pDisplayModeCombo->addItem( tr("Contour"), QVariant(static_cast( 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( 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 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; }