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

#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;
}