#include "nmWxMeasureDlg.h" #include "iUnitWxBinder.h" #include "nmDataAnalyzeManager.h" #include "nmDataMeasure.h" #include "nmDataWellBase.h" #include "nmGuiPlot.h" #include "ZxBaseUtil.h" #include #include #include #include #include #include #include #include #include #include #include nmWxMeasureDlg::nmWxMeasureDlg(QWidget* parent) : iDlgBase(parent), m_pLengthEdit(nullptr), m_pFitStartButton(nullptr), m_pFitEndButton(nullptr), m_bUnitBound(false) { // 浮动窗口必须绑定父级画布的分析,不能随全局当前页签变化而串用数据。 nmGuiPlot* pOwnerPlot = qobject_cast(parent); if (pOwnerPlot != nullptr) { m_pDataManager = pOwnerPlot->boundDataManager(); } if (!m_pDataManager.isNull()) { m_pData = m_pDataManager->getMeasureData(); } initUI(); updateDisplay(); setWindowTitle(tr("Measure")); // 保留浮动置顶行为,便于在绘图区连续测量,同时移除无帮助内容的问号按钮。 setWindowFlags((windowFlags() | Qt::WindowStaysOnTopHint) & ~Qt::WindowContextHelpButtonHint); // 初始大小由字体、主题及布局推荐尺寸决定;adjustSize 同时考虑屏幕尺寸。 // 这里只设置初始大小,不限制用户之后调整窗口。 adjustSize(); } void nmWxMeasureDlg::initUI() { // 与比例尺设置保持相同的外边距和分组间距,控件尺寸交给字体与主题计算。 QVBoxLayout* pMainLayout = new QVBoxLayout(this); pMainLayout->setContentsMargins(12, 10, 12, 0); pMainLayout->setSpacing(10); QGroupBox* pGroup = new QGroupBox(tr("Measurement results"), this); // 分组只横向伸展,内容高度由字体和主题的推荐尺寸决定,避免纵向拉长控件或行间距。 pGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); QGridLayout* pGrid = new QGridLayout(pGroup); pGrid->setContentsMargins(8, 15, 8, 10); pGrid->setHorizontalSpacing(6); pGrid->setVerticalSpacing(6); // 沿用储层参数常规字段的三列规则:标签按内容,输入框与单位下拉框按 8:5 伸展。 pGrid->setColumnStretch(0, 0); pGrid->setColumnStretch(1, 8); pGrid->setColumnStretch(2, 5); QLabel* pLengthLabel = new QLabel(tr("Length"), pGroup); pLengthLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); pLengthLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); pGrid->addWidget(pLengthLabel, 0, 0); // 长度只能由测量和端点拟合产生;只读而非禁用,允许用户选中、复制结果。 m_pLengthEdit = new QLineEdit(pGroup); m_pLengthEdit->setObjectName("measureLength"); m_pLengthEdit->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); m_pLengthEdit->setReadOnly(true); // 纵向 Fixed 使用当前字体和主题的推荐高度,不设置固定像素高度。 m_pLengthEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); pGrid->addWidget(m_pLengthEdit, 0, 1); QComboBox* pLengthUnit = new QComboBox(pGroup); pLengthUnit->setObjectName("measureLengthUnit"); // 推荐及最小宽度按完整单位列表和主题箭头计算,窗口拉宽时与输入框一起伸展。 pLengthUnit->setSizeAdjustPolicy(QComboBox::AdjustToContents); pLengthUnit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); pGrid->addWidget(pLengthUnit, 0, 2); // 由米识别框架长度单位组,不借用井参数及其范围,也不读取模型的旧手写单位列表。 // 单位切换和显示精度交给绑定器处理,不将显示单位或显示数值写回测量模型。 pLengthUnit->addItem("m"); m_bUnitBound = bindUnitPairWxs(m_pLengthEdit, pLengthUnit, QString(), false); m_bUnitBound = m_bUnitBound && m_pUnitBinder != nullptr; m_pLengthEdit->setEnabled(m_bUnitBound); pLengthUnit->setEnabled(m_bUnitBound); m_pFitStartButton = new QPushButton(tr("Fit start to nearest well"), pGroup); m_pFitEndButton = new QPushButton(tr("Fit end to nearest well"), pGroup); // 拟合属于显式操作,不设为默认按钮,避免复制长度时按回车触发端点变化。 m_pFitStartButton->setAutoDefault(false); m_pFitEndButton->setAutoDefault(false); m_pFitStartButton->setEnabled(false); m_pFitEndButton->setEnabled(false); m_pFitStartButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); m_pFitEndButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); // 两个端点操作各占一行,横跨字段区,沿用分组的统一行间距。 pGrid->addWidget(m_pFitStartButton, 1, 0, 1, 3); pGrid->addWidget(m_pFitEndButton, 2, 0, 1, 3); connect(m_pFitStartButton, SIGNAL(clicked()), this, SLOT(onFitStartClicked())); connect(m_pFitEndButton, SIGNAL(clicked()), this, SLOT(onFitEndClicked())); pMainLayout->addWidget(pGroup); // 纵向余量由分组与底部按钮之间的布局空间吸收,内容保持紧凑排列。 pMainLayout->addStretch(1); // 底部沿用标准按钮框、框架图标及统一留白。 QWidget* pFooter = new QWidget(this); pFooter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); QHBoxLayout* pFooterLayout = new QHBoxLayout(pFooter); pFooterLayout->setContentsMargins(4, 5, 4, 5); pFooterLayout->setSpacing(8); pFooterLayout->addStretch(); QDialogButtonBox* pButtons = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal, pFooter); QPushButton* pClose = pButtons->button(QDialogButtonBox::Close); pClose->setText(tr("Close")); pClose->setIcon(zxLoadIcon("Cancel")); pClose->setAutoDefault(false); pFooterLayout->addWidget(pButtons); pMainLayout->addWidget(pFooter); connect(pButtons, SIGNAL(rejected()), this, SLOT(onCloseClicked())); } void nmWxMeasureDlg::onFitStartClicked() { fitToNearestWell(true); } void nmWxMeasureDlg::onFitEndClicked() { fitToNearestWell(false); } void nmWxMeasureDlg::fitToNearestWell(bool bFitStart) { if (m_pDataManager.isNull() || m_pData.isNull()) { return; } // 每次读取所属分析的最新井列表;无井时保留测量点和按钮状态。 const QVector vecWells = m_pDataManager->getWellDataList(); const QPointF current = m_pData->getCurrentPoint(); // 沿用测量模型约定:(0, 0) 表示没有待处理的单点,按已有线段端点拟合。 const bool bSinglePoint = !(qFuzzyCompare(current.x(), 0.0) && qFuzzyCompare(current.y(), 0.0)); const QPointF reference = bSinglePoint ? current : (bFitStart ? m_pData->getStartPoint() : m_pData->getEndPoint()); double dMinDistance = std::numeric_limits::max(); QPointF nearest; bool bFound = false; for (int nIndex = 0; nIndex < vecWells.size(); ++nIndex) { nmDataWellBase* pWell = vecWells[nIndex]; if (pWell == nullptr) { continue; } const QPointF wellPoint(pWell->getX().getValue().toDouble(), pWell->getY().getValue().toDouble()); const double dDistance = QLineF(reference, wellPoint).length(); if (dDistance < dMinDistance) { dMinDistance = dDistance; nearest = wellPoint; bFound = true; } } if (!bFound) { return; } // 单点测量中,未拟合的一端保留点击位置;普通线段只移动指定端点。 if (bFitStart) { m_pFitStartButton->setEnabled(false); m_pData->setStartPoint(nearest); if (bSinglePoint) { m_pData->setEndPoint(current); } } else { m_pFitEndButton->setEnabled(false); if (bSinglePoint) { m_pData->setStartPoint(current); } m_pData->setEndPoint(nearest); } // 几何计算使用模型中的米制坐标,不读取可能已切为英尺等单位的界面文本。 const double dLength = QLineF(m_pData->getStartPoint(), m_pData->getEndPoint()).length(); nmDataAttribute length = m_pData->getLength(); length.setValue(dLength); m_pData->setLength(length); m_pData->setCurrentPoint(QPointF(0, 0)); updateDisplay(); } void nmWxMeasureDlg::updateDisplay() { if (m_pData.isNull()) { m_pLengthEdit->clear(); m_pFitStartButton->setEnabled(false); m_pFitEndButton->setEnabled(false); return; } if (m_bUnitBound) { // 更新基准值而不重建单位列表,连续测量和拟合后仍保留当前显示单位。 m_pUnitBinder->setBaseValue(m_pLengthEdit, m_pData->getLength().getValue().toDouble()); } else { m_pLengthEdit->clear(); } } void nmWxMeasureDlg::closeEvent(QCloseEvent* event) { emit needDeleteMeasureObj(); iDlgBase::closeEvent(event); } void nmWxMeasureDlg::onCloseClicked() { emit needDeleteMeasureObj(); accept(); } void nmWxMeasureDlg::resetButtons() { const bool bCanFit = !m_pDataManager.isNull() && !m_pData.isNull(); m_pFitStartButton->setEnabled(bCanFit); m_pFitEndButton->setEnabled(bCanFit); }