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

318 lines
14 KiB
C++

#include "nmWxVolumeSliceSettingsDlg.h"
#include "nmParameterField.h"
#include <QCheckBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QHideEvent>
#include <QLabel>
#include <QLineEdit>
#include <QPalette>
#include <QPushButton>
#include <QSizePolicy>
#include <QToolButton>
#include <QVariant>
#include <QVBoxLayout>
nmWxVolumeSliceSettingsDlg::nmWxVolumeSliceSettingsDlg(QWidget* pParent)
: iDlgBase(pParent), m_pShowPlaneCheck(nullptr),
m_pShowSourceCheck(nullptr), m_pErrorLabel(nullptr), m_pApplyButton(nullptr), m_bUpdating(false)
{
createUi();
writeSettings(m_oApplied);
resize(minimumWidth(), sizeHint().height());
}
void nmWxVolumeSliceSettingsDlg::createUi()
{
setWindowTitle(tr("Slice Settings"));
setObjectName(QLatin1String("volumeSliceSettingsDialog"));
setModal(false);
setAttribute(Qt::WA_DeleteOnClose, false);
// 复用编辑井、储层特性的基类主题,同时保留工具窗口的非模态交互及 Esc 关闭行为。
setWindowFlags((windowFlags() & ~Qt::WindowType_Mask &
~Qt::WindowContextHelpButtonHint) | Qt::Tool);
setIgnoreEsc(false);
// 六个分量各占一行,共用标签列和输入列;原点区与法向区分别放置对应快捷操作。
const int nEditorHeight = nmParameterField::editorHeight();
const int nEditorMinimumWidth = nmParameterField::editorMinimumWidth();
QGroupBox* pPlaneGroup = new QGroupBox(tr("Plane Parameters"), this);
pPlaneGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QGridLayout* pPlaneLayout = new QGridLayout(pPlaneGroup);
pPlaneLayout->setContentsMargins(8, 15, 8, 10);
pPlaneLayout->setHorizontalSpacing(6);
pPlaneLayout->setVerticalSpacing(6);
pPlaneLayout->setColumnStretch(1, 1);
int nFieldLabelWidth = 0;
const char* pAxes[3] = { "X", "Y", "Z" };
for(int nAxis = 0; nAxis < 3; ++nAxis) {
QLabel* pOriginLabel = new QLabel(tr("Origin %1").arg(QLatin1String(pAxes[nAxis])), pPlaneGroup);
QLabel* pNormalLabel = new QLabel(tr("Normal %1").arg(QLatin1String(pAxes[nAxis])), pPlaneGroup);
pOriginLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
pNormalLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
nFieldLabelWidth = qMax(nFieldLabelWidth,
qMax(pOriginLabel->sizeHint().width(), pNormalLabel->sizeHint().width()));
pPlaneLayout->addWidget(pOriginLabel, nAxis, 0);
pPlaneLayout->addWidget(pNormalLabel, nAxis + 5, 0);
m_pOriginEdits[nAxis] = new QLineEdit(pPlaneGroup);
m_pNormalEdits[nAxis] = new QLineEdit(pPlaneGroup);
m_pOriginEdits[nAxis]->setObjectName(QString::fromLatin1("sliceOrigin") + QLatin1String(pAxes[nAxis]));
m_pNormalEdits[nAxis]->setObjectName(QString::fromLatin1("sliceNormal") + QLatin1String(pAxes[nAxis]));
m_pOriginEdits[nAxis]->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_pNormalEdits[nAxis]->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_pOriginEdits[nAxis]->setMinimumWidth(nEditorMinimumWidth);
m_pNormalEdits[nAxis]->setMinimumWidth(nEditorMinimumWidth);
m_pOriginEdits[nAxis]->setFixedHeight(nEditorHeight);
m_pNormalEdits[nAxis]->setFixedHeight(nEditorHeight);
m_pOriginEdits[nAxis]->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_pNormalEdits[nAxis]->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
pPlaneLayout->addWidget(m_pOriginEdits[nAxis], nAxis, 1);
pPlaneLayout->addWidget(m_pNormalEdits[nAxis], nAxis + 5, 1);
connect(m_pOriginEdits[nAxis], SIGNAL(textChanged(QString)), this, SLOT(onDraftChanged()));
connect(m_pNormalEdits[nAxis], SIGNAL(textChanged(QString)), this, SLOT(onDraftChanged()));
}
// 模型中心仅调整原点,放在原点 Z 下方,保持按钮紧凑并与输入框右边界对齐。
QToolButton* pCenterButton = new QToolButton(pPlaneGroup);
pCenterButton->setText(tr("Model Center"));
pCenterButton->setAutoRaise(true);
pCenterButton->setMinimumWidth(72);
pCenterButton->setFixedHeight(nEditorHeight);
pCenterButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
pPlaneLayout->addWidget(pCenterButton, 3, 1, Qt::AlignRight);
pPlaneLayout->setRowMinimumHeight(4, 6);
connect(pCenterButton, SIGNAL(clicked()), this, SLOT(onCenterRequested()));
// 控件按轴成对创建,显式调整 Tab 顺序,使键盘输入顺序与从上到下的布局一致。
setTabOrder(m_pOriginEdits[0], m_pOriginEdits[1]);
setTabOrder(m_pOriginEdits[1], m_pOriginEdits[2]);
setTabOrder(m_pOriginEdits[2], pCenterButton);
setTabOrder(pCenterButton, m_pNormalEdits[0]);
setTabOrder(m_pNormalEdits[0], m_pNormalEdits[1]);
setTabOrder(m_pNormalEdits[1], m_pNormalEdits[2]);
// 三个方向按钮放在法向 Z 下方,等宽分配输入列空间,整体左右边界与输入框一致。
QHBoxLayout* pShortcutLayout = new QHBoxLayout;
pShortcutLayout->setContentsMargins(0, 0, 0, 0);
pShortcutLayout->setSpacing(6);
const char* pPlanes[3] = { "XY", "XZ", "YZ" };
QWidget* pPreviousShortcutWidget = m_pNormalEdits[2];
for(int nPlane = 0; nPlane < 3; ++nPlane) {
QToolButton* pButton = new QToolButton(pPlaneGroup);
pButton->setText(QString::fromLatin1(pPlanes[nPlane]));
pButton->setProperty("normalAxis", 2 - nPlane);
pButton->setAutoRaise(true);
pButton->setMinimumWidth(44);
pButton->setFixedHeight(nEditorHeight);
pButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
pShortcutLayout->addWidget(pButton, 1);
setTabOrder(pPreviousShortcutWidget, pButton);
pPreviousShortcutWidget = pButton;
connect(pButton, SIGNAL(clicked()), this, SLOT(onAxisRequested()));
}
pPlaneLayout->addLayout(pShortcutLayout, 8, 1);
// 着色属性及渲染模式统一使用旧底栏;浮动窗口只保留平面相关的显示选项。
QGroupBox* pDisplayGroup = new QGroupBox(tr("Display Options"), this);
pDisplayGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QGridLayout* pDisplayLayout = new QGridLayout(pDisplayGroup);
pDisplayLayout->setContentsMargins(8, 15, 8, 10);
pDisplayLayout->setHorizontalSpacing(6);
pDisplayLayout->setVerticalSpacing(6);
pPlaneLayout->setColumnMinimumWidth(0, nFieldLabelWidth);
pDisplayLayout->setColumnMinimumWidth(0, nFieldLabelWidth);
pDisplayLayout->setColumnStretch(1, 1);
m_pShowPlaneCheck = new QCheckBox(tr("Show Plane"), pDisplayGroup);
m_pShowSourceCheck = new QCheckBox(tr("Show Original Model"), pDisplayGroup);
m_pShowPlaneCheck->setObjectName(QLatin1String("sliceShowPlane"));
m_pShowSourceCheck->setObjectName(QLatin1String("sliceShowSource"));
// 拖动说明放在显示平面的悬停提示中,减少常驻文字对参数布局的干扰。
m_pShowPlaneCheck->setToolTip(tr("Drag the center or outline to move the plane; drag arrows to rotate."));
QHBoxLayout* pDisplayOptionsLayout = new QHBoxLayout;
pDisplayOptionsLayout->setContentsMargins(0, 0, 0, 0);
pDisplayOptionsLayout->setSpacing(12);
pDisplayOptionsLayout->addWidget(m_pShowPlaneCheck);
pDisplayOptionsLayout->addWidget(m_pShowSourceCheck);
pDisplayOptionsLayout->addStretch();
pDisplayLayout->addLayout(pDisplayOptionsLayout, 0, 1);
setTabOrder(pPreviousShortcutWidget, m_pShowPlaneCheck);
setTabOrder(m_pShowPlaneCheck, m_pShowSourceCheck);
connect(m_pShowPlaneCheck, SIGNAL(toggled(bool)), this, SLOT(onDisplayOptionsChanged()));
connect(m_pShowSourceCheck, SIGNAL(toggled(bool)), this, SLOT(onDisplayOptionsChanged()));
m_pErrorLabel = new QLabel(this);
m_pErrorLabel->setWordWrap(true);
QPalette oErrorPalette = m_pErrorLabel->palette();
oErrorPalette.setColor(QPalette::WindowText, QColor(Qt::red));
m_pErrorLabel->setPalette(oErrorPalette);
m_pErrorLabel->hide();
// 底部沿用编辑井的边距及框架图标,应用型窗口的三个操作集中靠右排列。
QHBoxLayout* pFooterLayout = new QHBoxLayout;
pFooterLayout->setContentsMargins(4, 5, 4, 5);
pFooterLayout->setSpacing(8);
pFooterLayout->addStretch();
QPushButton* pResetButton = new QPushButton(tr("Reset"), this);
m_pApplyButton = new QPushButton(tr("Apply"), this);
QPushButton* pCloseButton = new QPushButton(tr("Close"), this);
m_pApplyButton->setObjectName(QLatin1String("sliceApplyButton"));
m_pApplyButton->setDefault(true);
pResetButton->setAutoDefault(false);
pCloseButton->setAutoDefault(false);
pResetButton->setMinimumWidth(72);
m_pApplyButton->setMinimumWidth(72);
pCloseButton->setMinimumWidth(72);
pCloseButton->setIcon(zxLoadIcon("Cancel"));
pCloseButton->setIconSize(QSize(16, 16));
pFooterLayout->addWidget(pResetButton);
pFooterLayout->addWidget(m_pApplyButton);
pFooterLayout->addWidget(pCloseButton);
connect(m_pApplyButton, SIGNAL(clicked()), this, SLOT(onApplyRequested()));
connect(pResetButton, SIGNAL(clicked()), this, SLOT(onResetRequested()));
connect(pCloseButton, SIGNAL(clicked()), this, SLOT(close()));
QVBoxLayout* pMainLayout = new QVBoxLayout(this);
pMainLayout->setContentsMargins(12, 10, 12, 0);
pMainLayout->setSpacing(10);
pMainLayout->addWidget(pPlaneGroup);
pMainLayout->addWidget(pDisplayGroup);
pMainLayout->addWidget(m_pErrorLabel);
pMainLayout->addStretch();
pMainLayout->addLayout(pFooterLayout);
setMinimumWidth(400);
}
void nmWxVolumeSliceSettingsDlg::setData(const QSharedPointer<const nmVolumeData>& pData, const nmVolumeSliceSettings& oApplied)
{
m_pData = pData;
setAppliedSettings(oApplied);
}
void nmWxVolumeSliceSettingsDlg::setAppliedSettings(const nmVolumeSliceSettings& oSettings)
{
m_oApplied = oSettings;
writeSettings(oSettings);
}
void nmWxVolumeSliceSettingsDlg::setAppliedProperty(const QString& sPropertyId)
{
// 底栏属性立即生效;重置、关闭窗口及再次应用平面时也应继续使用此属性。
m_oApplied.m_sPropertyId = sPropertyId;
}
bool nmWxVolumeSliceSettingsDlg::getDraft(nmVolumeSliceSettings& oSettings, QString* pError) const
{
if(pError != nullptr) pError->clear();
if(m_pData.isNull()) return false;
nmVolumeSliceSettings oDraft = m_oApplied;
for(int nAxis = 0; nAxis < 3; ++nAxis) {
bool bOriginOk = false;
bool bNormalOk = false;
oDraft.m_dOrigin[nAxis] = m_pOriginEdits[nAxis]->text().trimmed().toDouble(&bOriginOk);
oDraft.m_dNormal[nAxis] = m_pNormalEdits[nAxis]->text().trimmed().toDouble(&bNormalOk);
if(!bOriginOk || !bNormalOk) {
if(pError != nullptr) *pError = tr("Enter valid numbers for origin and normal.");
return false;
}
}
if(!oDraft.normalize(pError)) return false;
oDraft.m_bShowPlane = m_pShowPlaneCheck->isChecked();
oDraft.m_bShowSource = m_pShowSourceCheck->isChecked();
oSettings = oDraft;
return true;
}
void nmWxVolumeSliceSettingsDlg::writeSettings(const nmVolumeSliceSettings& oSettings)
{
m_bUpdating = true;
for(int nAxis = 0; nAxis < 3; ++nAxis) {
m_pOriginEdits[nAxis]->setText(QString::number(oSettings.m_dOrigin[nAxis], 'g', 17));
m_pNormalEdits[nAxis]->setText(QString::number(oSettings.m_dNormal[nAxis], 'g', 17));
}
m_pShowPlaneCheck->setChecked(oSettings.m_bShowPlane);
m_pShowSourceCheck->setChecked(oSettings.m_bShowSource);
m_bUpdating = false;
updateValidation();
}
void nmWxVolumeSliceSettingsDlg::setPlanePreview(const nmVolumeSliceSettings& oSettings)
{
m_bUpdating = true;
for(int nAxis = 0; nAxis < 3; ++nAxis) {
m_pOriginEdits[nAxis]->setText(QString::number(oSettings.m_dOrigin[nAxis], 'g', 17));
m_pNormalEdits[nAxis]->setText(QString::number(oSettings.m_dNormal[nAxis], 'g', 17));
}
m_bUpdating = false;
updateValidation();
}
void nmWxVolumeSliceSettingsDlg::setError(const QString& sError)
{
m_pErrorLabel->setText(sError);
m_pErrorLabel->setVisible(!sError.isEmpty());
}
void nmWxVolumeSliceSettingsDlg::updateValidation()
{
nmVolumeSliceSettings oDraft;
QString sError;
m_pApplyButton->setEnabled(getDraft(oDraft, &sError));
setError(sError);
}
void nmWxVolumeSliceSettingsDlg::onDraftChanged()
{
if(m_bUpdating) return;
updateValidation();
if(m_pApplyButton->isEnabled()) emit previewChanged();
}
void nmWxVolumeSliceSettingsDlg::onAxisRequested()
{
const int nNormalAxis = sender()->property("normalAxis").toInt();
m_bUpdating = true;
for(int nAxis = 0; nAxis < 3; ++nAxis) m_pNormalEdits[nAxis]->setText(nAxis == nNormalAxis ? "1" : "0");
m_bUpdating = false;
onDraftChanged();
}
void nmWxVolumeSliceSettingsDlg::onCenterRequested()
{
if(m_pData.isNull()) return;
const nmVolumeSliceSettings oCenter = nmVolumeSliceSettings::fromData(*m_pData);
m_bUpdating = true;
for(int nAxis = 0; nAxis < 3; ++nAxis) m_pOriginEdits[nAxis]->setText(QString::number(oCenter.m_dOrigin[nAxis], 'g', 17));
m_bUpdating = false;
onDraftChanged();
}
void nmWxVolumeSliceSettingsDlg::onResetRequested()
{
writeSettings(m_oApplied);
emit previewChanged();
}
void nmWxVolumeSliceSettingsDlg::onApplyRequested()
{
updateValidation();
if(m_pApplyButton->isEnabled()) emit applyRequested();
}
void nmWxVolumeSliceSettingsDlg::onDisplayOptionsChanged()
{
if(m_bUpdating) return;
m_oApplied.m_bShowPlane = m_pShowPlaneCheck->isChecked();
m_oApplied.m_bShowSource = m_pShowSourceCheck->isChecked();
emit displayOptionsChanged(m_oApplied.m_bShowPlane, m_oApplied.m_bShowSource);
}
void nmWxVolumeSliceSettingsDlg::hideEvent(QHideEvent* pEvent)
{
// 最小化产生的系统隐藏并非用户关闭,保留草稿与绑定以便还原窗口后继续操作。
if(pEvent->spontaneous()) { iDlgBase::hideEvent(pEvent); return; }
writeSettings(m_oApplied);
iDlgBase::hideEvent(pEvent);
emit dialogHidden();
}