diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index c0659bfb..75bee651 100644 Binary files a/Bin/Config/Lang/cn/nmNum_cn.qm and b/Bin/Config/Lang/cn/nmNum_cn.qm differ diff --git a/Bin/Config/Lang/cn/nmNum_cn.ts b/Bin/Config/Lang/cn/nmNum_cn.ts index c445156b..513cea33 100644 --- a/Bin/Config/Lang/cn/nmNum_cn.ts +++ b/Bin/Config/Lang/cn/nmNum_cn.ts @@ -2870,11 +2870,12 @@ Reason: %1 Below min低于最小值 Above max高于最大值 Color map色带 + Color mapping颜色映射 + Out-of-range colors越界值颜色 Minimum最小值 Maximum最大值 - Unit单位 - Dimensionless无量纲 Minimum and maximum must be finite numbers.最小值和最大值必须是有限数。 + Unable to convert the range to the selected unit.无法将范围换算为所选单位。 Minimum must be less than maximum.最小值必须小于最大值。 Unable to read the committed color settings.无法读取已提交的颜色设置。 diff --git a/Bin/Res/Icon/NmRestorePropertyRange.png b/Bin/Res/Icon/NmRestorePropertyRange.png new file mode 100644 index 00000000..9d1e81e6 Binary files /dev/null and b/Bin/Res/Icon/NmRestorePropertyRange.png differ diff --git a/Include/nmNum/nmSubWxs/nmWxPebiResultColorScaleSettingsDlg.h b/Include/nmNum/nmSubWxs/nmWxPebiResultColorScaleSettingsDlg.h index 02e1ed83..4bd9922b 100644 --- a/Include/nmNum/nmSubWxs/nmWxPebiResultColorScaleSettingsDlg.h +++ b/Include/nmNum/nmSubWxs/nmWxPebiResultColorScaleSettingsDlg.h @@ -14,6 +14,7 @@ class QLabel; class QLineEdit; class QPushButton; class QToolButton; +class iUnitGroup; class nmPebiResultSceneController; /** @@ -54,6 +55,10 @@ private slots: void onRestorePropertyRangeClicked(); /** @brief 用户编辑任一上下限后显式进入手工范围模式。 */ void onManualRangeEdited(const QString& sText); + /** @brief 色带改变后刷新未启用越界颜色的端点预览。 */ + void onColorMapChanged(int nIndex); + /** @brief 切换输入单位并保持两个输入值表示的物理量不变。 */ + void onDisplayUnitChanged(int nIndex); /** @brief 根据复选框同步低于范围颜色按钮状态。 */ void onBelowRangeToggled(bool bChecked); /** @brief 根据复选框同步高于范围颜色按钮状态。 */ @@ -72,8 +77,10 @@ private: void createUi(); /** @brief 从 Scene 读取初值并记录不可变的预期身份。 */ bool initializeFromScene(); - /** @brief 将设置和值域写入控件,不触发手工范围编辑状态。 */ - void populateEditor(const nmPebiResultDisplaySettings& oSettings, + /** @brief 从属性目录和全局单位表初始化当前对话框的单位选择。 */ + void initializeUnitEditor(); + /** @brief 换算成功后将设置和值域写入控件,不触发手工编辑状态。 */ + bool populateEditor(const nmPebiResultDisplaySettings& oSettings, const double dVisibleRange[2]); /** @brief 解析编辑值并构造待提交纯值设置。 */ bool collectEditedSettings( @@ -85,6 +92,18 @@ private: QString formatValue(double dValue) const; /** @brief 使用 C locale 严格解析有限 double。 */ bool parseFiniteValue(const QString& sText, double& dValue) const; + /** @brief 使用全局单位表换算单个有限值。 */ + bool convertUnitValue(double dSourceValue, + const QString& sSourceUnit, + const QString& sDestinationUnit, + double& dDestinationValue) const; + /** @brief 将基础单位范围换算到当前对话框显示单位。 */ + bool convertBaseRangeToDisplay(const double dBaseRange[2], + double dDisplayRange[2]) const; + /** @brief 根据当前单位刷新两个范围输入框的提示文字。 */ + void refreshRangeToolTips(); + /** @brief 根据色带、范围模式和启用状态刷新两个颜色按钮。 */ + void refreshRangeColorButtons(); /** @brief 更新颜色按钮色块。 */ void updateColorButton(QPushButton* pButton, const QColor& oColor); @@ -95,16 +114,21 @@ private: quint64 m_nExpectedBindingRevision; ///< 打开时捕获的 Scene 成功绑定序号。 QString m_sExpectedSnapshotId; ///< 打开时捕获的快照稳定 ID。 QString m_sExpectedPropertyId; ///< 打开时捕获的属性稳定 ID。 - QString m_sDisplayUnit; ///< 当前属性默认显示单位,仅用于标签。 + QString m_sBaseUnit; ///< Catalog 中的数据层基础单位。 + QString m_sDisplayUnit; ///< 当前对话框实例选择的输入显示单位。 + iUnitGroup* m_pUnitGroup; ///< 全局单位框架拥有,未找到时为空。 double m_dPropertyRange[2]; ///< 快照完整业务范围,回退按钮使用。 nmPebiResultDisplaySettings m_oBaselineSettings; ///< 最近成功 Apply 的基线。 + QColor m_oEditedBelowRangeColor; ///< 尚未提交的低于范围颜色。 + QColor m_oEditedAboveRangeColor; ///< 尚未提交的高于范围颜色。 bool m_bManualRangeEdited; ///< 仅用户编辑或已有手工范围时为 true。 + bool m_bRangeValuesEditedByUser; ///< 本次基线后用户是否亲自编辑过数值。 bool m_bInitialized; ///< Scene 初值是否完整读取成功。 QComboBox* m_pColorMapCombo; ///< 带渐变预览和稳定 ID 的色带框。 QLineEdit* m_pMinimumEdit; ///< 支持科学计数法的最小值文字框。 QLineEdit* m_pMaximumEdit; ///< 支持科学计数法的最大值文字框。 - QLabel* m_pUnitLabel; ///< 当前属性默认显示单位;无量纲时显示“无量纲”。 + QComboBox* m_pUnitCombo; ///< 两个范围输入框共用的临时显示单位。 QToolButton* m_pRestoreRangeButton; ///< 恢复完整业务范围按钮。 QCheckBox* m_pLogarithmicCheck; ///< Log10 映射开关。 QLabel* m_pLogReasonLabel; ///< Log 不可用时的明确原因。 diff --git a/Src/nmNum/nmSubWxs/nmWxPebiResultColorScaleSettingsDlg.cpp b/Src/nmNum/nmSubWxs/nmWxPebiResultColorScaleSettingsDlg.cpp index c23bf501..920dabcd 100644 --- a/Src/nmNum/nmSubWxs/nmWxPebiResultColorScaleSettingsDlg.cpp +++ b/Src/nmNum/nmSubWxs/nmWxPebiResultColorScaleSettingsDlg.cpp @@ -1,6 +1,8 @@ #include "nmWxPebiResultColorScaleSettingsDlg.h" -#include "nmPebiResultPropertyDisplayText.h" +#include "iUnitGroup.h" +#include "iUnitHelper.h" +#include "nmPebiResultPropertyCatalog.h" #include "nmPebiResultSceneController.h" #include "nmVTKColorMapCatalog.h" @@ -9,14 +11,17 @@ #include #include #include -#include +#include +#include #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -80,12 +85,14 @@ nmWxPebiResultColorScaleSettingsDlg( : QDialog(pParent), m_pSceneController(pSceneController), m_nExpectedBindingRevision(0), + m_pUnitGroup(nullptr), m_bManualRangeEdited(false), + m_bRangeValuesEditedByUser(false), m_bInitialized(false), m_pColorMapCombo(nullptr), m_pMinimumEdit(nullptr), m_pMaximumEdit(nullptr), - m_pUnitLabel(nullptr), + m_pUnitCombo(nullptr), m_pRestoreRangeButton(nullptr), m_pLogarithmicCheck(nullptr), m_pLogReasonLabel(nullptr), @@ -104,7 +111,7 @@ nmWxPebiResultColorScaleSettingsDlg( m_bInitialized = initializeFromScene(); m_pApplyButton->setEnabled(m_bInitialized); m_pOkButton->setEnabled(m_bInitialized); - if(!m_bInitialized) { + if(!m_bInitialized && m_pErrorLabel->text().isEmpty()) { setErrorText(tr("The result color settings are unavailable.")); } } @@ -128,10 +135,17 @@ void nmWxPebiResultColorScaleSettingsDlg::onOkClicked() void nmWxPebiResultColorScaleSettingsDlg::onRestorePropertyRangeClicked() { + double dDisplayRange[2] = { 0.0, 0.0 }; + if(!convertBaseRangeToDisplay(m_dPropertyRange, dDisplayRange)) { + setErrorText(tr("Unable to convert the range to the selected unit.")); + return; + } m_bManualRangeEdited = false; - m_pMinimumEdit->setText(formatValue(m_dPropertyRange[0])); - m_pMaximumEdit->setText(formatValue(m_dPropertyRange[1])); + m_bRangeValuesEditedByUser = false; + m_pMinimumEdit->setText(formatValue(dDisplayRange[0])); + m_pMaximumEdit->setText(formatValue(dDisplayRange[1])); refreshLogAvailability(); + refreshRangeColorButtons(); setErrorText(QString()); } @@ -140,50 +154,114 @@ void nmWxPebiResultColorScaleSettingsDlg::onManualRangeEdited( { Q_UNUSED(sText); m_bManualRangeEdited = true; + m_bRangeValuesEditedByUser = true; refreshLogAvailability(); + refreshRangeColorButtons(); +} + +void nmWxPebiResultColorScaleSettingsDlg::onColorMapChanged(int nIndex) +{ + Q_UNUSED(nIndex); + refreshRangeColorButtons(); +} + +void nmWxPebiResultColorScaleSettingsDlg::onDisplayUnitChanged(int nIndex) +{ + if(nIndex < 0) { + return; + } + const QString sNewUnit = m_pUnitCombo->itemText(nIndex); + if(sNewUnit == m_sDisplayUnit) { + return; + } + + double dOldDisplayRange[2] = { 0.0, 0.0 }; + double dBaseRange[2] = { 0.0, 0.0 }; + double dNewDisplayRange[2] = { 0.0, 0.0 }; + QString sError; + if(!parseFiniteValue(m_pMinimumEdit->text(), dOldDisplayRange[0]) || + !parseFiniteValue(m_pMaximumEdit->text(), dOldDisplayRange[1])) { + sError = tr("Minimum and maximum must be finite numbers."); + } else if(!convertUnitValue( + dOldDisplayRange[0], m_sDisplayUnit, + m_sBaseUnit, dBaseRange[0]) || + !convertUnitValue( + dOldDisplayRange[1], m_sDisplayUnit, + m_sBaseUnit, dBaseRange[1]) || + !convertUnitValue( + dBaseRange[0], m_sBaseUnit, + sNewUnit, dNewDisplayRange[0]) || + !convertUnitValue( + dBaseRange[1], m_sBaseUnit, + sNewUnit, dNewDisplayRange[1])) { + sError = tr("Unable to convert the range to the selected unit."); + } + + if(!sError.isEmpty()) { + const bool bSignalsBlocked = m_pUnitCombo->blockSignals(true); + m_pUnitCombo->setCurrentIndex( + m_pUnitCombo->findText(m_sDisplayUnit)); + m_pUnitCombo->blockSignals(bSignalsBlocked); + setErrorText(sError); + return; + } + + m_sDisplayUnit = sNewUnit; + m_pMinimumEdit->setText(formatValue(dNewDisplayRange[0])); + m_pMaximumEdit->setText(formatValue(dNewDisplayRange[1])); + refreshRangeToolTips(); + refreshLogAvailability(); + setErrorText(QString()); } void nmWxPebiResultColorScaleSettingsDlg::onBelowRangeToggled(bool bChecked) { - m_pBelowColorButton->setEnabled(bChecked); + Q_UNUSED(bChecked); + refreshRangeColorButtons(); } void nmWxPebiResultColorScaleSettingsDlg::onAboveRangeToggled(bool bChecked) { - m_pAboveColorButton->setEnabled(bChecked); + Q_UNUSED(bChecked); + refreshRangeColorButtons(); } void nmWxPebiResultColorScaleSettingsDlg::onChooseBelowRangeColor() { QColor oColor = QColorDialog::getColor( - m_oBaselineSettings.m_oBelowRangeColor, this, + m_oEditedBelowRangeColor, this, tr("Below minimum color")); if(oColor.isValid()) { oColor.setAlpha(255); - m_oBaselineSettings.m_oBelowRangeColor = oColor; - updateColorButton(m_pBelowColorButton, oColor); + m_oEditedBelowRangeColor = oColor; + refreshRangeColorButtons(); } } void nmWxPebiResultColorScaleSettingsDlg::onChooseAboveRangeColor() { QColor oColor = QColorDialog::getColor( - m_oBaselineSettings.m_oAboveRangeColor, this, + m_oEditedAboveRangeColor, this, tr("Above maximum color")); if(oColor.isValid()) { oColor.setAlpha(255); - m_oBaselineSettings.m_oAboveRangeColor = oColor; - updateColorButton(m_pAboveColorButton, oColor); + m_oEditedAboveRangeColor = oColor; + refreshRangeColorButtons(); } } void nmWxPebiResultColorScaleSettingsDlg::refreshLogAvailability() { - double dMinimum = 0.0; + double dDisplayMinimum = 0.0; + double dBaseMinimum = 0.0; const bool bMinimumValid = - parseFiniteValue(m_pMinimumEdit->text(), dMinimum); + parseFiniteValue(m_pMinimumEdit->text(), dDisplayMinimum); + const bool bMinimumConverted = bMinimumValid && + convertUnitValue(dDisplayMinimum, m_sDisplayUnit, + m_sBaseUnit, dBaseMinimum); const bool bPropertyPositive = m_dPropertyRange[0] > 0.0; - const bool bDisplayPositive = bMinimumValid && dMinimum > 0.0; + const bool bDisplayPositive = + bMinimumConverted && dBaseMinimum > 0.0; const bool bAvailable = bPropertyPositive && bDisplayPositive; if(!bAvailable && m_pLogarithmicCheck->isChecked()) { // 禁用前先清除不可提交的 Log 状态,避免用户被锁在失败循环中。 @@ -193,25 +271,27 @@ void nmWxPebiResultColorScaleSettingsDlg::refreshLogAvailability() m_pLogarithmicCheck->blockSignals(bSignalsBlocked); } m_pLogarithmicCheck->setEnabled(bAvailable); + QString sReason; if(!bPropertyPositive) { - m_pLogReasonLabel->setText( - tr("Logarithmic scale requires a positive property range.")); + sReason = tr( + "Logarithmic scale requires a positive property range."); } else if(!bDisplayPositive) { - m_pLogReasonLabel->setText( - tr("Logarithmic scale requires a positive minimum.")); - } else { - m_pLogReasonLabel->clear(); + sReason = tr( + "Logarithmic scale requires a positive minimum."); } + m_pLogReasonLabel->setText(sReason); + m_pLogReasonLabel->setVisible(!sReason.isEmpty()); } void nmWxPebiResultColorScaleSettingsDlg::createUi() { setWindowTitle(tr("Color settings for current property")); setModal(true); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); m_pColorMapCombo = new QComboBox(this); m_pColorMapCombo->setIconSize(QSize(128, 16)); - m_pColorMapCombo->setMinimumWidth(260); + m_pColorMapCombo->setMinimumWidth(280); const QStringList listColorMapIds = nmVTKColorMapCatalog::getColorMapIds(); for(int nIndex = 0; nIndex < listColorMapIds.size(); ++nIndex) { @@ -224,61 +304,92 @@ void nmWxPebiResultColorScaleSettingsDlg::createUi() m_pMinimumEdit = new QLineEdit(this); m_pMaximumEdit = new QLineEdit(this); - m_pUnitLabel = new QLabel(this); + m_pMinimumEdit->setMinimumWidth(220); + m_pMinimumEdit->setMaximumWidth(250); + m_pMaximumEdit->setMinimumWidth(220); + m_pMaximumEdit->setMaximumWidth(250); + m_pUnitCombo = new QComboBox(this); + m_pUnitCombo->setMinimumWidth(80); + m_pUnitCombo->setMaximumWidth(140); + m_pUnitCombo->hide(); m_pMinimumEdit->setAlignment(Qt::AlignRight); m_pMaximumEdit->setAlignment(Qt::AlignRight); m_pRestoreRangeButton = new QToolButton(this); m_pRestoreRangeButton->setAutoRaise(true); - m_pRestoreRangeButton->setFixedSize(24, 24); - m_pRestoreRangeButton->setIconSize(QSize(16, 16)); + m_pRestoreRangeButton->setFixedSize(36, 36); + m_pRestoreRangeButton->setIconSize(QSize(32, 32)); m_pRestoreRangeButton->setIcon(QIcon(resultColorIconPath( - QString::fromLatin1("Undo.png")))); + QString::fromLatin1("NmRestorePropertyRange.png")))); m_pRestoreRangeButton->setToolTip(tr("Restore property range")); - QHBoxLayout* pMinimumLayout = new QHBoxLayout(); - pMinimumLayout->setContentsMargins(0, 0, 0, 0); - pMinimumLayout->addWidget(m_pMinimumEdit, 1); - pMinimumLayout->addWidget(m_pRestoreRangeButton); - QWidget* pMinimumWidget = new QWidget(this); - pMinimumWidget->setLayout(pMinimumLayout); - m_pLogarithmicCheck = new QCheckBox(tr("Logarithmic scale"), this); m_pLogReasonLabel = new QLabel(this); m_pLogReasonLabel->setWordWrap(true); m_pLogReasonLabel->setStyleSheet( QString::fromLatin1("color: #666666;")); + m_pLogReasonLabel->hide(); m_pBelowRangeCheck = new QCheckBox(tr("Below min"), this); m_pBelowColorButton = new QPushButton(this); m_pBelowColorButton->setFixedSize(44, 22); - QHBoxLayout* pBelowLayout = new QHBoxLayout(); - pBelowLayout->setContentsMargins(0, 0, 0, 0); - pBelowLayout->addWidget(m_pBelowRangeCheck); - pBelowLayout->addWidget(m_pBelowColorButton); - pBelowLayout->addStretch(); - QWidget* pBelowWidget = new QWidget(this); - pBelowWidget->setLayout(pBelowLayout); + m_pBelowColorButton->setEnabled(false); + m_pBelowColorButton->setToolTip(tr("Below minimum color")); m_pAboveRangeCheck = new QCheckBox(tr("Above max"), this); m_pAboveColorButton = new QPushButton(this); m_pAboveColorButton->setFixedSize(44, 22); - QHBoxLayout* pAboveLayout = new QHBoxLayout(); - pAboveLayout->setContentsMargins(0, 0, 0, 0); - pAboveLayout->addWidget(m_pAboveRangeCheck); - pAboveLayout->addWidget(m_pAboveColorButton); - pAboveLayout->addStretch(); - QWidget* pAboveWidget = new QWidget(this); - pAboveWidget->setLayout(pAboveLayout); - - QFormLayout* pFormLayout = new QFormLayout(); - pFormLayout->addRow(tr("Color map"), m_pColorMapCombo); - pFormLayout->addRow(tr("Minimum"), pMinimumWidget); - pFormLayout->addRow(tr("Maximum"), m_pMaximumEdit); - pFormLayout->addRow(tr("Unit"), m_pUnitLabel); - pFormLayout->addRow(QString(), m_pLogarithmicCheck); - pFormLayout->addRow(QString(), m_pLogReasonLabel); - pFormLayout->addRow(QString(), pBelowWidget); - pFormLayout->addRow(QString(), pAboveWidget); + m_pAboveColorButton->setEnabled(false); + m_pAboveColorButton->setToolTip(tr("Above maximum color")); + + QGroupBox* pMappingGroup = new QGroupBox(tr("Color mapping"), this); + QGridLayout* pMappingLayout = new QGridLayout(pMappingGroup); + pMappingLayout->setContentsMargins(10, 16, 10, 10); + pMappingLayout->setHorizontalSpacing(8); + pMappingLayout->setVerticalSpacing(6); + QLabel* pColorMapLabel = new QLabel(tr("Color map"), pMappingGroup); + QLabel* pMinimumLabel = new QLabel(tr("Minimum"), pMappingGroup); + QLabel* pMaximumLabel = new QLabel(tr("Maximum"), pMappingGroup); + pColorMapLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + pMinimumLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + pMaximumLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + const int nFieldLabelWidth = qMax( + pColorMapLabel->sizeHint().width(), + qMax(pMinimumLabel->sizeHint().width(), + pMaximumLabel->sizeHint().width())); + pMappingLayout->setColumnMinimumWidth(0, nFieldLabelWidth); + pMappingLayout->setColumnMinimumWidth(1, 220); + pMappingLayout->addWidget(pColorMapLabel, 0, 0); + pMappingLayout->addWidget(m_pColorMapCombo, 0, 1, 1, 4); + pMappingLayout->addWidget(pMinimumLabel, 1, 0); + pMappingLayout->addWidget(m_pMinimumEdit, 1, 1); + pMappingLayout->addWidget(m_pUnitCombo, 1, 2, 2, 1, + Qt::AlignVCenter); + pMappingLayout->addWidget(m_pRestoreRangeButton, 1, 4, 2, 1, + Qt::AlignCenter); + pMappingLayout->addWidget(pMaximumLabel, 2, 0); + pMappingLayout->addWidget(m_pMaximumEdit, 2, 1); + pMappingLayout->addWidget(m_pLogarithmicCheck, 3, 1, 1, 4); + pMappingLayout->addWidget(m_pLogReasonLabel, 4, 1, 1, 4); + pMappingLayout->setColumnStretch(0, 0); + pMappingLayout->setColumnStretch(1, 0); + pMappingLayout->setColumnStretch(2, 0); + pMappingLayout->setColumnStretch(3, 1); + pMappingLayout->setColumnStretch(4, 0); + + QGroupBox* pRangeColorGroup = new QGroupBox( + tr("Out-of-range colors"), this); + QGridLayout* pRangeColorLayout = new QGridLayout(pRangeColorGroup); + pRangeColorLayout->setContentsMargins(10, 16, 10, 10); + pRangeColorLayout->setHorizontalSpacing(12); + pRangeColorLayout->setVerticalSpacing(6); + pRangeColorLayout->addWidget(m_pBelowRangeCheck, 0, 1); + pRangeColorLayout->addWidget(m_pBelowColorButton, 0, 2, Qt::AlignLeft); + pRangeColorLayout->addWidget(m_pAboveRangeCheck, 1, 1); + pRangeColorLayout->addWidget(m_pAboveColorButton, 1, 2, Qt::AlignLeft); + pRangeColorLayout->setColumnStretch(0, 1); + pRangeColorLayout->setColumnStretch(1, 0); + pRangeColorLayout->setColumnStretch(2, 0); + pRangeColorLayout->setColumnStretch(3, 1); m_pErrorLabel = new QLabel(this); m_pErrorLabel->setWordWrap(true); @@ -291,18 +402,41 @@ void nmWxPebiResultColorScaleSettingsDlg::createUi() Qt::Horizontal, this); m_pApplyButton = pButtonBox->button(QDialogButtonBox::Apply); m_pOkButton = pButtonBox->button(QDialogButtonBox::Ok); + QPushButton* pCancelButton = + pButtonBox->button(QDialogButtonBox::Cancel); + const QSize oFooterIconSize(16, 16); + m_pOkButton->setIcon(QIcon(resultColorIconPath( + QString::fromLatin1("OK.png")))); + pCancelButton->setIcon(QIcon(resultColorIconPath( + QString::fromLatin1("Cancel.png")))); + m_pOkButton->setMinimumWidth(72); + pCancelButton->setMinimumWidth(72); + m_pApplyButton->setMinimumWidth(72); + m_pOkButton->setIconSize(oFooterIconSize); + pCancelButton->setIconSize(oFooterIconSize); + if(pButtonBox->layout() != nullptr) { + pButtonBox->layout()->setSpacing(6); + } connect(m_pApplyButton, SIGNAL(clicked()), this, SLOT(onApplyClicked())); connect(m_pOkButton, SIGNAL(clicked()), this, SLOT(onOkClicked())); - connect(pButtonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), - this, SLOT(reject())); + connect(pCancelButton, SIGNAL(clicked()), this, SLOT(reject())); QVBoxLayout* pMainLayout = new QVBoxLayout(this); - pMainLayout->addLayout(pFormLayout); - pMainLayout->addWidget(m_pErrorLabel); - pMainLayout->addWidget(pButtonBox); - setMinimumWidth(420); + pMainLayout->setContentsMargins(12, 10, 12, 0); + pMainLayout->setSpacing(8); + pMainLayout->addWidget(pMappingGroup); + pMainLayout->addWidget(pRangeColorGroup); + QHBoxLayout* pFooterLayout = new QHBoxLayout(); + pFooterLayout->setContentsMargins(4, 2, 4, 5); + pFooterLayout->setSpacing(8); + m_pErrorLabel->setSizePolicy( + QSizePolicy::Expanding, QSizePolicy::Preferred); + pFooterLayout->addWidget(m_pErrorLabel, 1); + pFooterLayout->addWidget(pButtonBox); + pMainLayout->addLayout(pFooterLayout); + setMinimumWidth(470); connect(m_pRestoreRangeButton, SIGNAL(clicked()), this, SLOT(onRestorePropertyRangeClicked())); @@ -310,6 +444,10 @@ void nmWxPebiResultColorScaleSettingsDlg::createUi() this, SLOT(onManualRangeEdited(QString))); connect(m_pMaximumEdit, SIGNAL(textEdited(QString)), this, SLOT(onManualRangeEdited(QString))); + connect(m_pColorMapCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(onColorMapChanged(int))); + connect(m_pUnitCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(onDisplayUnitChanged(int))); connect(m_pBelowRangeCheck, SIGNAL(toggled(bool)), this, SLOT(onBelowRangeToggled(bool))); connect(m_pAboveRangeCheck, SIGNAL(toggled(bool)), @@ -329,48 +467,102 @@ bool nmWxPebiResultColorScaleSettingsDlg::initializeFromScene() m_pSceneController->getSnapshotBindingRevision(); m_sExpectedSnapshotId = m_pSceneController->getCurrentSnapshotId(); m_sExpectedPropertyId = m_pSceneController->getCurrentPropertyId(); - m_sDisplayUnit = nmPebiResultPropertyDisplayText::getDefaultDisplayUnit( - m_sExpectedPropertyId); - m_pUnitLabel->setText(m_sDisplayUnit.isEmpty() - ? tr("Dimensionless") : m_sDisplayUnit); + nmPebiResultPropertyDescriptor oDescriptor; double dVisibleRange[2] = { 0.0, 0.0 }; if(m_nExpectedBindingRevision == 0 || m_sExpectedSnapshotId.isEmpty() || m_sExpectedPropertyId.isEmpty() || + !nmPebiResultPropertyCatalog::findDescriptor( + m_sExpectedPropertyId, oDescriptor) || !m_pSceneController->getCurrentPropertyRange(m_dPropertyRange) || !m_pSceneController->getCurrentDisplayRange(dVisibleRange) || !m_pSceneController->getCurrentDisplaySettings( m_oBaselineSettings)) { return false; } - populateEditor(m_oBaselineSettings, dVisibleRange); - return true; + m_sBaseUnit = oDescriptor.m_sBaseUnit; + m_sDisplayUnit = oDescriptor.m_sDefaultDisplayUnit.isEmpty() + ? m_sBaseUnit : oDescriptor.m_sDefaultDisplayUnit; + initializeUnitEditor(); + return populateEditor(m_oBaselineSettings, dVisibleRange); } -void nmWxPebiResultColorScaleSettingsDlg::populateEditor( +void nmWxPebiResultColorScaleSettingsDlg::initializeUnitEditor() +{ + const bool bSignalsBlocked = m_pUnitCombo->blockSignals(true); + m_pUnitCombo->clear(); + m_pUnitGroup = nullptr; + if(m_sBaseUnit.isEmpty()) { + m_sDisplayUnit.clear(); + m_pUnitCombo->setEnabled(false); + m_pUnitCombo->hide(); + m_pUnitCombo->blockSignals(bSignalsBlocked); + return; + } + + m_pUnitGroup = iUnitHelper::getUnitGroupByUnit(m_sBaseUnit); + QStringList listUnits; + if(m_pUnitGroup != nullptr && + m_pUnitGroup->indexOf(m_sBaseUnit) >= 0) { + listUnits = m_pUnitGroup->getAllUnitNames(); + } + const bool bHasUnitGroup = !listUnits.isEmpty() && + listUnits.contains(m_sBaseUnit); + if(!bHasUnitGroup) { + m_pUnitGroup = nullptr; + listUnits.clear(); + listUnits.append(m_sBaseUnit); + m_sDisplayUnit = m_sBaseUnit; + } else if(!listUnits.contains(m_sDisplayUnit)) { + m_sDisplayUnit = m_sBaseUnit; + } + + m_pUnitCombo->addItems(listUnits); + m_pUnitCombo->setCurrentIndex( + m_pUnitCombo->findText(m_sDisplayUnit)); + m_pUnitCombo->setEnabled(bHasUnitGroup); + m_pUnitCombo->show(); + m_pUnitCombo->blockSignals(bSignalsBlocked); +} + +bool nmWxPebiResultColorScaleSettingsDlg::populateEditor( const nmPebiResultDisplaySettings& oSettings, const double dVisibleRange[2]) { + double dDisplayRange[2] = { 0.0, 0.0 }; + if(!convertBaseRangeToDisplay(dVisibleRange, dDisplayRange)) { + setErrorText(tr("Unable to convert the range to the selected unit.")); + return false; + } + + m_oEditedBelowRangeColor = oSettings.m_oBelowRangeColor; + m_oEditedAboveRangeColor = oSettings.m_oAboveRangeColor; + m_oEditedBelowRangeColor.setAlpha(255); + m_oEditedAboveRangeColor.setAlpha(255); + m_bManualRangeEdited = oSettings.m_bHasManualRange; + + const bool bColorMapSignalsBlocked = + m_pColorMapCombo->blockSignals(true); + const bool bBelowSignalsBlocked = + m_pBelowRangeCheck->blockSignals(true); + const bool bAboveSignalsBlocked = + m_pAboveRangeCheck->blockSignals(true); const int nColorIndex = m_pColorMapCombo->findData( oSettings.m_sColorMapId); m_pColorMapCombo->setCurrentIndex(nColorIndex); - m_pMinimumEdit->setText(formatValue(dVisibleRange[0])); - m_pMaximumEdit->setText(formatValue(dVisibleRange[1])); - m_bManualRangeEdited = oSettings.m_bHasManualRange; + m_pMinimumEdit->setText(formatValue(dDisplayRange[0])); + m_pMaximumEdit->setText(formatValue(dDisplayRange[1])); m_pLogarithmicCheck->setChecked( oSettings.m_eScaleMode == NM_PebiColorScale_Logarithmic); m_pBelowRangeCheck->setChecked(oSettings.m_bUseBelowRangeColor); m_pAboveRangeCheck->setChecked(oSettings.m_bUseAboveRangeColor); - updateColorButton(m_pBelowColorButton, oSettings.m_oBelowRangeColor); - updateColorButton(m_pAboveColorButton, oSettings.m_oAboveRangeColor); - onBelowRangeToggled(oSettings.m_bUseBelowRangeColor); - onAboveRangeToggled(oSettings.m_bUseAboveRangeColor); + m_pColorMapCombo->blockSignals(bColorMapSignalsBlocked); + m_pBelowRangeCheck->blockSignals(bBelowSignalsBlocked); + m_pAboveRangeCheck->blockSignals(bAboveSignalsBlocked); + m_bRangeValuesEditedByUser = false; + refreshRangeColorButtons(); refreshLogAvailability(); - - const QString sUnitSuffix = m_sDisplayUnit.isEmpty() - ? QString() : QString::fromLatin1(" (") + - m_sDisplayUnit + QString::fromLatin1(")"); - m_pMinimumEdit->setToolTip(tr("Minimum") + sUnitSuffix); - m_pMaximumEdit->setToolTip(tr("Maximum") + sUnitSuffix); + refreshRangeToolTips(); + return true; } bool nmWxPebiResultColorScaleSettingsDlg::collectEditedSettings( @@ -378,24 +570,40 @@ bool nmWxPebiResultColorScaleSettingsDlg::collectEditedSettings( QString& sError) const { sError.clear(); - double dMinimum = 0.0; - double dMaximum = 0.0; - if(!parseFiniteValue(m_pMinimumEdit->text(), dMinimum) || - !parseFiniteValue(m_pMaximumEdit->text(), dMaximum)) { + double dDisplayMinimum = 0.0; + double dDisplayMaximum = 0.0; + if(!parseFiniteValue(m_pMinimumEdit->text(), dDisplayMinimum) || + !parseFiniteValue(m_pMaximumEdit->text(), dDisplayMaximum)) { sError = tr("Minimum and maximum must be finite numbers."); return false; } // 提交值与界面最多 12 位有效数字保持一致,重复 Apply 不产生隐藏精度差异。 - double dCanonicalMinimum = 0.0; - double dCanonicalMaximum = 0.0; - if(!parseFiniteValue(formatValue(dMinimum), dCanonicalMinimum) || - !parseFiniteValue(formatValue(dMaximum), dCanonicalMaximum)) { + if(!parseFiniteValue( + formatValue(dDisplayMinimum), dDisplayMinimum) || + !parseFiniteValue( + formatValue(dDisplayMaximum), dDisplayMaximum)) { sError = tr("Minimum and maximum must be finite numbers."); return false; } - dMinimum = dCanonicalMinimum; - dMaximum = dCanonicalMaximum; - if(m_bManualRangeEdited && dMinimum >= dMaximum) { + + double dBaseMinimum = 0.0; + double dBaseMaximum = 0.0; + const bool bReuseBaselineRange = m_bManualRangeEdited && + !m_bRangeValuesEditedByUser && + m_oBaselineSettings.m_bHasManualRange; + if(bReuseBaselineRange) { + dBaseMinimum = m_oBaselineSettings.m_dManualMinimum; + dBaseMaximum = m_oBaselineSettings.m_dManualMaximum; + } else if(!convertUnitValue( + dDisplayMinimum, m_sDisplayUnit, + m_sBaseUnit, dBaseMinimum) || + !convertUnitValue( + dDisplayMaximum, m_sDisplayUnit, + m_sBaseUnit, dBaseMaximum)) { + sError = tr("Unable to convert the range to the selected unit."); + return false; + } + if(m_bManualRangeEdited && dBaseMinimum >= dBaseMaximum) { sError = tr("Minimum must be less than maximum."); return false; } @@ -407,10 +615,14 @@ bool nmWxPebiResultColorScaleSettingsDlg::collectEditedSettings( oSettings.m_eScaleMode = m_pLogarithmicCheck->isChecked() ? NM_PebiColorScale_Logarithmic : NM_PebiColorScale_Linear; oSettings.m_bHasManualRange = m_bManualRangeEdited; - oSettings.m_dManualMinimum = m_bManualRangeEdited ? dMinimum : 0.0; - oSettings.m_dManualMaximum = m_bManualRangeEdited ? dMaximum : 0.0; + oSettings.m_dManualMinimum = + m_bManualRangeEdited ? dBaseMinimum : 0.0; + oSettings.m_dManualMaximum = + m_bManualRangeEdited ? dBaseMaximum : 0.0; oSettings.m_bUseBelowRangeColor = m_pBelowRangeCheck->isChecked(); oSettings.m_bUseAboveRangeColor = m_pAboveRangeCheck->isChecked(); + oSettings.m_oBelowRangeColor = m_oEditedBelowRangeColor; + oSettings.m_oAboveRangeColor = m_oEditedAboveRangeColor; oSettings.m_oBelowRangeColor.setAlpha(255); oSettings.m_oAboveRangeColor.setAlpha(255); return true; @@ -444,7 +656,11 @@ bool nmWxPebiResultColorScaleSettingsDlg::applyEditor() } m_oBaselineSettings = oCommittedSettings; - populateEditor(oCommittedSettings, dVisibleRange); + if(!populateEditor(oCommittedSettings, dVisibleRange)) { + sError = tr("Unable to convert the range to the selected unit."); + emit operationFailed(sError); + return false; + } setErrorText(QString()); emit displaySettingsApplied(); return true; @@ -469,6 +685,98 @@ bool nmWxPebiResultColorScaleSettingsDlg::parseFiniteValue( return true; } +bool nmWxPebiResultColorScaleSettingsDlg::convertUnitValue( + double dSourceValue, + const QString& sSourceUnit, + const QString& sDestinationUnit, + double& dDestinationValue) const +{ + if(!isFiniteEditorValue(dSourceValue)) { + return false; + } + if(sSourceUnit == sDestinationUnit) { + dDestinationValue = dSourceValue; + return true; + } + if(sSourceUnit.isEmpty() || sDestinationUnit.isEmpty() || + m_pUnitGroup == nullptr || + m_pUnitGroup->indexOf(sSourceUnit) < 0 || + m_pUnitGroup->indexOf(sDestinationUnit) < 0) { + return false; + } + + int nDigit = 12; + double dCandidate = 0.0; + if(!m_pUnitGroup->convert( + sSourceUnit, dSourceValue, + sDestinationUnit, dCandidate, nDigit) || + !isFiniteEditorValue(dCandidate)) { + return false; + } + dDestinationValue = dCandidate; + return true; +} + +bool nmWxPebiResultColorScaleSettingsDlg::convertBaseRangeToDisplay( + const double dBaseRange[2], + double dDisplayRange[2]) const +{ + double dMinimum = 0.0; + double dMaximum = 0.0; + if(!convertUnitValue( + dBaseRange[0], m_sBaseUnit, + m_sDisplayUnit, dMinimum) || + !convertUnitValue( + dBaseRange[1], m_sBaseUnit, + m_sDisplayUnit, dMaximum)) { + return false; + } + dDisplayRange[0] = dMinimum; + dDisplayRange[1] = dMaximum; + return true; +} + +void nmWxPebiResultColorScaleSettingsDlg::refreshRangeToolTips() +{ + const QString sUnitSuffix = m_sDisplayUnit.isEmpty() + ? QString() : QString::fromLatin1(" (") + + m_sDisplayUnit + QString::fromLatin1(")"); + m_pMinimumEdit->setToolTip(tr("Minimum") + sUnitSuffix); + m_pMaximumEdit->setToolTip(tr("Maximum") + sUnitSuffix); +} + +void nmWxPebiResultColorScaleSettingsDlg::refreshRangeColorButtons() +{ + const QString sColorMapId = m_pColorMapCombo->itemData( + m_pColorMapCombo->currentIndex()).toString(); + QColor oMinimumColor; + QColor oMaximumColor; + const bool bHasMinimumColor = nmVTKColorMapCatalog::getColor( + sColorMapId, 0.0, oMinimumColor); + const bool bAutomaticConstant = !m_bManualRangeEdited && + m_dPropertyRange[0] == m_dPropertyRange[1]; + const bool bHasMaximumColor = bAutomaticConstant + ? bHasMinimumColor + : nmVTKColorMapCatalog::getColor( + sColorMapId, 1.0, oMaximumColor); + if(bAutomaticConstant && bHasMinimumColor) { + oMaximumColor = oMinimumColor; + } + + const bool bUseBelowColor = m_pBelowRangeCheck->isChecked(); + const bool bUseAboveColor = m_pAboveRangeCheck->isChecked(); + m_pBelowColorButton->setEnabled(bUseBelowColor); + m_pAboveColorButton->setEnabled(bUseAboveColor); + updateColorButton( + m_pBelowColorButton, + !bUseBelowColor && bHasMinimumColor + ? oMinimumColor : m_oEditedBelowRangeColor); + updateColorButton( + m_pAboveColorButton, + !bUseAboveColor && bHasMaximumColor + ? oMaximumColor : m_oEditedAboveRangeColor); +} + void nmWxPebiResultColorScaleSettingsDlg::updateColorButton( QPushButton* pButton, const QColor& oColor) @@ -477,7 +785,10 @@ void nmWxPebiResultColorScaleSettingsDlg::updateColorButton( return; } pButton->setStyleSheet(QString::fromLatin1( - "QPushButton { background-color: rgb(%1,%2,%3); border: 1px solid #666; }") + "QPushButton { background-color: rgb(%1,%2,%3); " + "border: 1px solid #666; } " + "QPushButton:disabled { background-color: rgb(%1,%2,%3); " + "border: 1px solid #888; }") .arg(oColor.red()).arg(oColor.green()).arg(oColor.blue())); }