|
|
|
|
|
#include "nmWxMeasureDlg.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "iUnitWxBinder.h"
|
|
|
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
|
|
|
#include "nmDataMeasure.h"
|
|
|
|
|
|
#include "nmDataWellBase.h"
|
|
|
|
|
|
#include "nmGuiPlot.h"
|
|
|
|
|
|
#include "ZxBaseUtil.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <QComboBox>
|
|
|
|
|
|
#include <QDialogButtonBox>
|
|
|
|
|
|
#include <QGridLayout>
|
|
|
|
|
|
#include <QGroupBox>
|
|
|
|
|
|
#include <QHBoxLayout>
|
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
|
#include <QLineF>
|
|
|
|
|
|
#include <QPushButton>
|
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
|
|
|
|
nmWxMeasureDlg::nmWxMeasureDlg(QWidget* parent)
|
|
|
|
|
|
: iDlgBase(parent),
|
|
|
|
|
|
m_pLengthEdit(nullptr),
|
|
|
|
|
|
m_pFitStartButton(nullptr),
|
|
|
|
|
|
m_pFitEndButton(nullptr),
|
|
|
|
|
|
m_bUnitBound(false)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 浮动窗口必须绑定父级画布的分析,不能随全局当前页签变化而串用数据。
|
|
|
|
|
|
nmGuiPlot* pOwnerPlot = qobject_cast<nmGuiPlot*>(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<nmDataWellBase*> 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<double>::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);
|
|
|
|
|
|
}
|