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/nmParameterField.cpp

538 lines
15 KiB
C++

#include "nmParameterField.h"
#include "iParaItemCtrl.h"
#include "iParameter.h"
#include "iSysParaHelper.h"
#include "mModuleDefines.h"
#include <QComboBox>
#include <QCoreApplication>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPalette>
#include <QSizePolicy>
namespace
{
/* Qt 4.8的lupdate需要显式标记间接翻译字符串。 */
static const char* const s_pFieldTranslationMarkers[] =
{
QT_TRANSLATE_NOOP("nmParameterField", "The parameter layout is unavailable."),
QT_TRANSLATE_NOOP("nmParameterField", "Parameter definition not found: %1"),
QT_TRANSLATE_NOOP("nmParameterField", "Failed to bind the unit for parameter: %1")
};
QString fieldText(const char* pText)
{
Q_UNUSED(s_pFieldTranslationMarkers);
return QCoreApplication::translate("nmParameterField", pText);
}
QString validationToolTip(const QString& sText, bool bValid)
{
if (bValid)
{
return sText;
}
// 无效提示使用富文本着色业务文本必须先转义以免被当作HTML解析。
QString sEscaped = sText;
sEscaped.replace("&", "&amp;");
sEscaped.replace("<", "&lt;");
sEscaped.replace(">", "&gt;");
sEscaped.replace("\"", "&quot;");
sEscaped.replace("\n", "<br/>");
return "<span style=\"color:red;\">" + sEscaped + "</span>";
}
}
nmParameterFieldInfo::nmParameterFieldInfo()
: m_eEditorType(NM_PARAMETER_EDITOR_NUMBER),
m_bUnitEnabled(true)
{
}
nmParameterField::nmParameterField(
const nmParameterFieldInfo& oFieldInfo,
nmIParameterUnitService* pUnitService,
QObject* pParent)
: QObject(pParent),
m_oFieldInfo(oFieldInfo),
m_pUnitService(pUnitService),
m_pParameter(nullptr),
m_pLabel(nullptr),
m_pLineEdit(nullptr),
m_pValueComboBox(nullptr),
m_pUnitComboBox(nullptr),
m_pUnitLabel(nullptr),
m_clrNormalText(),
m_sExternalValidationError(),
m_bVisible(true)
{
}
nmParameterField::~nmParameterField()
{
m_pUnitService = nullptr;
m_pParameter = nullptr;
m_pLabel = nullptr;
m_pLineEdit = nullptr;
m_pValueComboBox = nullptr;
m_pUnitComboBox = nullptr;
m_pUnitLabel = nullptr;
}
bool nmParameterField::initialize(QGridLayout* pLayout,
int nRow,
int nColumnOffset,
QString& sError)
{
if (pLayout == nullptr)
{
sError = fieldText("The parameter layout is unavailable.");
return false;
}
// 第一步文本和枚举字段不依赖XML数值定义。
if (isText())
{
createTextEditor(pLayout, nRow, nColumnOffset);
sError.clear();
return true;
}
if (isEnum())
{
createEnumEditor(pLayout, nRow, nColumnOffset);
sError.clear();
return true;
}
// 第二步数值字段取得NM系列的基础参数定义。
m_pParameter = _paraHelper->getPara(
m_oFieldInfo.m_sBaseParameterId, s_Nm_Serie);
if (m_pParameter == nullptr)
{
sError = fieldText("Parameter definition not found: %1")
.arg(m_oFieldInfo.m_sBaseParameterId);
return false;
}
// 第三步:创建标签、输入框和单位列。
return createNumericEditor(pLayout, nRow, nColumnOffset, sError);
}
QString nmParameterField::parameterId() const
{
return m_oFieldInfo.m_sParameterId;
}
QString nmParameterField::baseParameterId() const
{
return m_oFieldInfo.m_sBaseParameterId;
}
bool nmParameterField::isEnum() const
{
return m_oFieldInfo.m_eEditorType == NM_PARAMETER_EDITOR_ENUM;
}
bool nmParameterField::isText() const
{
return m_oFieldInfo.m_eEditorType == NM_PARAMETER_EDITOR_TEXT;
}
void nmParameterField::setValueRightAligned(bool bRightAligned)
{
if (m_pLineEdit != nullptr)
{
m_pLineEdit->setAlignment(
(bRightAligned ? Qt::AlignRight : Qt::AlignLeft) |
Qt::AlignVCenter);
}
}
int nmParameterField::editorMinimumWidth()
{
return 140;
}
int nmParameterField::unitMinimumWidth()
{
return 86;
}
int nmParameterField::editorHeight()
{
return 25;
}
bool nmParameterField::createNumericEditor(
QGridLayout* pLayout,
int nRow,
int nColumnOffset,
QString& sError)
{
// 第一步:创建统一尺寸的标签和值输入框,保证各业务面板列对齐。
QWidget* pParentWidget = pLayout->parentWidget();
m_pLabel = new QLabel(m_pParameter->m_sAlias, pParentWidget);
m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_pLabel->setWordWrap(false);
m_pLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_pLineEdit = new QLineEdit(pParentWidget);
m_clrNormalText = m_pLineEdit->palette().color(QPalette::Text);
m_pLineEdit->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_pLineEdit->setFixedHeight(editorHeight());
m_pLineEdit->setMinimumWidth(editorMinimumWidth());
m_pLineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
pLayout->addWidget(m_pLabel, nRow, nColumnOffset);
pLayout->addWidget(m_pLineEdit, nRow, nColumnOffset + 1);
// 第二步:有单位字段交给窗口服务绑定;无单位字段保留占位列。
if (m_oFieldInfo.m_bUnitEnabled &&
!m_pParameter->m_sUnit.isEmpty())
{
m_pUnitComboBox = new QComboBox(pParentWidget);
m_pUnitComboBox->setFixedHeight(editorHeight());
m_pUnitComboBox->setMinimumWidth(unitMinimumWidth());
m_pUnitComboBox->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Fixed);
pLayout->addWidget(m_pUnitComboBox, nRow, nColumnOffset + 2);
if (m_pUnitService == nullptr ||
!m_pUnitService->bindUnitField(
m_pLineEdit,
m_pUnitComboBox,
m_oFieldInfo.m_sBaseParameterId))
{
sError = fieldText(
"Failed to bind the unit for parameter: %1")
.arg(m_oFieldInfo.m_sBaseParameterId);
return false;
}
}
else
{
m_pUnitLabel = new QLabel(pParentWidget);
m_pUnitLabel->setMinimumWidth(unitMinimumWidth());
m_pUnitLabel->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Fixed);
pLayout->addWidget(m_pUnitLabel, nRow, nColumnOffset + 2);
}
updateValidationAppearance(true);
sError.clear();
return true;
}
void nmParameterField::createTextEditor(QGridLayout* pLayout,
int nRow,
int nColumnOffset)
{
QWidget* pParentWidget = pLayout->parentWidget();
m_pLabel = new QLabel(m_oFieldInfo.m_sLabel, pParentWidget);
m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_pLabel->setWordWrap(false);
m_pLineEdit = new QLineEdit(pParentWidget);
m_clrNormalText = m_pLineEdit->palette().color(QPalette::Text);
m_pLineEdit->setFixedHeight(editorHeight());
m_pLineEdit->setMinimumWidth(editorMinimumWidth());
m_pLineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_pUnitLabel = new QLabel(pParentWidget);
m_pUnitLabel->setMinimumWidth(unitMinimumWidth());
m_pUnitLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
pLayout->addWidget(m_pLabel, nRow, nColumnOffset);
pLayout->addWidget(m_pLineEdit, nRow, nColumnOffset + 1);
pLayout->addWidget(m_pUnitLabel, nRow, nColumnOffset + 2);
}
void nmParameterField::createEnumEditor(QGridLayout* pLayout,
int nRow,
int nColumnOffset)
{
QWidget* pParentWidget = pLayout->parentWidget();
m_pLabel = new QLabel(m_oFieldInfo.m_sLabel, pParentWidget);
m_pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_pLabel->setWordWrap(false);
m_pValueComboBox = new QComboBox(pParentWidget);
m_pValueComboBox->setFixedHeight(editorHeight());
m_pValueComboBox->setMinimumWidth(editorMinimumWidth());
m_pValueComboBox->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Fixed);
for (int nIndex = 0;
nIndex < m_oFieldInfo.m_listOptions.size();
++nIndex)
{
const nmParameterEnumOption& oOption =
m_oFieldInfo.m_listOptions[nIndex];
m_pValueComboBox->addItem(oOption.m_sText, oOption.m_sCode);
}
m_pUnitLabel = new QLabel(pParentWidget);
m_pUnitLabel->setMinimumWidth(unitMinimumWidth());
m_pUnitLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
pLayout->addWidget(m_pLabel, nRow, nColumnOffset);
pLayout->addWidget(m_pValueComboBox, nRow, nColumnOffset + 1);
pLayout->addWidget(m_pUnitLabel, nRow, nColumnOffset + 2);
}
void nmParameterField::setBaseValue(const QVariant& oValue)
{
if (m_pLineEdit == nullptr || !oValue.isValid())
{
return;
}
// 程序回填仍走控件标准写值接口,由所属页面的刷新标记屏蔽业务提交。
if (isText())
{
m_pLineEdit->setText(oValue.toString());
}
else if (m_pUnitComboBox != nullptr && m_pUnitService != nullptr)
{
m_pUnitService->setUnitBaseValue(
m_pLineEdit, oValue.toDouble());
}
else
{
m_pLineEdit->setText(formatBaseValue(oValue.toDouble()));
}
validate();
}
QVariant nmParameterField::baseValue(bool& bValid)
{
bValid = validate();
if (!bValid || m_pLineEdit == nullptr)
{
return QVariant();
}
// 单位字段始终向业务层返回基准单位值,界面显示单位不进入数据模型。
if (isText())
{
return QVariant(m_pLineEdit->text().trimmed());
}
if (m_pUnitComboBox != nullptr && m_pUnitService != nullptr)
{
return QVariant(m_pUnitService->unitBaseValue(m_pLineEdit));
}
bool bNumberOk = false;
double dValue = m_pLineEdit->text().trimmed().toDouble(&bNumberOk);
return bNumberOk ? QVariant(dValue) : QVariant();
}
void nmParameterField::setEnumCode(const QString& sCode)
{
if (m_pValueComboBox == nullptr)
{
return;
}
int nIndex = m_pValueComboBox->findData(sCode);
m_pValueComboBox->setCurrentIndex(nIndex >= 0 ? nIndex : 0);
}
QString nmParameterField::enumCode() const
{
if (m_pValueComboBox == nullptr ||
m_pValueComboBox->currentIndex() < 0)
{
return QString();
}
return m_pValueComboBox->itemData(
m_pValueComboBox->currentIndex()).toString();
}
void nmParameterField::setExternalValidationError(const QString& sError)
{
m_sExternalValidationError = sError;
validate();
}
bool nmParameterField::validate()
{
if (!m_bVisible || isEnum())
{
return true;
}
if (m_pLineEdit == nullptr)
{
return false;
}
// 第一步:按编辑器类型执行本地格式、范围和整数约束校验。
bool bValid = false;
if (isText())
{
bValid = true;
}
else if (m_pParameter == nullptr)
{
bValid = false;
}
else if (m_pUnitComboBox != nullptr && m_pUnitService != nullptr)
{
bValid = m_pUnitService->isUnitInputValid(m_pLineEdit);
}
else
{
bool bNumberOk = false;
double dValue = m_pLineEdit->text().trimmed()
.toDouble(&bNumberOk);
bValid = bNumberOk && dValue == dValue &&
dValue >= m_pParameter->m_dMin &&
dValue <= m_pParameter->m_dMax;
if (bValid &&
m_oFieldInfo.m_eEditorType == NM_PARAMETER_EDITOR_INTEGER)
{
bValid = dValue == static_cast<int>(dValue);
}
}
// 第二步:叠加面板提供的跨字段业务规则,例如射孔区间关系。
bValid = bValid && m_sExternalValidationError.isEmpty();
updateValidationAppearance(bValid);
return bValid;
}
void nmParameterField::setFieldVisible(bool bVisible)
{
m_bVisible = bVisible;
if (m_pLabel != nullptr)
{
m_pLabel->setVisible(bVisible);
}
if (m_pLineEdit != nullptr)
{
m_pLineEdit->setVisible(bVisible);
}
if (m_pValueComboBox != nullptr)
{
m_pValueComboBox->setVisible(bVisible);
}
if (m_pUnitComboBox != nullptr)
{
m_pUnitComboBox->setVisible(bVisible);
}
if (m_pUnitLabel != nullptr)
{
m_pUnitLabel->setVisible(bVisible);
}
}
bool nmParameterField::isFieldVisible() const
{
return m_bVisible;
}
QLineEdit* nmParameterField::valueEditor() const
{
return m_pLineEdit;
}
QComboBox* nmParameterField::enumEditor() const
{
return m_pValueComboBox;
}
QString nmParameterField::formatBaseValue(double dValue) const
{
if (m_pParameter == nullptr)
{
return QString::number(dValue, 'g', 15);
}
if (m_oFieldInfo.m_eEditorType == NM_PARAMETER_EDITOR_INTEGER ||
m_pParameter->m_nDigit == 0)
{
return QString::number(dValue, 'f', 0);
}
if (m_pParameter->m_bScientific)
{
return QString::number(dValue, 'e', m_pParameter->m_nDigit);
}
// 普通小数去除无意义尾零,避免界面出现与有效精度无关的冗长数字。
QString sValue = QString::number(
dValue, 'f', m_pParameter->m_nDigit);
while (sValue.contains('.') && sValue.endsWith('0'))
{
sValue.chop(1);
}
if (sValue.endsWith('.'))
{
sValue.chop(1);
}
return sValue;
}
void nmParameterField::updateValidationAppearance(bool bValid)
{
if (m_pLineEdit == nullptr)
{
return;
}
// 第一步:只调整文字颜色,不覆盖应用主题提供的输入框背景和边框。
QPalette oPalette = m_pLineEdit->palette();
oPalette.setColor(QPalette::Text,
bValid ? m_clrNormalText : QColor(Qt::red));
m_pLineEdit->setPalette(oPalette);
// 第二步:组合参数名称、当前单位范围和跨字段错误作为统一帮助信息。
QString sHelp = m_oFieldInfo.m_sLabel;
if (m_pParameter != nullptr)
{
QString sCurrentUnit;
iUnitGroup* pUnitGroup = nullptr;
if (m_pUnitComboBox != nullptr && m_pUnitService != nullptr)
{
sCurrentUnit = m_pUnitService->currentUnit(m_pLineEdit);
pUnitGroup = m_pUnitService->unitGroup(m_pLineEdit);
}
QString sRange = iParaItemCtrl::getScaleStrOf(
m_pParameter, sCurrentUnit, pUnitGroup);
sHelp = m_pParameter->m_sAlias;
if (!m_pParameter->m_sName.isEmpty())
{
if (!sHelp.isEmpty())
{
sHelp += "[" + m_pParameter->m_sName + "]";
}
else
{
sHelp = m_pParameter->m_sName;
}
}
if (!sRange.isEmpty())
{
if (!sHelp.isEmpty())
{
sHelp += "\n";
}
sHelp += sRange;
}
}
if (!m_sExternalValidationError.isEmpty())
{
if (!sHelp.isEmpty())
{
sHelp += "\n";
}
sHelp += m_sExternalValidationError;
}
m_pLineEdit->setToolTip(validationToolTip(sHelp, bValid));
m_pLineEdit->setWhatsThis(sHelp);
if (m_pLabel != nullptr)
{
m_pLabel->setWhatsThis(sHelp);
}
}