#include "nmWxPebiResultAnimationExportDlg.h" #include #include #include #include #include #include #include #include #include #include #include namespace { QString exportDialogIconPath(const QString& sFileName) { const QString sApplicationDir = QCoreApplication::applicationDirPath(); return sApplicationDir.section('/', 0, -2) + "/Res/Icon/" + sFileName; } } nmWxPebiResultAnimationExportDlg::nmWxPebiResultAnimationExportDlg( const QVector& vecFrameTimesHours, const QSize& oCurrentViewportSize, QWidget* pParent) : QDialog(pParent), m_vecFrameTimesHours(vecFrameTimesHours), m_oCurrentViewportSize(oCurrentViewportSize), m_pFpsSpinBox(nullptr), m_pStartFrameCombo(nullptr), m_pEndFrameCombo(nullptr), m_pSamplingIntervalSpinBox(nullptr), m_pResolutionCombo(nullptr), m_pWidthSpinBox(nullptr), m_pHeightSpinBox(nullptr), m_pOutputFrameCountLabel(nullptr), m_pEstimatedDurationLabel(nullptr), m_pErrorLabel(nullptr) { createUi(); populateFrameItems(); onResolutionPresetChanged(m_pResolutionCombo->currentIndex()); onFrameRangeChanged(); } nmPebiResultAnimationExportSettings nmWxPebiResultAnimationExportDlg::getSettings() const { return m_oAcceptedSettings; } void nmWxPebiResultAnimationExportDlg::onFrameRangeChanged() { const int nStartIndex = m_pStartFrameCombo->currentIndex(); if(nStartIndex >= 0 && m_pEndFrameCombo->currentIndex() < nStartIndex) { const bool bBlocked = m_pEndFrameCombo->blockSignals(true); m_pEndFrameCombo->setCurrentIndex(nStartIndex); m_pEndFrameCombo->blockSignals(bBlocked); } const int nRangeLength = qMax(1, m_pEndFrameCombo->currentIndex() - m_pStartFrameCombo->currentIndex()); m_pSamplingIntervalSpinBox->setMaximum(nRangeLength); updateSummary(); } void nmWxPebiResultAnimationExportDlg::onResolutionPresetChanged( int nIndex) { Q_UNUSED(nIndex); const ResolutionPreset ePreset = static_cast( m_pResolutionCombo->itemData( m_pResolutionCombo->currentIndex()).toInt()); int nWidth = m_oCurrentViewportSize.width(); int nHeight = m_oCurrentViewportSize.height(); if(ePreset == Resolution_720p) { nWidth = 1280; nHeight = 720; } else if(ePreset == Resolution_1080p) { nWidth = 1920; nHeight = 1080; } if(nWidth <= 0 || nHeight <= 0) { nWidth = 1280; nHeight = 720; } const bool bCustom = ePreset == Resolution_Custom; if(!bCustom) { m_pWidthSpinBox->setValue(normalizeEvenDimension(nWidth)); m_pHeightSpinBox->setValue(normalizeEvenDimension(nHeight)); } m_pWidthSpinBox->setEnabled(bCustom); m_pHeightSpinBox->setEnabled(bCustom); updateSummary(); } void nmWxPebiResultAnimationExportDlg::normalizeCustomDimensions() { // editingFinished后再回写,避免valueChanged期间干扰用户键盘输入。 m_pWidthSpinBox->setValue( normalizeEvenDimension(m_pWidthSpinBox->value())); m_pHeightSpinBox->setValue( normalizeEvenDimension(m_pHeightSpinBox->value())); updateSummary(); } void nmWxPebiResultAnimationExportDlg::updateSummary() { const nmPebiResultAnimationExportSettings oSettings = collectSettings(); const int nOutputFrameCount = oSettings.getOutputFrameCount(); m_pOutputFrameCountLabel->setText( QString::number(nOutputFrameCount)); const double dDuration = oSettings.m_nFramesPerSecond > 0 ? static_cast(nOutputFrameCount) / static_cast(oSettings.m_nFramesPerSecond) : 0.0; m_pEstimatedDurationLabel->setText( tr("%1 s").arg(QString::number(dDuration, 'g', 6))); m_pErrorLabel->clear(); } void nmWxPebiResultAnimationExportDlg::acceptSettings() { // 接受前再规范化一次,覆盖按钮点击与焦点事件顺序差异。 normalizeCustomDimensions(); const nmPebiResultAnimationExportSettings oSettings = collectSettings(); if(!oSettings.isValid(m_vecFrameTimesHours.size())) { m_pErrorLabel->setText( tr("The animation export settings are invalid.")); return; } m_oAcceptedSettings = oSettings; accept(); } void nmWxPebiResultAnimationExportDlg::createUi() { setWindowTitle(tr("Export Animation")); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setMinimumWidth(430); QVBoxLayout* pMainLayout = new QVBoxLayout(this); pMainLayout->setContentsMargins(12, 10, 12, 10); pMainLayout->setSpacing(8); QGridLayout* pGrid = new QGridLayout(); pGrid->setHorizontalSpacing(8); pGrid->setVerticalSpacing(6); int nRow = 0; m_pFpsSpinBox = new QSpinBox(this); m_pFpsSpinBox->setRange(1, 5000); m_pFpsSpinBox->setValue(15); m_pFpsSpinBox->setSuffix(tr(" FPS")); pGrid->addWidget(new QLabel(tr("Frame rate"), this), nRow, 0); pGrid->addWidget(m_pFpsSpinBox, nRow++, 1, 1, 2); m_pStartFrameCombo = new QComboBox(this); pGrid->addWidget(new QLabel(tr("Start frame"), this), nRow, 0); pGrid->addWidget(m_pStartFrameCombo, nRow++, 1, 1, 2); m_pEndFrameCombo = new QComboBox(this); pGrid->addWidget(new QLabel(tr("End frame"), this), nRow, 0); pGrid->addWidget(m_pEndFrameCombo, nRow++, 1, 1, 2); m_pSamplingIntervalSpinBox = new QSpinBox(this); m_pSamplingIntervalSpinBox->setRange(1, 1); pGrid->addWidget(new QLabel(tr("Sampling interval"), this), nRow, 0); pGrid->addWidget(m_pSamplingIntervalSpinBox, nRow++, 1, 1, 2); m_pResolutionCombo = new QComboBox(this); m_pResolutionCombo->addItem(tr("Current view"), Resolution_CurrentView); m_pResolutionCombo->addItem(QString::fromLatin1("1280 x 720"), Resolution_720p); m_pResolutionCombo->addItem(QString::fromLatin1("1920 x 1080"), Resolution_1080p); m_pResolutionCombo->addItem(tr("Custom"), Resolution_Custom); pGrid->addWidget(new QLabel(tr("Resolution"), this), nRow, 0); pGrid->addWidget(m_pResolutionCombo, nRow++, 1, 1, 2); m_pWidthSpinBox = new QSpinBox(this); m_pHeightSpinBox = new QSpinBox(this); m_pWidthSpinBox->setRange(2, 8192); m_pHeightSpinBox->setRange(2, 8192); m_pWidthSpinBox->setSingleStep(2); m_pHeightSpinBox->setSingleStep(2); QHBoxLayout* pCustomSizeLayout = new QHBoxLayout(); pCustomSizeLayout->setContentsMargins(0, 0, 0, 0); pCustomSizeLayout->setSpacing(6); pCustomSizeLayout->addWidget(m_pWidthSpinBox); pCustomSizeLayout->addWidget(new QLabel(QString::fromLatin1("x"), this)); pCustomSizeLayout->addWidget(m_pHeightSpinBox); pGrid->addWidget(new QLabel(tr("Output size"), this), nRow, 0); pGrid->addLayout(pCustomSizeLayout, nRow++, 1, 1, 2); m_pOutputFrameCountLabel = new QLabel(this); pGrid->addWidget(new QLabel(tr("Output frames"), this), nRow, 0); pGrid->addWidget(m_pOutputFrameCountLabel, nRow++, 1, 1, 2); m_pEstimatedDurationLabel = new QLabel(this); pGrid->addWidget(new QLabel(tr("Estimated duration"), this), nRow, 0); pGrid->addWidget(m_pEstimatedDurationLabel, nRow++, 1, 1, 2); pGrid->setColumnStretch(1, 1); pMainLayout->addLayout(pGrid); m_pErrorLabel = new QLabel(this); m_pErrorLabel->setStyleSheet("QLabel { color: #a32626; }"); m_pErrorLabel->setWordWrap(true); pMainLayout->addWidget(m_pErrorLabel); QDialogButtonBox* pButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); QPushButton* pOkButton = pButtonBox->button(QDialogButtonBox::Ok); QPushButton* pCancelButton = pButtonBox->button(QDialogButtonBox::Cancel); pOkButton->setIcon(QIcon(exportDialogIconPath("OK.png"))); pCancelButton->setIcon(QIcon(exportDialogIconPath("Cancel.png"))); pOkButton->setIconSize(QSize(16, 16)); pCancelButton->setIconSize(QSize(16, 16)); pOkButton->setMinimumWidth(72); pCancelButton->setMinimumWidth(72); pMainLayout->addWidget(pButtonBox); connect(m_pStartFrameCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onFrameRangeChanged())); connect(m_pEndFrameCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onFrameRangeChanged())); connect(m_pSamplingIntervalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateSummary())); connect(m_pFpsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateSummary())); connect(m_pResolutionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onResolutionPresetChanged(int))); connect(m_pWidthSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateSummary())); connect(m_pHeightSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateSummary())); connect(m_pWidthSpinBox, SIGNAL(editingFinished()), this, SLOT(normalizeCustomDimensions())); connect(m_pHeightSpinBox, SIGNAL(editingFinished()), this, SLOT(normalizeCustomDimensions())); connect(pButtonBox, SIGNAL(accepted()), this, SLOT(acceptSettings())); connect(pButtonBox, SIGNAL(rejected()), this, SLOT(reject())); } void nmWxPebiResultAnimationExportDlg::populateFrameItems() { for(int nFrameIndex = 0; nFrameIndex < m_vecFrameTimesHours.size(); ++nFrameIndex) { const QString sText = tr("%1 h").arg(QString::number( m_vecFrameTimesHours[nFrameIndex], 'g', 8)); m_pStartFrameCombo->addItem(sText, nFrameIndex); m_pEndFrameCombo->addItem(sText, nFrameIndex); } if(!m_vecFrameTimesHours.isEmpty()) { m_pStartFrameCombo->setCurrentIndex(0); m_pEndFrameCombo->setCurrentIndex( m_vecFrameTimesHours.size() - 1); } } nmPebiResultAnimationExportSettings nmWxPebiResultAnimationExportDlg::collectSettings() const { nmPebiResultAnimationExportSettings oSettings; oSettings.m_nFramesPerSecond = m_pFpsSpinBox->value(); oSettings.m_nStartFrameIndex = m_pStartFrameCombo->itemData( m_pStartFrameCombo->currentIndex()).toInt(); oSettings.m_nEndFrameIndex = m_pEndFrameCombo->itemData( m_pEndFrameCombo->currentIndex()).toInt(); oSettings.m_nSamplingInterval = m_pSamplingIntervalSpinBox->value(); oSettings.m_nOutputWidth = normalizeEvenDimension(m_pWidthSpinBox->value()); oSettings.m_nOutputHeight = normalizeEvenDimension(m_pHeightSpinBox->value()); return oSettings; } int nmWxPebiResultAnimationExportDlg::normalizeEvenDimension(int nValue) { nValue = qBound(2, nValue, 8192); if((nValue % 2) != 0) { ++nValue; } return qMin(nValue, 8192); }