#include "nmWxIncludeOtherWells.h" #include "nmDataAnalyzeManager.h" #include "nmDataWellBase.h" #include "ZxBaseUtil.h" #include #include #include #include #include #include #include #include #include #include namespace { // Include 对话框只展示当前 PEBI 网格能够表达的真实井型。 bool isSupportedNumericalWell(nmDataWellBase* pWellData) { if(pWellData == nullptr || pWellData->getWellCode().isEmpty()) { return false; } const NM_WELL_MODEL eWellType = pWellData->getWellType(); return eWellType == NM_WELL_MODEL::Vertical_Well || eWellType == NM_WELL_MODEL::Vertical_Fractured_Well || eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well; } // 从同一条流量记录中取得指定相的只读点集,避免三处分支判断各自漂移。 const QVector& phasePoints( const nmFlowGaugeRecord& oRecord, NM_PHASE_TYPE ePhase) { switch(ePhase) { case PHASE_Gas: return oRecord.vecGasPoints; case PHASE_Water: return oRecord.vecWaterPoints; case PHASE_Oil: default: return oRecord.vecOilPoints; } } // “有该相产量”按至少一个非零值判断;只有公共零占位行不算真实产量。 bool hasNonZeroPhaseRate( const nmFlowGaugeRecord& oRecord, NM_PHASE_TYPE ePhase) { const QVector& vecPoints = phasePoints(oRecord, ePhase); for(int nIndex = 0; nIndex < vecPoints.size(); ++nIndex) { if(vecPoints[nIndex].y() != 0.0) { return true; } } return false; } // 下拉项只有在记录可用且该相确有非零产量时才允许选择。 bool isSelectableFlowPhase( const nmFlowGaugeRecord& oRecord, NM_PHASE_TYPE ePhase) { return oRecord.eStatus == NM_GaugeRecord_Usable && hasNonZeroPhaseRate(oRecord, ePhase); } // 候选井至少要有一条可选分相记录;压力记录不是主动井候选的必要条件。 bool hasSelectableRateRecord(const QVector& vecRecords) { for(int nIndex = 0; nIndex < vecRecords.size(); ++nIndex) { const nmFlowGaugeRecord& oRecord = vecRecords[nIndex]; if(isSelectableFlowPhase(oRecord, PHASE_Oil) || isSelectableFlowPhase(oRecord, PHASE_Gas) || isSelectableFlowPhase(oRecord, PHASE_Water)) { return true; } } return false; } // Gauge Code 是记录的稳定身份,显示名称和测量时间都不能替代它参与关联。 const nmFlowGaugeRecord* findFlowRecord( const QVector& vecRecords, const QString& sGaugeCode) { if(sGaugeCode.isEmpty()) { return nullptr; } for(int nIndex = 0; nIndex < vecRecords.size(); ++nIndex) { if(vecRecords[nIndex].sGaugeCode == sGaugeCode) { return &vecRecords[nIndex]; } } return nullptr; } nmFlowGaugeRecord* findMutableFlowRecord( QVector& vecRecords, const QString& sGaugeCode) { if(sGaugeCode.isEmpty()) { return nullptr; } for(int nIndex = 0; nIndex < vecRecords.size(); ++nIndex) { if(vecRecords[nIndex].sGaugeCode == sGaugeCode) { return &vecRecords[nIndex]; } } return nullptr; } // 压力曲线至少需要两个点才能用于历史曲线计算。 bool isSelectablePressureRecord(const nmPressureGaugeRecord& oRecord) { return oRecord.eStatus == NM_GaugeRecord_Usable && oRecord.vecPressurePoints.size() >= 2; } // 打开对话框时只恢复仍存在且仍可用的压力记录 Code。 bool containsSelectablePressureCode( const QVector& vecRecords, const QString& sGaugeCode) { for(int nIndex = 0; nIndex < vecRecords.size(); ++nIndex) { if(vecRecords[nIndex].sGaugeCode == sGaugeCode && isSelectablePressureRecord(vecRecords[nIndex])) { return true; } } return false; } // 下拉框只显示稳定的 Gauge Code,避免名称和测量时间挤占表格列宽。 // 异常旧数据没有 Code 时,才回退到框架名称或“未命名”。 QString gaugeRecordLabel(const QString& sGaugeName, const QString& sGaugeCode, const QString& sGaugeTime) { Q_UNUSED(sGaugeTime); QString sLabel = sGaugeCode.isEmpty() ? sGaugeName : sGaugeCode; if(sLabel.isEmpty()) { sLabel = nmWxIncludeOtherWells::tr("(unnamed)"); } return sLabel; } QString gaugeRecordToolTip(const QString& sGaugeName, const QString& sGaugeCode, const QString& sGaugeTime) { const QString sName = sGaugeName.trimmed().isEmpty() ? nmWxIncludeOtherWells::tr("(unnamed)") : sGaugeName; const QString sCode = sGaugeCode.trimmed().isEmpty() ? nmWxIncludeOtherWells::tr("None") : sGaugeCode; const QString sTime = sGaugeTime.trimmed().isEmpty() ? nmWxIncludeOtherWells::tr("None") : sGaugeTime; QStringList listLines; listLines << nmWxIncludeOtherWells::tr("Curve name: %1").arg(sName); listLines << nmWxIncludeOtherWells::tr("Gauge Code: %1").arg(sCode); listLines << nmWxIncludeOtherWells::tr("Measurement time: %1").arg(sTime); return listLines.join(QLatin1String("\n")); } void appendGaugeRecordItem(QComboBox* pComboBox, const QString& sGaugeName, const QString& sGaugeCode, const QString& sGaugeTime) { pComboBox->addItem(gaugeRecordLabel( sGaugeName, sGaugeCode, sGaugeTime), sGaugeCode); pComboBox->setItemData(pComboBox->count() - 1, gaugeRecordToolTip(sGaugeName, sGaugeCode, sGaugeTime), Qt::ToolTipRole); } void updateGaugeComboToolTip(QComboBox* pComboBox) { if(pComboBox == nullptr || pComboBox->currentIndex() < 0) { return; } QString sToolTip = pComboBox->itemData( pComboBox->currentIndex(), Qt::ToolTipRole).toString(); if(sToolTip.isEmpty()) { sToolTip = pComboBox->currentText(); } pComboBox->setToolTip(sToolTip); } // 流动段索引是一基索引;统计段数时必须先排除框架可能写入的公共占位行。 int flowRecordSegmentCount(const nmFlowGaugeRecord& oRecord) { const QVector* pPoints = !oRecord.vecOilPoints.isEmpty() ? &oRecord.vecOilPoints : (!oRecord.vecGasPoints.isEmpty() ? &oRecord.vecGasPoints : (!oRecord.vecWaterPoints.isEmpty() ? &oRecord.vecWaterPoints : nullptr)); if(pPoints == nullptr) { return 0; } bool bHasPlaceholder = false; if(oRecord.bMultiPhase) { bHasPlaceholder = !oRecord.vecOilPoints.isEmpty() && !oRecord.vecGasPoints.isEmpty() && !oRecord.vecWaterPoints.isEmpty() && oRecord.vecOilPoints.first() == QPointF(0.0, 0.0) && oRecord.vecGasPoints.first() == QPointF(0.0, 0.0) && oRecord.vecWaterPoints.first() == QPointF(0.0, 0.0); } else { bHasPlaceholder = pPoints->first() == QPointF(0.0, 0.0); } return pPoints->size() - (bHasPlaceholder ? 1 : 0); } // 井别只用于只读展示和默认参考相判断,不由油、气、水下拉选择反推。 QString wellCategoryText(NM_WELL_CATEGORY eWellCategory) { switch(eWellCategory) { case NM_WellCategory_Oil: return nmWxIncludeOtherWells::tr("Oil well"); case NM_WellCategory_Gas: return nmWxIncludeOtherWells::tr("Gas well"); case NM_WellCategory_Water: return nmWxIncludeOtherWells::tr("Water well"); default: return nmWxIncludeOtherWells::tr("Unknown"); } } // WellDataRow 将三个固定相保存为独立开关,这里统一完成枚举到成员的映射。 bool rowPhaseUsed(const WellDataRow& oData, NM_PHASE_TYPE ePhase) { switch(ePhase) { case PHASE_Oil: return oData.m_bUseOilRate; case PHASE_Gas: return oData.m_bUseGasRate; case PHASE_Water: return oData.m_bUseWaterRate; default: return false; } } void setRowPhaseUsed( WellDataRow& oData, NM_PHASE_TYPE ePhase, bool bUsed) { switch(ePhase) { case PHASE_Oil: oData.m_bUseOilRate = bUsed; break; case PHASE_Gas: oData.m_bUseGasRate = bUsed; break; case PHASE_Water: oData.m_bUseWaterRate = bUsed; break; default: break; } } } nmWxIncludeOtherWells::nmWxIncludeOtherWells( nmDataAnalyzeManager* pDataManager, QWidget* pParent) : iDlgBase(pParent), m_pDataManager(pDataManager), m_pTableWidget(nullptr), m_pFooterHelpButton(nullptr), m_pButtonBox(nullptr), m_pOkButton(nullptr), m_pCancelButton(nullptr), m_pButtonLayout(nullptr), m_pMainLayout(nullptr) { this->initUI(); } nmWxIncludeOtherWells::~nmWxIncludeOtherWells() { // Qt 的对象父子关系会自动处理控件释放。 } void nmWxIncludeOtherWells::initUI() { this->setupTable(); this->setupButtons(); this->setupLayouts(); this->setupConnections(); this->setWindowTitle(tr("Include Other Wells")); this->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // 初始尺寸跟随列宽和井数量,手动缩小时仍由滚动条保护表头及下拉内容。 this->setMinimumSize(720, 240); this->adjustDialogSizeToTable(); } void nmWxIncludeOtherWells::setupTable() { m_pTableWidget = new QTableWidget(this); m_pTableWidget->setColumnCount(ColumnCount); m_pTableWidget->setRowCount(0); // 本表以复选框和下拉框作为操作入口,不使用表格行选中态。 m_pTableWidget->setSelectionMode(QAbstractItemView::NoSelection); m_pTableWidget->setFocusPolicy(Qt::NoFocus); m_pTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); m_pTableWidget->setSortingEnabled(false); m_pTableWidget->setDragEnabled(false); m_pTableWidget->setAcceptDrops(false); m_pTableWidget->setDropIndicatorShown(false); m_pTableWidget->setAlternatingRowColors(true); m_pTableWidget->setWordWrap(false); m_pTableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); m_pTableWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); // 与编辑井流量明细表使用相同的调色板、表头和网格边框。 // 表格内下拉框采用透明无边框样式,使交替行色连续显示。 m_pTableWidget->setStyleSheet( "QTableWidget {" " background-color: palette(base);" " alternate-background-color: rgb(232, 232, 232);" " gridline-color: palette(midlight);" " border: 1px solid palette(mid);" "}" "QHeaderView {" " background-color: palette(button);" " color: palette(button-text);" "}" "QHeaderView::section {" " background-color: palette(button);" " color: palette(button-text);" " border: none;" " border-right: 1px solid palette(mid);" " border-bottom: 1px solid palette(mid);" "}" "QTableCornerButton::section {" " background-color: palette(base);" " border: none;" " border-right: 1px solid palette(mid);" " border-bottom: 1px solid palette(mid);" "}" "QTableWidget QComboBox {" " border: none;" " background: transparent;" " padding-left: 6px;" "}" "QTableWidget QComboBox::drop-down {" " border: none;" "}"); QStringList listHeaders; listHeaders << tr("Name") << tr("Well category") << tr("Included") << tr("Oil production") << tr("Gas production") << tr("Water production") << tr("Pressure") << tr("Flow segment"); m_pTableWidget->setHorizontalHeaderLabels(listHeaders); m_pTableWidget->horizontalHeader()->setVisible(true); m_pTableWidget->horizontalHeader()->setMovable(false); m_pTableWidget->horizontalHeader()->setMinimumSectionSize(64); m_pTableWidget->horizontalHeader()->setStretchLastSection(false); m_pTableWidget->horizontalHeader()->setResizeMode( ColumnName, QHeaderView::Stretch); m_pTableWidget->horizontalHeader()->setResizeMode( ColumnWellCategory, QHeaderView::ResizeToContents); m_pTableWidget->horizontalHeader()->setResizeMode( ColumnIncluded, QHeaderView::ResizeToContents); m_pTableWidget->horizontalHeader()->setResizeMode( ColumnOilRate, QHeaderView::ResizeToContents); m_pTableWidget->horizontalHeader()->setResizeMode( ColumnGasRate, QHeaderView::ResizeToContents); m_pTableWidget->horizontalHeader()->setResizeMode( ColumnWaterRate, QHeaderView::ResizeToContents); m_pTableWidget->horizontalHeader()->setResizeMode( ColumnPressure, QHeaderView::ResizeToContents); m_pTableWidget->horizontalHeader()->setResizeMode( ColumnFlowSegment, QHeaderView::ResizeToContents); m_pTableWidget->verticalHeader()->setVisible(true); m_pTableWidget->verticalHeader()->setMovable(false); m_pTableWidget->verticalHeader()->setDefaultAlignment(Qt::AlignCenter); m_pTableWidget->verticalHeader()->setDefaultSectionSize(32); m_pTableWidget->verticalHeader()->setMinimumSectionSize(28); QVector vecMapWells; if(m_pDataManager != nullptr) { vecMapWells = m_pDataManager->getWellDataList(); } const QString sPrimaryWellCode = m_pDataManager != nullptr ? m_pDataManager->getPrimaryWellCode() : QString(); // 候选来源仍严格限定为 Map 中的其他主动井;只把选择界面迁移到这里。 for(int nIndex = 0; nIndex < vecMapWells.size(); ++nIndex) { nmDataWellBase* pWellData = vecMapWells[nIndex]; if(!isSupportedNumericalWell(pWellData) || pWellData->getWellCode() == sPrimaryWellCode || !nmIsValidWellCategory(pWellData->getWellCategory())) { continue; } WellDataRow oWellRow; oWellRow.m_sWellCode = pWellData->getWellCode(); oWellRow.m_sName = pWellData->getWellName(); oWellRow.m_bIsPrimary = false; oWellRow.m_eWellCategory = pWellData->getWellCategory(); oWellRow.m_eMode = NM_CaseWell_RateControlled; oWellRow.m_vecPressureRecords = pWellData->getPressureRecords(); oWellRow.m_vecFlowRecords = pWellData->getFlowRecords(); oWellRow.m_bCanCalculate = hasSelectableRateRecord(oWellRow.m_vecFlowRecords); if(!oWellRow.m_bCanCalculate) { continue; } oWellRow.m_sPressureGaugeCode = pWellData->getSelectedPressureGaugeCode(); if(!containsSelectablePressureCode( oWellRow.m_vecPressureRecords, oWellRow.m_sPressureGaugeCode)) { oWellRow.m_sPressureGaugeCode.clear(); } oWellRow.m_sFlowGaugeCode = pWellData->getSelectedFlowGaugeCode(); oWellRow.m_bUseOilRate = pWellData->getUseOilRate(); oWellRow.m_bUseGasRate = pWellData->getUseGasRate(); oWellRow.m_bUseWaterRate = pWellData->getUseWaterRate(); oWellRow.m_nFlowSegmentIndex = pWellData->getIndexF(); const bool bHadRequestedPhase = oWellRow.m_bUseOilRate || oWellRow.m_bUseGasRate || oWellRow.m_bUseWaterRate; normalizeFlowSelection(oWellRow); // 新井默认启用三相;若框架当前选择失效,回退到第一条有产量记录。 // 用户已明确把三相都设为“无”时不自动恢复。 if(!oWellRow.canCalculate() && bHadRequestedPhase) { for(int nRecordIndex = 0; nRecordIndex < oWellRow.m_vecFlowRecords.size(); ++nRecordIndex) { const nmFlowGaugeRecord& oRecord = oWellRow.m_vecFlowRecords[nRecordIndex]; if(oRecord.eStatus != NM_GaugeRecord_Usable) { continue; } oWellRow.m_sFlowGaugeCode = oRecord.sGaugeCode; oWellRow.m_bUseOilRate = isSelectableFlowPhase(oRecord, PHASE_Oil); oWellRow.m_bUseGasRate = isSelectableFlowPhase(oRecord, PHASE_Gas); oWellRow.m_bUseWaterRate = isSelectableFlowPhase(oRecord, PHASE_Water); normalizeFlowSelection(oWellRow); if(oWellRow.canCalculate()) { break; } } } oWellRow.m_bIsIncluded = oWellRow.canCalculate() && m_pDataManager->getNumericalAnalysisCase()->isIncludedWell( oWellRow.m_sWellCode); const int nRow = m_vecWellData.size(); m_vecWellData.append(oWellRow); m_pTableWidget->setRowCount(nRow + 1); addWellDataToTable(oWellRow, nRow); } if(m_vecWellData.isEmpty()) { m_pTableWidget->setRowCount(1); m_pTableWidget->setSpan(0, 0, 1, ColumnCount); QTableWidgetItem* pEmptyItem = new QTableWidgetItem( tr("No eligible wells")); pEmptyItem->setFlags(Qt::NoItemFlags); pEmptyItem->setTextAlignment(Qt::AlignCenter); m_pTableWidget->setItem(0, 0, pEmptyItem); // 空状态行不是候选井,不显示容易产生误解的行号“1”。 m_pTableWidget->verticalHeader()->setVisible(false); } else { // Qt 默认按一基显示纵向表头;宽度随最大行号位数增长。 const int nRowNumberWidth = m_pTableWidget->fontMetrics().width( QString::number(m_vecWellData.size())) + 18; m_pTableWidget->verticalHeader()->setFixedWidth( qMax(32, nRowNumberWidth)); } // 单元格控件全部创建完成后再按真实表头和下拉内容计算列宽。 m_pTableWidget->resizeColumnsToContents(); } void nmWxIncludeOtherWells::adjustDialogSizeToTable() { if(m_pTableWidget == nullptr || m_pMainLayout == nullptr) { return; } int nTableWidth = m_pTableWidget->frameWidth() * 2; if(m_pTableWidget->verticalHeader()->isVisible()) { nTableWidth += m_pTableWidget->verticalHeader()->width(); } for(int nColumn = 0; nColumn < ColumnCount; ++nColumn) { nTableWidth += m_pTableWidget->columnWidth(nColumn); } const int nVisibleRows = qMin(qMax(1, m_pTableWidget->rowCount()), 10); int nTableHeight = m_pTableWidget->frameWidth() * 2 + m_pTableWidget->horizontalHeader()->sizeHint().height() + nVisibleRows * m_pTableWidget->verticalHeader()->defaultSectionSize(); const int nScrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent); if(m_pTableWidget->rowCount() > nVisibleRows) { nTableWidth += nScrollBarExtent; } int nLeft = 0; int nTop = 0; int nRight = 0; int nBottom = 0; m_pMainLayout->getContentsMargins(&nLeft, &nTop, &nRight, &nBottom); const int nLayoutWidth = nLeft + nRight; const int nButtonHeight = qMax(m_pFooterHelpButton->sizeHint().height(), qMax(m_pOkButton->sizeHint().height(), m_pCancelButton->sizeHint().height())); const int nLayoutHeight = nTop + nBottom + nButtonHeight + m_pMainLayout->spacing(); QRect oAvailableGeometry; QDesktopWidget* pDesktop = QApplication::desktop(); if(pDesktop != nullptr) { oAvailableGeometry = pDesktop->availableGeometry(this); } if(!oAvailableGeometry.isValid()) { oAvailableGeometry = QRect(0, 0, 1280, 720); } const int nMaximumWidth = qMax(640, oAvailableGeometry.width() * 9 / 10); const int nMaximumHeight = qMax(260, oAvailableGeometry.height() * 4 / 5); int nDialogWidth = qMin(nTableWidth + nLayoutWidth, nMaximumWidth); if(nTableWidth + nLayoutWidth > nDialogWidth) { nTableHeight += nScrollBarExtent; } const int nDialogHeight = qMin(nTableHeight + nLayoutHeight, nMaximumHeight); resize(qMax(minimumWidth(), nDialogWidth), qMax(minimumHeight(), nDialogHeight)); } void nmWxIncludeOtherWells::addWellDataToTable( const WellDataRow& data, int row) { QTableWidgetItem* pNameItem = new QTableWidgetItem(data.m_sName); pNameItem->setData(Qt::UserRole, data.m_sWellCode); pNameItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); pNameItem->setToolTip(data.m_sName); m_pTableWidget->setItem(row, ColumnName, pNameItem); QTableWidgetItem* pCategoryItem = new QTableWidgetItem( wellCategoryText(data.m_eWellCategory)); pCategoryItem->setTextAlignment(Qt::AlignCenter); m_pTableWidget->setItem(row, ColumnWellCategory, pCategoryItem); QWidget* pCheckContainer = new QWidget(m_pTableWidget); QCheckBox* pCheckBox = new QCheckBox(pCheckContainer); QHBoxLayout* pCheckLayout = new QHBoxLayout(pCheckContainer); pCheckLayout->setContentsMargins(0, 0, 0, 0); pCheckLayout->setAlignment(Qt::AlignCenter); pCheckLayout->addWidget(pCheckBox); pCheckBox->setProperty("row", row); connect(pCheckBox, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxStateChanged(int))); m_pTableWidget->setCellWidget(row, ColumnIncluded, pCheckContainer); const int arrRateColumns[] = { ColumnOilRate, ColumnGasRate, ColumnWaterRate }; const NM_PHASE_TYPE arrPhases[] = { PHASE_Oil, PHASE_Gas, PHASE_Water }; for(int nIndex = 0; nIndex < 3; ++nIndex) { QComboBox* pRateCombo = new QComboBox(m_pTableWidget); pRateCombo->setProperty("row", row); pRateCombo->setProperty("phase", static_cast(arrPhases[nIndex])); pRateCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); pRateCombo->setMaxVisibleItems(16); connect(pRateCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onRateSourceChanged(int))); m_pTableWidget->setCellWidget(row, arrRateColumns[nIndex], pRateCombo); } QComboBox* pPressureCombo = new QComboBox(m_pTableWidget); pPressureCombo->setProperty("row", row); pPressureCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); pPressureCombo->setMaxVisibleItems(16); connect(pPressureCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onPressureSourceChanged(int))); m_pTableWidget->setCellWidget(row, ColumnPressure, pPressureCombo); QComboBox* pSegmentCombo = new QComboBox(m_pTableWidget); pSegmentCombo->setProperty("row", row); pSegmentCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); connect(pSegmentCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onFlowSegmentChanged(int))); m_pTableWidget->setCellWidget(row, ColumnFlowSegment, pSegmentCombo); refreshRowEditors(row); } void nmWxIncludeOtherWells::normalizeFlowSelection(WellDataRow& data) const { const nmFlowGaugeRecord* pRecord = findFlowRecord( data.m_vecFlowRecords, data.m_sFlowGaugeCode); if(pRecord == nullptr || pRecord->eStatus != NM_GaugeRecord_Usable) { data.m_sFlowGaugeCode.clear(); data.m_bUseOilRate = false; data.m_bUseGasRate = false; data.m_bUseWaterRate = false; data.m_nFlowSegmentIndex = 0; return; } data.m_bUseOilRate = data.m_bUseOilRate && isSelectableFlowPhase(*pRecord, PHASE_Oil); data.m_bUseGasRate = data.m_bUseGasRate && isSelectableFlowPhase(*pRecord, PHASE_Gas); data.m_bUseWaterRate = data.m_bUseWaterRate && isSelectableFlowPhase(*pRecord, PHASE_Water); if(!data.m_bUseOilRate && !data.m_bUseGasRate && !data.m_bUseWaterRate) { data.m_sFlowGaugeCode.clear(); data.m_nFlowSegmentIndex = 0; return; } const int nSegmentCount = flowRecordSegmentCount(*pRecord); if(nSegmentCount <= 0) { data.m_sFlowGaugeCode.clear(); data.m_bUseOilRate = false; data.m_bUseGasRate = false; data.m_bUseWaterRate = false; data.m_nFlowSegmentIndex = 0; return; } if(data.m_nFlowSegmentIndex < 1 || data.m_nFlowSegmentIndex > nSegmentCount) { data.m_nFlowSegmentIndex = pRecord->nIndexF; if(data.m_nFlowSegmentIndex < 1 || data.m_nFlowSegmentIndex > nSegmentCount) { data.m_nFlowSegmentIndex = nSegmentCount; } } nmFlowGaugeRecord* pMutableRecord = findMutableFlowRecord( data.m_vecFlowRecords, data.m_sFlowGaugeCode); if(pMutableRecord != nullptr) { pMutableRecord->nIndexF = data.m_nFlowSegmentIndex; } } void nmWxIncludeOtherWells::switchRowFlowGaugeCode( WellDataRow& data, const QString& sGaugeCode) const { // 行数据是对话框工作副本,切换前先保存当前记录自己的流动段。 nmFlowGaugeRecord* pOldRecord = findMutableFlowRecord( data.m_vecFlowRecords, data.m_sFlowGaugeCode); if(pOldRecord != nullptr) { pOldRecord->nIndexF = data.m_nFlowSegmentIndex; } data.m_sFlowGaugeCode = sGaugeCode; nmFlowGaugeRecord* pNewRecord = findMutableFlowRecord( data.m_vecFlowRecords, data.m_sFlowGaugeCode); if(pNewRecord == nullptr || pNewRecord->eStatus != NM_GaugeRecord_Usable) { data.m_nFlowSegmentIndex = 0; return; } const int nSegmentCount = flowRecordSegmentCount(*pNewRecord); data.m_nFlowSegmentIndex = pNewRecord->nIndexF; if(data.m_nFlowSegmentIndex < 1 || data.m_nFlowSegmentIndex > nSegmentCount) { data.m_nFlowSegmentIndex = nSegmentCount; } } void nmWxIncludeOtherWells::populateRateCombo( QComboBox* pComboBox, const WellDataRow& data, NM_PHASE_TYPE ePhase) const { if(pComboBox == nullptr) { return; } const bool bSignalsWereBlocked = pComboBox->blockSignals(true); pComboBox->clear(); pComboBox->addItem(tr("None"), QString()); pComboBox->setItemData(0, tr("None"), Qt::ToolTipRole); // 选择控件只展示当前相真正可用的记录;异常记录仍可在“检查数据”中查看。 for(int nIndex = 0; nIndex < data.m_vecFlowRecords.size(); ++nIndex) { const nmFlowGaugeRecord& oRecord = data.m_vecFlowRecords[nIndex]; if(!isSelectableFlowPhase(oRecord, ePhase)) { continue; } appendGaugeRecordItem(pComboBox, oRecord.sGaugeName, oRecord.sGaugeCode, oRecord.sGaugeTime); } int nCurrentIndex = 0; if(rowPhaseUsed(data, ePhase)) { const int nFoundIndex = pComboBox->findData(data.m_sFlowGaugeCode); if(nFoundIndex > 0) { nCurrentIndex = nFoundIndex; } } pComboBox->setCurrentIndex(nCurrentIndex); updateGaugeComboToolTip(pComboBox); pComboBox->blockSignals(bSignalsWereBlocked); } void nmWxIncludeOtherWells::populatePressureCombo( QComboBox* pComboBox, const WellDataRow& data) const { if(pComboBox == nullptr) { return; } const bool bSignalsWereBlocked = pComboBox->blockSignals(true); pComboBox->clear(); pComboBox->addItem(tr("None"), QString()); pComboBox->setItemData(0, tr("None"), Qt::ToolTipRole); for(int nIndex = 0; nIndex < data.m_vecPressureRecords.size(); ++nIndex) { const nmPressureGaugeRecord& oRecord = data.m_vecPressureRecords[nIndex]; if(!isSelectablePressureRecord(oRecord)) { continue; } appendGaugeRecordItem(pComboBox, oRecord.sGaugeName, oRecord.sGaugeCode, oRecord.sGaugeTime); } const int nCurrentIndex = pComboBox->findData( data.m_sPressureGaugeCode); pComboBox->setCurrentIndex(nCurrentIndex < 0 ? 0 : nCurrentIndex); updateGaugeComboToolTip(pComboBox); pComboBox->blockSignals(bSignalsWereBlocked); } void nmWxIncludeOtherWells::populateFlowSegmentCombo( QComboBox* pComboBox, const WellDataRow& data) const { if(pComboBox == nullptr) { return; } const bool bSignalsWereBlocked = pComboBox->blockSignals(true); pComboBox->clear(); const nmFlowGaugeRecord* pRecord = findFlowRecord( data.m_vecFlowRecords, data.m_sFlowGaugeCode); const int nSegmentCount = pRecord == nullptr ? 0 : flowRecordSegmentCount(*pRecord); for(int nIndex = 1; nIndex <= nSegmentCount; ++nIndex) { pComboBox->addItem(tr("Segment %1").arg(nIndex), nIndex); } const int nCurrentIndex = pComboBox->findData( data.m_nFlowSegmentIndex); if(nCurrentIndex >= 0) { pComboBox->setCurrentIndex(nCurrentIndex); } pComboBox->setEnabled(data.canCalculate()); pComboBox->setToolTip(pComboBox->currentText()); pComboBox->blockSignals(bSignalsWereBlocked); } void nmWxIncludeOtherWells::refreshRowEditors(int row) { if(row < 0 || row >= m_vecWellData.size()) { return; } const WellDataRow& oData = m_vecWellData[row]; populateRateCombo(qobject_cast( m_pTableWidget->cellWidget(row, ColumnOilRate)), oData, PHASE_Oil); populateRateCombo(qobject_cast( m_pTableWidget->cellWidget(row, ColumnGasRate)), oData, PHASE_Gas); populateRateCombo(qobject_cast( m_pTableWidget->cellWidget(row, ColumnWaterRate)), oData, PHASE_Water); populatePressureCombo(qobject_cast( m_pTableWidget->cellWidget(row, ColumnPressure)), oData); populateFlowSegmentCombo(qobject_cast( m_pTableWidget->cellWidget(row, ColumnFlowSegment)), oData); updateIncludeCheckBox(row); } void nmWxIncludeOtherWells::updateIncludeCheckBox(int row) { if(row < 0 || row >= m_vecWellData.size()) { return; } WellDataRow& oData = m_vecWellData[row]; if(!oData.canCalculate()) { oData.m_bIsIncluded = false; } QWidget* pContainer = m_pTableWidget->cellWidget(row, ColumnIncluded); QCheckBox* pCheckBox = pContainer == nullptr ? nullptr : pContainer->findChild(); if(pCheckBox == nullptr) { return; } const bool bSignalsWereBlocked = pCheckBox->blockSignals(true); pCheckBox->setEnabled(oData.m_bCanCalculate); pCheckBox->setChecked(oData.m_bIsIncluded); pCheckBox->setToolTip(oData.canCalculate() ? QString() : tr("Select at least one production record first.")); pCheckBox->blockSignals(bSignalsWereBlocked); } void nmWxIncludeOtherWells::onCheckBoxStateChanged(int state) { QCheckBox* pCheckBox = qobject_cast(sender()); if(pCheckBox == nullptr) { return; } const int nRow = pCheckBox->property("row").toInt(); if(nRow < 0 || nRow >= m_vecWellData.size()) { return; } WellDataRow& oData = m_vecWellData[nRow]; if(oData.m_bIsPrimary || (state == Qt::Checked && !oData.canCalculate())) { oData.m_bIsIncluded = false; updateIncludeCheckBox(nRow); return; } oData.m_bIsIncluded = state == Qt::Checked; } void nmWxIncludeOtherWells::onRateSourceChanged(int nIndex) { QComboBox* pComboBox = qobject_cast(sender()); if(pComboBox == nullptr || nIndex < 0) { return; } const int nRow = pComboBox->property("row").toInt(); const NM_PHASE_TYPE ePhase = static_cast( pComboBox->property("phase").toInt()); if(nRow < 0 || nRow >= m_vecWellData.size()) { return; } WellDataRow& oData = m_vecWellData[nRow]; // 可见文本只用于展示,真正写回始终读取 itemData 中的 Gauge Code。 const QString sGaugeCode = pComboBox->itemData(nIndex).toString(); if(sGaugeCode.isEmpty()) { setRowPhaseUsed(oData, ePhase, false); if(!oData.m_bUseOilRate && !oData.m_bUseGasRate && !oData.m_bUseWaterRate) { switchRowFlowGaugeCode(oData, QString()); } } else { const nmFlowGaugeRecord* pRecord = findFlowRecord( oData.m_vecFlowRecords, sGaugeCode); if(pRecord == nullptr || !isSelectableFlowPhase(*pRecord, ePhase)) { refreshRowEditors(nRow); return; } if(oData.m_sFlowGaugeCode != sGaugeCode) { switchRowFlowGaugeCode(oData, sGaugeCode); } setRowPhaseUsed(oData, ePhase, true); } // PEBI 每口井只有一条 Rate.t。切换任一相的记录后,其他已启用相 // 自动使用同一记录;新记录没有的相自动回到“无”。 normalizeFlowSelection(oData); refreshRowEditors(nRow); } void nmWxIncludeOtherWells::onPressureSourceChanged(int nIndex) { QComboBox* pComboBox = qobject_cast(sender()); if(pComboBox == nullptr || nIndex < 0) { return; } const int nRow = pComboBox->property("row").toInt(); if(nRow < 0 || nRow >= m_vecWellData.size()) { return; } m_vecWellData[nRow].m_sPressureGaugeCode = pComboBox->itemData(nIndex).toString(); updateGaugeComboToolTip(pComboBox); } void nmWxIncludeOtherWells::onFlowSegmentChanged(int nIndex) { QComboBox* pComboBox = qobject_cast(sender()); if(pComboBox == nullptr || nIndex < 0) { return; } const int nRow = pComboBox->property("row").toInt(); if(nRow < 0 || nRow >= m_vecWellData.size()) { return; } WellDataRow& oData = m_vecWellData[nRow]; oData.m_nFlowSegmentIndex = pComboBox->itemData(nIndex).toInt(); nmFlowGaugeRecord* pRecord = findMutableFlowRecord( oData.m_vecFlowRecords, oData.m_sFlowGaugeCode); if(pRecord != nullptr) { pRecord->nIndexF = oData.m_nFlowSegmentIndex; } pComboBox->setToolTip(pComboBox->currentText()); } QVector nmWxIncludeOtherWells::getWellData() const { return m_vecWellData; } void nmWxIncludeOtherWells::setupButtons() { m_pFooterHelpButton = new QToolButton(this); m_pFooterHelpButton->setText(tr("Help")); m_pFooterHelpButton->setIcon(zxLoadIcon("Help")); m_pFooterHelpButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); m_pFooterHelpButton->setAutoRaise(true); m_pButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); m_pOkButton = m_pButtonBox->button(QDialogButtonBox::Ok); m_pCancelButton = m_pButtonBox->button(QDialogButtonBox::Cancel); if(m_pOkButton != nullptr) { m_pOkButton->setIcon(zxLoadIcon("OK")); m_pOkButton->setText(tr("OK")); } if(m_pCancelButton != nullptr) { m_pCancelButton->setIcon(zxLoadIcon("Cancel")); m_pCancelButton->setText(tr("Cancel")); } } void nmWxIncludeOtherWells::setupLayouts() { m_pButtonLayout = new QHBoxLayout(); m_pButtonLayout->addWidget(m_pFooterHelpButton); m_pButtonLayout->addStretch(); m_pButtonLayout->addWidget(m_pButtonBox); m_pMainLayout = new QVBoxLayout(this); m_pMainLayout->addWidget(m_pTableWidget, 1); m_pMainLayout->addLayout(m_pButtonLayout); setLayout(m_pMainLayout); } void nmWxIncludeOtherWells::setupConnections() { connect(m_pFooterHelpButton, SIGNAL(clicked()), this, SLOT(slotHelp())); connect(m_pButtonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(m_pButtonBox, SIGNAL(rejected()), this, SLOT(reject())); } void nmWxIncludeOtherWells::slotHelp() { ZxBaseUtil::startHelp(); }