From b565039240d7700d45577339e6c2ee89f6069e3a Mon Sep 17 00:00:00 2001 From: lh <2334563547@qq.com> Date: Sun, 20 Sep 2026 15:33:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E9=A2=9C=E8=89=B2=E8=AE=BE=E7=BD=AE=E5=AF=B9=E8=AF=9D=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用颜色映射和越界值颜色分组重构对话框布局 - 统一范围输入、单位选择、重置按钮及底部按钮的尺寸与对齐 - 新增属性单位下拉框,复用全局单位表完成显示单位与基础单位双向换算 - 保持单位选择为对话框临时状态,避免纯单位切换改写手工范围 - 在单位换算失败时恢复原状态并阻止不完整的界面刷新 - 未启用越界专用颜色时显示当前色带端点色 - 独立保存用户自定义颜色,保持 Apply、OK 和 Cancel 的事务语义 - 更换属性范围重置图标并更新中文翻译资源 --- Bin/Config/Lang/cn/nmNum_cn.qm | Bin 147684 -> 147846 bytes Bin/Config/Lang/cn/nmNum_cn.ts | 5 +- Bin/Res/Icon/NmRestorePropertyRange.png | Bin 0 -> 1615 bytes .../nmWxPebiResultColorScaleSettingsDlg.h | 32 +- .../nmWxPebiResultColorScaleSettingsDlg.cpp | 511 ++++++++++++++---- 5 files changed, 442 insertions(+), 106 deletions(-) create mode 100644 Bin/Res/Icon/NmRestorePropertyRange.png diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index c0659bfb8ba1c83110f39c2f442f76284f00304b..75bee651221dd54496007e9e06375d4ac20d2752 100644 GIT binary patch delta 4024 zcmX|@3tUa<`p2K=ZC_ezxA)dgQfcoBdn?-A3!{=tn4=h^nJ7fXoC;m^ANR}2ZG_Gv zml+I0oP!i2VJ5f2a9m0j*5@P}8S9*rYrx|taC@o-f*mW7Tuy%HIIki}r9oR5{G#g;bS)A=W)**oJe&M`LL9#IM ztsZ1&Q-CRSlr6J!s{vVMBH*)=AiI?Ym^u$+4PJm*?h^lUlGvI?S7eLuR|=PhiRD8< zzWE$rdmwF-2jXv3B{z%LBSBv157_KWYK2diYZm|=c7psfTRwD z<#=65Hgwy{El^@{ zA`D;k3h>5WiEmcJm%W$rTb9C?0l9!-y(F$0F7b#~V(DCoSL`X?(#Y~xQ?})#&}T4i zNicx*R*6IUz_?|r0nZ1+_%%G(!7PYcKMt_#1&NuHC6+`;d^v|a1atoz$q>!n1OA5~ zI+1USYlaC4zQ9Bum^gYaVETNRxU2|p(Hn_7CrG@UNx1^obTGt}`f_z9ORQZ?RYHLH zrxPURYz2J#PqGy~zd8xC9W(&zTp)ECx9#3*kb2Y&pm{K)b@2myJ^<3vZvb}r8>D6I z;M@aqa+(0<)j;M}1bhrI|MN4f5hPX=QLgA|{}I+EZvhB>3!65`0Md`bF5xubrQ=j1 z8r`cA_J<__wlc`?$91}^g`>WMfmwvZ$-H2|iy=@N7|A{LBYCI{SV-Y2qkEeLTw7WN zI6oB1v+i&kJ3v+W7QnhY@aIaAyY&Fv7V`kBR?{|B2-Z*y|6fi{RwiL9cH2?Qees6E zt@P}G4<%bgutp0iv9c4Yam=8lz|fUytW5ZV1wogt^rCi^mr z${X7O%@-{?ggi0K;#qJL;2(Yz)7cL9QATG^mvb^@g*`V_j7)Ir2ar5VW)(RS@U~H6 z>vlTV*?`Sd-#JXI=_HG}DDrb&LjJaaxSf)1S1kHfwt2*FfXCDlk8hNCr9fh3ro^fW ziBAqme0o^on->z>E&reO9c1jBMEOYB=GX0jflacl^LQ>DzbV_kwhS=ywnT?lS7kqy zEClGYPPXgR5ngC_$#&Q9Br8v$>@H#Av+=S6ee!_G*HR5%UKk`R)i?uQn@ApZdUk6A zh1;12JzFg+kIV!3)K_-bFBovo4cXm?<$$_gvIk{6x0Za6)dyPu?i(h17~2zY#$wqc zuN1%&3uVvRUi0APRAd(=JeSM2Zs3hz4B2))>G~o;9x!qU;JhMv-wE@%-zz0PRmq3$ z=N0KZQcG8}kL7&%f`I@kEfWNp%Y`8{M_OYzVkNh2YpQU&QgY_x-shI}=|4%PJ{q+)V>)Izh>fMu(~(h3+J`>x#9CZsk97-Hs@n z*X07{U!#0SJMSMArX-%if*|n=Cxz)!CNIs&3b$2U^{m|r_i(OyW2nO1tq}0Z0BUiZ zWP0+jB6}w9YQ6pde$(GYFrbS!^)Lxn1X=k% zfXN)Fs)y%@qk?V0X?{|Uf{T4RpmB%b+81~fozUa8hL00%LeKI$fa}Kyo~s7{esU24 zdw&DqZ7GD54h1lu5C)a;NufBO!h1%H`7=S7yfX;E(o2X_6!8=xiA`^Xgv%EJcjQYf z@D`H3d;l1^T}b+w|L)!2gqgP7XTOY>*tl4jIdU1`o1@g&)6V)gVNC(=-K9r_^$z@; z_ok7p(ZD73HyT;dKPbj%5LO9SKXECGA}Gt~t$kp{XR|#H!lM}{c)QY2iE$LRliFl- zzu_wCLc8%!cUj_xaUH~4VWMv2V4i%H9mKmDiZN|uwcV-3Gz=e7P%pFYnWLB)eFxy; z5b?Xco%v)tofhpPy`E#t!6E_Br031;! z{_x={Z)nHF-Ng&}t@Pr*3l9P;SBwAVgE*F3k*%9aXcAAKcjr;|(@?h%p-% zyhSCw1K5Kl)Y3baozqifpHR~q8CPI`fvUBTKlaUMs@9L3dlFS`ar}V22dUb>Tg;WH zrJ_D2>sBjOz8~*4Ut2jxNAhELrN%x{c$|iM1oCldfrruILzLCn$CLTA+iVp#I0~4s z%WCR19f1b-0m_0F%^37r5Nnfk7P&wf1NNO{$!(z(z zw6i{K)exis_)D~UQT%z%siq>&h3s)(^7k?`Wp9f23T9^*<#~nRbE@|;vePC~dk46m zuva&R#`4;*Qr(=I4&Y#|Za&N3bMIUAi*K6%C;qB_S$ZGvemW(4>)9n0WqU{H{)*CQ z)?5Ni5+xROrABWvd*eq=J|+}tsE@h-;cl8Sa-Ilv6Eso7#&X4e){0 zYp`$7T)o!`!2WlM5oXP`mk0Swm_|Xq!R);|E$}@r*2HRuCq?iKy+Gl9M%}yR+Q|d$ z0PRWQt8B{hGkD+C&Uwde_M=8S&xwcF-%q>XP8Q(CX6@ozXLzki)GpDBfU}lMEc!rI zek<^Iit+cvN0jY9-t%2I?c4M#0J873?S2vbB%`&T###gRoUHwnFa_Z6W3-=E1_L^V z&j6-MTBZvq)&-z`kF(|GtxHoAoF~dGf&VG^M?H9E(#%xnv)n(mxFJ>FT0#xH(eq{S!2 ZPfd@X=A4l-BYo4rT0BtPrw)(B{ue~;E*t;= delta 3995 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 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 0000000000000000000000000000000000000000..9d1e81e631061e4fcb0e91322d3a4d24c1d0d3ed GIT binary patch literal 1615 zcmV-V2C(^wP)Ry+-BACOl4~Qp(2*ARae>6yg!vYt{DEmFmr0D_^%e zjt2m`My3<|M>EzlN7Az?10jSEdc+MVU#RT6k+0f+y7b`AMt3Z3T2Y;Aj9?-GT-WP3 z{Z_lv*xWjlQD1p>?gyDD#`wEn(As)=@9MjsZM=CYJ2#b@NX7jX2_WV0EkQ~U0Oi_d zu~hx_@$oauGnCSOF;Ff9;P1b6dG+H5_GCJtYb@A$Xgl3rUrJC_rgKe75E6uhuG6d3 zHXaIY`zo*_}8Z!|Gu?3Kb58wBS4-f^5uHISf8{=!oUEq*^z~& zJCPa7Bohc@DPiDw8>Q+m4zV-GW*K96$ioMqVLI7L`>iY4qfg8$lpqA9TI+spJh&iTH(2R+pwCOS4I3XRJ_mZN%XB7jD^t zPo?7sLAlmiD-_S4h%aUg&bg*(lqwiw01!gB?%?aS=Ia;NQ|V0HvV@e?M$?q7*Ulc) zb%PKR+=)l-m)rNt&SW}<5C|deY={{k{<1J|Qa99ltKy9cSF6iHNFc+OFyjaoEO4ntVE;b)6mn zs4A;&d4s{g^F(kM4L~;TAdJ$M*6$DexH+!yJXZ)W zkmiU14A)#wa1}85PWA6HR|teqKub#LCwZuYP{zMp$pQida5y=H{2slw)a=Dm3IMpC zXE3yV`|!h&(EjkNrFQ#azuyCZ2xp4ILb4xO+wmgWsLa=EcB?Bhj0p*j)0?oUua&?? z01M;MUSIV*7XUEElqwjL@X_8t{Nv;G|L0o2JGX=}_SJOZqrzAs<|o9q?Wf08LUv$K zx!vLb6RHhelK_6B4+8YzH`Tvf%`qOq?x5Ya4Z|RWUb$VlQyxrDC4~eD&}cM{E~lBo z2qDAH8DbC~9pS)dEB2e09#m|U$&3?9YQ?Qyug5vRc%%Hr`>XSF)4u!FYW=AhHEF6$ zWnt>dn+yBd5yjhu>&MeNwq=a67(Cv$rLi@_)N~J><=4Vw^l>l20+xAxd z^s%JIHO3erIPjA0%_9Qvg#7P~(%-JFnO5xSpB$C|LV}b)6{Y2fzkY5f6S3*Zh(C60 zyIUv}&i~vTvvkfmr8_z_TFn)A0o=~F{`mfCSE%W+7}r!76}bPFzFhpcH(Og9#p1c6 z=JJARnpVUx$WDOWbMER&<&8`0$wX{AZXp06hbfWU5W(OaxL&Q^7}(8MPsW#~qLD~c zHw;WE?7VQtJOBV8#O-Y3&sTGezLHGDB8CoO_W?X1>y4&ucievG!~%bADQX(JVd$Le ze&Y%X#K%Fo{Ene7rF31l)9HNhb>*GU>sX5>$Ks4C{%4+)_wL;N*#Uhqt^RPz;7n09 zjdRWzBZTbRT0&*&zY$U>jYJ|ZJgF`p9DDDJ(tmDcO)H*EB@uw@da8oXJ{8k-UDGs0 zVT>sVld!UO4SnjbgrN~alwv}Nq9`w&|518$~}0R9h3k7 N002ovPDHLkV1fvC8G--+ literal 0 HcmV?d00001 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())); }