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.
318 lines
14 KiB
C++
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();
|
|
}
|