#include "zxLogInstance.h" #include "ZxBaHelper.h" #include #include "ZxResolutionHelper.h" #include "zxSysUtils.h" #include "ZxPtyDock.h" #include "iDockBaseWx.h" #include "ZxMainWindow.h" #include "ZxTabWidget.h" #include "ZxSubAxisX.h" #include "ZxSubAxisY.h" #include "ZxPlot.h" #include "ZxObjCurve.h" #include "ZxSubTitle.h" #include "tCurvePlotScene.h" #include "tCurvePlotView.h" #include "ZxTableView.h" #include "ZxTableModel.h" #include "mModuleDefines.h" #include "ZxDataWell.h" #include "ZxDataProject.h" #ifdef QT_DEBUG #include "ZxDataGaugeP.h" #endif #ifdef QT_DEBUG #include "nmXmlHelper.h" #endif #include "nmGuiPlot.h" #include "nmPlotGraphicBinder.h" #include "nmSubWndMain.h" #include "nmPlotDialogContext.h" #include "nmPlotDialogContextProvider.h" #include "nmWxGridVTKContainerWidget.h" #include "nmWxSelectWellsDlg.h" #include "nmWxSelectWellsWidget.h" #include "nmWxParameterProperty.h" #include "nmCalculationDllPebiSolverTask.h" #include "nmCalculationUtils.h" #include "nmObjPointWell.h" #include "nmDataAnalyzeManager.h" #include "nmDataDemo.h" #include "nmDataLogFile.h" #include "nmDataMeasure.h" #include "nmWxParaProperty.h" #include "nmWxRCDialog.h" #include "nmWxWellLayerDlg.h" #include "nmWxGeometryLayerDlg.h" #include "nmReservoirPropertiesDataSource.h" #include "nmWxReservoirPropertiesEditor.h" #include "nmWxPropertyInterpolationDlg.h" #include "nmWxPostprocessingAnimationWidget.h" #include "nmWxGeoRefDlg.h" #include "iSubWndFitting.h" #include "iAnalRun.h" #include #include #include #include #include "nmSingalCenter.h" #include "nmWxParaPropertyPebi.h" #include "nmWxNumericalDesign.h" #include "nmWxReservoirProperties.h" #include "nmCalculationPebiGrid.h" #include "nmDataOutline.h" #include "nmDataVerticalFracturedWell.h" #include "nmDataHorizontalFracturedWell.h" #include "nmDataVerticalWell.h" #include "ZxDataGaugeP.h" #include "ZxDataGaugeF.h" #include "nmWxDFN.h" #include "nmWxDisplaySettings.h" #include "ZxRstWnd.h" #include namespace { // 保留原来求解器单例的防重入语义:全局同一时间只允许一个DLL求解任务运行。 // QPointer会在QObject销毁后自动变空,避免保留已经释放的线程指针。 QPointer s_pRunningSolverTask; QPointer s_pPendingSolverWindow; } nmSubWndMain::nmSubWndMain(QWidget *parent, QString sExt) : iSubWndBaseFit(parent, sExt) { m_pWxPlot = NULL; m_pWxDockParas = NULL; m_pWxParas = NULL; m_pWxDockData1 = NULL; m_pTableView1 = NULL; m_pWxDockData2 = NULL; m_pTableView2 = NULL; #ifdef QT_DEBUG m_pWxDockTemp = NULL; m_pListWxTemp = NULL; #endif // 为了后续支持多井提供方便 if(m_pDataWell == NULL) { m_pDataWell = zxCurWell; } //m_sMdiWndType = s_MdiType_DataNum; m_sMdiWndType = s_MdiType_DataAnal; setWindowTitle(tr("nmSubWndMain")); m_lockState = false; // 默认为false m_openDialogCmdIds.clear(); m_pProgressDlg = nullptr; m_pSolverTask = nullptr; m_pSolverDataManager = nullptr; m_pSolverFitting = nullptr; m_pPendingResultDataManager = nullptr; m_pPendingResultFitting = nullptr; m_bPendingResultWidgetRefresh = false; m_bPendingResultCompletionMessage = false; m_bPendingResultWindowReady = false; m_nPendingResultPebiCount = -1; m_nPendingResultSolveTimeMs = -1; m_bSolverResultCompletionPending = false; m_bPendingSolverSucceeded = false; m_bPendingSolverInputChanged = false; m_nPendingSolverPebiCount = -1; m_nPendingSolverTimeMs = -1; m_bSolverStartPending = false; m_bSolverCancelRequested = false; m_pPlotToolBar = nullptr; } nmSubWndMain::~nmSubWndMain() { if(m_pWxPlot != nullptr) { // Map窗口析构时,解除DataManager中保存的Plot引用,避免后续访问已释放画布。 foreach(nmDataAnalyzeManager* pManager, nmDataAnalyzeManager::s_mapDataAnalManager.values()) { if(pManager != nullptr && pManager->getPlot() == m_pWxPlot) { pManager->setPlot(nullptr); } } } #ifdef QT_DEBUG // 坚持谁创建谁析构原则,删除(首先需要去除绑定) if(NULL != m_pWxDockTemp) { //QObject* pParent = m_pWxDockTemp->parent(); //ZxMainWindow* p = dynamic_cast(pParent); ZxMainWindow* p = m_pMainWindow; if(NULL != p) { p->detachOuterDockWx(m_pWxDockTemp); } delete m_pWxDockTemp; m_pWxDockTemp = NULL; } #endif this->setTopDocksVisible(true); // TODO:删除已有在图元的数据对象 //if (m_pWxPlot != nullptr) { // QVector vecObjs = m_pWxPlot->getAllPlots(); // foreach(nmObjBase* obj, vecObjs) { // obj->removeData(); // } //} m_bSolverStartPending = false; disconnectPebiGridGenerationWait(); if(s_pPendingSolverWindow == this) { s_pPendingSolverWindow = nullptr; } if (m_pProgressDlg) { delete m_pProgressDlg; m_pProgressDlg = NULL; } if (m_pSolverTask) { // 第一步:停止向正在析构的窗口投递完成回调。 disconnect(m_pSolverTask, SIGNAL(sig_calculateDone(bool)), this, SLOT(on_solverTaskFinished(bool))); m_pSolverTask->requestCancel(); // 第二步:DataManager 会在成果窗口销毁后释放,而任务仍读取该对象。 // Qt 4.8 下 DLL 执行中只能等待其正常返回,绝不能强杀持锁线程。 if(m_pSolverTask->isRunning()) { m_pSolverTask->wait(); } if (m_pSolverTask == s_pRunningSolverTask) { s_pRunningSolverTask = nullptr; } // 第三步:线程已经停止,当前窗口仍是任务的明确所有者,可直接释放。 delete m_pSolverTask; m_pSolverTask = nullptr; } /*if (m_parameterPropertyWindow != nullptr) { delete m_parameterPropertyWindow; m_parameterPropertyWindow = nullptr; }*/ } void nmSubWndMain::configWnd(QString sPrevInfo) { iSubWndBaseFit::configWnd(sPrevInfo); if(NULL != m_pMainWindow) { disconnect(this, SIGNAL(sigAppendDock2Main(iDockBaseWx*, Qt::DockWidgetArea)), \ m_pMainWindow, SLOT(slotAppendDock2Main(iDockBaseWx*, Qt::DockWidgetArea))); connect(this, SIGNAL(sigAppendDock2Main(iDockBaseWx*, Qt::DockWidgetArea)), \ m_pMainWindow, SLOT(slotAppendDock2Main(iDockBaseWx*, Qt::DockWidgetArea))); } initUI(); } void nmSubWndMain::initUI() { // iSubWndBaseAF::initUI(); #ifdef QT_DEBUG // 示例:演示增加Dock并且追加至程序主窗口 initUiTop(); #endif // 主布局:定义 initUiMain(); // 外围布局,DockWx { initUiDockWxs(); initUiDockPtys(); } #ifdef QT_DEBUG // 示例:如何加载xml文件 if(_nmXmlHelper->loadXmlDefines()) { foreach(nmXmlData* pData, *(_nmXmlHelper->getVecNmXmlDataPtrs())) { Q_ASSERT(NULL != pData); qDebug() << QString("%1: %2").arg(pData->m_sName).arg(pData->m_sAlias); } } #endif } QWidget* nmSubWndMain::initUiMainWx() { QSplitter* pSplitter = new QSplitter(Qt::Vertical); Q_ASSERT(NULL != pSplitter); nmGuiPlot* pWxPlot = new nmGuiPlot(); m_pWxPlot = pWxPlot; // 将绘图对象添加到数据中心 nmDataAnalyzeManager::getCurrentInstance()->setPlot(m_pWxPlot); { // 建立信号,以便在此处增加新的工具 connect(pWxPlot, SIGNAL(sigToolbarBuilt(QToolBar*)), this, SLOT(slotToolbarBuilt(QToolBar*))); QSize sz = QSize(265, 203); pWxPlot->initUI("TestGui", sz); bindChartSignals(pWxPlot); pSplitter->addWidget(pWxPlot); } return pSplitter; } void nmSubWndMain::initUiDockWxs() { // TODO,本Dock的操作 // 基础参数Dock // { // m_pWxDockParas = new iDockBaseWx(tr("Parameters")); // Q_ASSERT (NULL != m_pWxDockParas); // addDockWidget(Qt::LeftDockWidgetArea, m_pWxDockParas); // m_pWxParas = new nmWxParaProperty(); // Q_ASSERT (NULL != m_pWxParas); // m_pWxParas->initUI(); // m_pWxDockParas->setWidget(m_pWxParas); // m_pWxDockParas->setMinimumWidth(_resoSizeW(200)); // } // 表格数据Dock // { // QStringList listTitles; // listTitles << tr("Name1"); // iDockBaseWx* pDockWx1 = new iDockBaseWx(tr("Data1")); // Q_ASSERT (NULL != pDockWx1); // { // addDockWidget(Qt::RightDockWidgetArea, pDockWx1); // m_pWxDockData1 = pDockWx1; // ZxTableView* pTableView = new ZxTableView(); // Q_ASSERT (NULL != pTableView); // { // ZxTableModel* pTableModel = new ZxTableModel(1, 1, listTitles); // Q_ASSERT (NULL != pTableModel); // pTableView->setModel(pTableModel); // pTableView->setMenuMode(ZxTableView::MTM_Edit_Without_Col); // pTableView->setSelectionBehavior(ZxTableView::SelectRows); // pTableView->fuzzyUiOfQt5(); // } // pDockWx1->setWidget(pTableView); // m_pTableView1 = pTableView; // } // iDockBaseWx* pDockWx2 = new iDockBaseWx(tr("Data2")); // Q_ASSERT (NULL != pDockWx2); // { // addDockWidget(Qt::RightDockWidgetArea, pDockWx2); // tabifyDockWidget(pDockWx1, pDockWx2); // m_pWxDockData2 = pDockWx2; // ZxTableView* pTableView = new ZxTableView(); // Q_ASSERT (NULL != pTableView); // { // ZxTableModel* pTableModel = new ZxTableModel(1, 1, listTitles); // Q_ASSERT (NULL != pTableModel); // pTableView->setModel(pTableModel); // pTableView->setMenuMode(ZxTableView::MTM_Readonly); // pTableView->fuzzyUiOfQt5(); // } // pDockWx2->setWidget(pTableView); // m_pTableView2 = pTableView; // } // } } void nmSubWndMain::initUiDockPtys() { iSubWndBaseAF::initUiDockPtys(); // 属性Dock { Q_ASSERT(NULL != m_pDockPty); m_pDockPty->setMinimumWidth(_resoSizeW(200)); } } #ifdef QT_DEBUG // 示例:演示增加Dock并且追加至程序主窗口 void nmSubWndMain::initUiTop() { #if 0 return; // 隐藏所有Dock this->setTopDocksVisible(false); // 创建新的Dock iDockBaseWx* pDockWx = new iDockBaseWx(tr("TODO:Demo")); Q_ASSERT(NULL != pDockWx); // QListWidget* pListWx = new QListWidget(); nmWxTreeWidget* treeWidget = new nmWxTreeWidget(pDockWx, m_pAnalyzeData); Q_ASSERT(NULL != treeWidget); pDockWx->setWidget(treeWidget); // m_pListWxTemp = pListWx; pDockWx->setMinimumWidth(_resoSizeW(200)); emit sigAppendDock2Main(pDockWx, Qt::LeftDockWidgetArea); m_pWxDockTemp = pDockWx; return; // 基础参数Dock // { // iDockBaseWx* pDockWx = new iDockBaseWx(tr("TODO:Demo")); // Q_ASSERT (NULL != pDockWx); // QListWidget* pListWx = new QListWidget(); // Q_ASSERT (NULL != pListWx); // pDockWx->setWidget(pListWx); // m_pListWxTemp = pListWx; // pDockWx->setMinimumWidth(_resoSizeW(200)); // emit sigAppendDock2Main(pDockWx, Qt::LeftDockWidgetArea); // m_pWxDockTemp = pDockWx; // } #endif } #endif void nmSubWndMain::bindChartSignals(iGuiPlot* pWxPlot) { nmGuiPlot* p = dynamic_cast(pWxPlot); if(NULL != p) { connect(p, SIGNAL(sigObjSelChanged(ZxObjBase*, bool)), \ this, SLOT(slotObjSelChanged(ZxObjBase*, bool))); connect(p, SIGNAL(sigObjPtsChanged(ZxObjBase*)), this, SLOT(slotObjPtsChanged(ZxObjBase*))); } iSubWndBaseAF::bindChartSignals(pWxPlot); } bool nmSubWndMain::runActionOf(QString sAction) { if(iSubWnd::runActionOf(sAction)) { return true; } // 数据加载 QString s = sAction; if(_isSame(s, "LoadP")) { return true; } return false; } bool nmSubWndMain::runCmdBy(QString sName, QString sID) { QString s = sName; int nId = sID.toInt(); // 运行菜单之前,先将所有处于选中状态的图元状态恢复 if(m_pWxPlot != nullptr) { m_pWxPlot->clearPlotsSelectStates(); } switch(nId) { case 5102: this->geologicalMapImport(); return true; /*case 5103: this->geologicalMapHide(); return true;*/ case 5120: this->drawMeasuringScale(); return true; case 5103: this->drawPolygonOutline(); return true; case 5104: this->drawSquareOutline(); return true; case 5105: this->drawRoundOutline(); return true; case 5106: this->drawFault(); return true; case 5107: this->selectWell(); return true; // 新建井菜单暂时停用。 // case 5108: // this->drawWell(); // return true; case 5109: this->drawCrack(); return true; case 5110: this->drawRegion(); return true; case 5111: this->drawRegionMark(); return true; case 5112: this->geoLayering(); return true; case 5113: this->reservoirCharacteristics(); return true; /*case 5114: this->generationMesh(); return true; */ case 5115: this->solveAndAnalyze(); return true; case 5117: this->mergeAnaResultToFitting(); this->schedulePendingResultWidgets(); return true; case 5121: this->deleteOneObj(); return true; case 5119: this->setLocked(); return true; case 5122: this->generateDFN(); return true; case 5123: this->displaySetting(); return true; case 5124: return m_pWxPlot != nullptr && m_pWxPlot->runAction("print_Preview"); case 5125: return m_pWxPlot != nullptr && m_pWxPlot->runAction("Print"); /* case 5117: this->initData(); return true; */ case 5129: if(isDialogCmdOpened(5129)) { return true; } this->drawMeasure(); return true; case 5127: this->drawGeoRef(); return true; case 5128: if(isDialogCmdOpened(5128)) { return true; } this->drawPointerPos(); return true; case 5126: if(isDialogCmdOpened(5126)) { return true; } this->showPropertyInterpolationDialog(); return true; } if(_isSame(s, "RunGrid")) { if(NULL != m_pWxPlot) { //m_pWxPlot->refreshGrid(true); } return true; } return false; } bool nmSubWndMain::checkCmdEnable(bool &b, \ QString sName, int nID, \ bool bLicensed) { bool b1 = (NULL != zxCurProject); bool b2 = (NULL != m_pDataWell); // 判断当前锁定状态 if(m_lockState) { /* if(nID >= 5102 && nID <= 5125) { b = (b1 && b2 && bLicensed && !isReadonly() && NULL != m_pWxPlot); return true; } */ /* if ((nID >= 5122 && nID <= 5125) || (nID >= 5112 && nID <= 5119 )) { b = true; return true; } */ /* if(nID >= 5102 && nID <= 5124) { b = true; return true; } */ // 可使用菜单栏 if(sName == "NmMap" || sName == "NmUnlocked" || sName == "NmGeometry" || sName == "NmProperties" || sName == "NmDFN" || sName == "NmCaculation" || sName == "NmBack" || sName == "NmResult" || sName == "NmGrid" || sName == "NmInterpolation" || sName == "NmShow" || sName == "NmPrintPreview" || sName == "NmPrint" || sName == "VisibleAnalBasic" || sName == "VisibleAnalPVT" || sName == "VisibleAnalDiff" || sName == "VisibleAnalPseu" || sName == "nmFitModel" || sName == "NmAnalChange") { b = true; return true; } else { b = false; return false; } } else { if((nID >= 5102 && nID <= 5130) /*|| (nID >= 9002 && nID <= 9003) */ || (nID >= 9101 && nID <= 9104) || nID == 3104) { b = (b1 && b2 && bLicensed && !isReadonly() && NULL != m_pWxPlot); return true; } } return iSubWndBaseFit::checkCmdEnable(b, sName, nID, bLicensed); } void nmSubWndMain::reAdjustToolbar(QToolBar* pToolBar) { iSubWndBaseAF::reAdjustToolbar(pToolBar); m_pPlotToolBar = pToolBar; // 获取所有动作的列表 // QList actions = m_pPlotToolBar->actions(); // // 隐藏第1个和第2个动作 // if (actions.size() > 0) { // actions[0]->setVisible(false); // 隐藏第1个动作 // } // if (actions.size() > 1) { // actions[1]->setVisible(false); // 隐藏第2个动作 // } } void nmSubWndMain::firstLoadAndUpdate() { iSubWndBaseAF::firstLoadAndUpdate(); } void nmSubWndMain::finalDeals() { iSubWndBaseAF::finalDeals(); #ifdef QT_DEBUG updatePlots(); updateDockWxs(); if(m_pWxDockData1 != NULL) { Q_ASSERT(NULL != m_pWxDockData1); m_pWxDockData1->raise(); } #endif } void nmSubWndMain::onActivated() { #ifdef QT_DEBUG // 当前窗体激活时,可以做些事情,比如把隐藏的Dock显示出来 if(NULL != m_pWxDockTemp) { if(!m_pWxDockTemp->isVisible()) { m_pWxDockTemp->setVisible(true); } } #endif } #ifdef QT_DEBUG void nmSubWndMain::updatePlots() { #if 0 // 里面根据曲线,重新刷了一下坐标轴范围。你屏蔽该函数,即可 // 坐标轴范围会变成背景图的大小 return ; // 说明:以从当前井获取一条压力数据进行绘图为例 QString sType = iDataModelType::sTypeDataGaugeP; QString sCodeP = ""; //如果已知压力数据Code,可以直接引用 ZxDataGaugeP* pDataObjP = dynamic_cast(getDataObjOf(sType, sCodeP)); if(NULL == pDataObjP) { qDebug() << tr("Failed to get data of type '%1'").arg(sType); return; } VecDouble vecX, vecY; QByteArray ba = pDataObjP->getGaugeDataOf(0); if(!ZxBaHelper::convertBa2VecXY(vecX, vecY, ba)) { qDebug() << tr("Failed to get x-y data of dataobj '%1'").arg(pDataObjP->getCode()); return; } nmGuiPlot* pWxPlot = m_pWxPlot; Q_ASSERT(NULL != pWxPlot); QString sName = tr("CurveName"); ZxObjBase* pObj = pWxPlot->updatePlotObjBy(sName, vecX, vecY, true); if(NULL != pObj) { pObj->setLockPos(true); //可以调整属性 // pObj->setReadOnly(true); pWxPlot->freshAxisScales(true);//刷新坐标轴刻度 // 设置标题+坐标轴等 pWxPlot->setAxisX(tr("Time"), "hr"); pWxPlot->setAxisY(tr("Pressure"), "MPa"); pWxPlot->setAxisXYLog(false, 0); pWxPlot->setAxisXYLog(false, 1); pWxPlot->m_pPlot->setLegendVisible(false); pWxPlot->m_pPlot->getTitle()->setName(tr("Demo")); } #endif } ZxDataObject* nmSubWndMain::getDataObjOf(QString sType, QString sCode /*= ""*/) { ZxDataWell* pDataWell = zxCurWell; Q_ASSERT(NULL != pDataWell); ZxDataObject* pDataObj = NULL; if(!sCode.isEmpty()) { pDataObj = pDataWell->getChild(sType, sCode); } else { ZxDataObjectList vecObjs = pDataWell->getChildren(sType); if(!vecObjs.isEmpty()) { pDataObj = vecObjs[0]; } } return pDataObj; } void nmSubWndMain::updateDockWxs() { #if 0 // TODO,刷新Dock内容 return; // 左侧参数窗体 Q_ASSERT(NULL != m_pWxParas); QStringList listParas; listParas << "K" << "S" << "rw" << "Miuo" << "Bo" << "phi"; m_pWxParas->refreshUIs(listParas); // 表格数据1 Q_ASSERT(NULL != m_pTableView1); ZxTableModel* pTableModel = m_pTableView1->getModel(); Q_ASSERT(NULL != pTableModel); QStringList listTitles; listTitles << tr("Col1") << tr("Col2\n(MPa)"); VVecDouble vvec; { VecDouble vec; vec << 0.2 << 3.4; vvec << vec; } { VecDouble vec; vec << 5.6 << 0.456; vvec << vec; } pTableModel->setTitles(listTitles); pTableModel->setData(vvec); if(NULL != m_pListWxTemp) { m_pListWxTemp->clear(); for(int i = 0; i < 10; i++) { QString sItem = tr("Demo Item %1").arg(i + 1); QListWidgetItem* pItem = new QListWidgetItem(sItem); Q_ASSERT(NULL != pItem); pItem->setIcon(zxLoadIcon("Open")); m_pListWxTemp->addItem(pItem); } } #endif } void nmSubWndMain::updateTableByCurve(ZxObjCurve* pObjCurve, ZxTableView* pTableView) { Q_ASSERT(NULL != pObjCurve); Q_ASSERT(NULL != pTableView); // 刷新表格标题 { QString sNameX = pObjCurve->getAxisX()->getName(); QString sUnit = pObjCurve->getAxisX()->getUnit(); if(!sUnit.isEmpty()) { sNameX += QString("\n(%1)").arg(sUnit); } QString sNameY = pObjCurve->getAxisY()->getName(); sUnit = pObjCurve->getAxisY()->getUnit(); if(!sUnit.isEmpty()) { sNameY += QString("\n(%1)").arg(sUnit); } QStringList listTitles; listTitles << sNameX << sNameY; pTableView->getModel()->setTitles(listTitles); } // 刷新表格数据 { VVecDouble vvec; QVector vecValues = pObjCurve->getAllValues(); for(int i = 0; i < vecValues.count(); i++) { VecDouble vec; vec << vecValues.at(i).x() * 1.0; vec << vecValues.at(i).y() * 1.0; vvec << vec; } pTableView->getModel()->setData(vvec); } } #endif void nmSubWndMain::slotObjSelChanged(ZxObjBase* p, bool b) { #ifdef QT_DEBUG // 如果选中了一条曲线,则把其数据显示在数据表格2中 ZxObjCurve* pObjCurve = dynamic_cast(p); if(NULL != pObjCurve && b) { Q_ASSERT(NULL != m_pWxDockData2); m_pWxDockData2->raise(); updateTableByCurve(pObjCurve, m_pTableView2); } #endif } void nmSubWndMain::slotObjPtsChanged(ZxObjBase* p) { } void nmSubWndMain::onSerialize(ZxSerializer* ser) { iSubWndBaseAF::onSerialize(ser); if(NULL != m_pWxPlot) { m_pWxPlot->setModified(false); } } void nmSubWndMain::onDeserialize(ZxSerializer* ser) { iSubWndBaseAF::onDeserialize(ser); if(NULL != m_pWxPlot) { m_pWxPlot->setModified(false); } } void nmSubWndMain::onDeserialized() { if(NULL != m_pWxPlot) { m_pWxPlot->resetAfterDeserialized(); } } //bool nmSubWndMain::loadRss() { // ZxRstJobWnd* pRstWnd = getRstJobWnd(); // Q_ASSERT(NULL != pRstWnd); // // 基础信息 // { // QByteArray v = pRstWnd->getDataInfo(); // IxSerDes* pSerObj = this; // if(!loadRstInfoFromArr(v, pSerObj)) { // return false; // } // } // // 图形 // if(NULL != m_pWxPlot) { // QByteArray v = pRstWnd->getDataInfo1(); // ZxPlot* pPlot = NULL; // loadOnePlot(pPlot, v); // if(NULL != pPlot) { // m_pWxPlot->m_pPlotScene->freshToPlot(pPlot, // m_pWxPlot->m_pPlotView); // m_pWxPlot->m_pPlot = m_pWxPlot->m_pPlotScene->m_pPlot; // pPlot->resetTools(m_pWxPlot->m_pPlotView); // m_pWxPlot->resetAfterDeserialized(); // } // m_pWxPlot->runUpdate(); // } // return true; //} //bool nmSubWndMain::saveRss() { // ZxRstJobWnd* pRstWnd = getRstJobWnd(); // if(NULL == pRstWnd) { // return false; // } // // 基础信息 // { // QByteArray v; // IxSerDes* pSerObj = this; // if(!saveRstInfoToArr(v, pSerObj)) { // return false; // } // pRstWnd->setDataInfo(v); // } // // 图形 // if(NULL != m_pWxPlot) { // ZxPlot* pPlot = m_pWxPlot->m_pPlot; // { // QByteArray v1; // if(saveOnePlot(pPlot, v1)) { // pRstWnd->setDataInfo1(v1); // } // } // } // return pRstWnd->save(); //} bool nmSubWndMain::loadRsts() { return true;// /* ZxRstWnd* pRstWnd = getRstUtilWnd(); Q_ASSERT (nullptr != pRstWnd); // 基础信息 if (!loadRstBase(pRstWnd)) { return false; } // 图形 if (nullptr != m_pWxPlot) { QByteArray v = QByteArray(); pRstWnd->loadSubPlotContent(v); ZxPlot* pPlot = nullptr; loadOnePlotFromBa(pPlot, v); if (nullptr != pPlot) { m_pWxPlot->m_pPlotScene->freshToPlot(pPlot, m_pWxPlot->m_pPlotView); m_pWxPlot->m_pPlot = m_pWxPlot->m_pPlotScene->m_pPlot; pPlot->resetTools(m_pWxPlot->m_pPlotView); m_pWxPlot->resetAfterDeserialized(); } m_pWxPlot->runUpdate(); }//*/ } bool nmSubWndMain::saveRsts() { ZxRstWnd* pRstWnd = this->getRstUtilWnd(); if(nullptr == pRstWnd) { return false; } return pRstWnd->save(); // // 基础信息 // if (!saveRstBase(pRstWnd)) // { // return false; // } // // 图形 // if (nullptr != m_pWxPlot) // { // ZxPlot* pPlot = m_pWxPlot->m_pPlot; // { // QByteArray v1; // if (saveOnePlotToBa(pPlot, v1)) // { // pRstWnd->saveSubPlotContent(v1); // } // } // } //return pRstWnd->save(); } bool nmSubWndMain::slotSaveAll() { ZxRstWnd* pRstWnd = getRstUtilWnd(); Q_ASSERT(NULL != pRstWnd); if(pRstWnd == NULL) { return false; } return saveRsts(); } void nmSubWndMain::updateSelectedWells(QList wellObjList) { // if(wellObjList.count() == 0) { // return; // } // setp 1,获取现在画布上所有井的 ZxDataWell* 数据指针 QVector pWellPlotList = m_pWxPlot->getWellPlots(); // QMap 用于快速查找和构建当前画布上的井数据列表 QMap pWellDataToPlotMap; for(int i = 0; i < pWellPlotList.count(); ++i) { nmObjPointWell* wellPlot = pWellPlotList[i]; ZxDataWell *pWellData = wellPlot->getWellData(); if (pWellData) { // 确保数据不为空 pWellDataToPlotMap.insert(pWellData, wellPlot); } } // 用集合保存当前井和选中井,避免井数增加后反复调用 QList::contains 造成平方级查找。 QList pWellPlotDataList = pWellDataToPlotMap.keys(); QSet pWellPlotDataSet; for(int i = 0; i < pWellPlotDataList.count(); ++i) { pWellPlotDataSet.insert(pWellPlotDataList[i]); } QSet selectedWellDataSet; for(int i = 0; i < wellObjList.count(); ++i) { if(wellObjList[i] != nullptr) { selectedWellDataSet.insert(wellObjList[i]); } } // setp 2,找出需要 REMOVE (删除) 的图元 QVector vDeleteWellPlotList; // 遍历所有当前在画布上的井数据 for(int i = 0; i < pWellPlotDataList.count(); ++i) { ZxDataWell *pWellDataOnPlot = pWellPlotDataList[i]; // 如果该井数据不在用户选中的列表 wellObjList 中,则需要删除 if (!selectedWellDataSet.contains(pWellDataOnPlot)) { // 从 Map 中获取对应的图元对象 vDeleteWellPlotList.append(pWellDataToPlotMap.value(pWellDataOnPlot)); } } // setp 3,找出需要 ADD (添加) 的井数据 QVector vAddWellPlotList; // 遍历所有用户选中的井数据 wellObjList for(int i = 0; i < wellObjList.count(); ++i) { ZxDataWell *pWellDataSelected = wellObjList[i]; // 确保井数据有效 if (pWellDataSelected && !pWellPlotDataSet.contains(pWellDataSelected)) { // 如果该井数据不在当前画布上的数据列表中,则需要添加 vAddWellPlotList.append(pWellDataSelected); } } // 单井增删过程中会多次请求重绘;批量阶段先暂停,全部处理完成后再统一刷新。 const bool updatesEnabled = m_pWxPlot->updatesEnabled(); m_pWxPlot->setUpdatesEnabled(false); // setp 4,执行删除操作 for(int i = 0; i < vDeleteWellPlotList.count(); ++i) { nmObjPointWell* pWellPlot = vDeleteWellPlotList[i]; // 如果是当前井,不可删除 (保留您的逻辑) if(m_pWxPlot->graphicBinder() && m_pWxPlot->graphicBinder()->isCurrentWellGraphic(pWellPlot)) continue; // 删除对应的参数类 pWellPlot->removeData(); // 实际从画布上删除图元 m_pWxPlot->m_pPlot->removeObjByName(pWellPlot->getName()); } // setp 5,将vAddWellPlotList中的井数据,构建图元,添加到画布上 for(int i = 0; i < vAddWellPlotList.count(); i++) { ZxDataWell* wellObj = vAddWellPlotList[i]; QString sName = wellObj->getName(); QVector points; points.append(QPointF(wellObj->getLocationX(), wellObj->getLocationY())); // 构建了图元 nmObjPointWell* pWellPlot = NULL; pWellPlot = (nmObjPointWell*)m_pWxPlot->appendOneObj(NMOT_Point_Well, sName, points); if(pWellPlot == NULL) { continue; } // 选择已有井重新生成图元时,先恢复Binder,后续afterCreated才能建立井数据绑定 if(m_pWxPlot->graphicBinder()) { pWellPlot->setGraphicBinder(m_pWxPlot->graphicBinder()); } // 设置图元的井数据 pWellPlot->setWellData(wellObj); // 移动到正确的位置 pWellPlot->moveToPos(pWellPlot->getPosOf(pWellPlot->getAllPos())); pWellPlot->afterCreated(); //选择完后初始化参数 } // 添加完毕后,重新渲染 m_pWxPlot->setUpdatesEnabled(updatesEnabled); if(updatesEnabled) { m_pWxPlot->update(); } } // 地质图导入 void nmSubWndMain::geologicalMapImport() { //打开图片选择弹框 //打开文件对话框选择图片文件 //QString filePath = QFileDialog::getOpenFileName(nullptr, "选择图片文件", "", "Images (*.png *.xpm *.jpg)"); //if(!filePath.isEmpty()) { // // 画布 设置背景,并自适应宽高 // ZxPlot* pPlot = m_pWxPlot->m_pPlot; // pPlot->setBkImgFile(filePath); // // QImage* pBkImg = pPlot->getBkImg(); // // Q_ASSERT (NULL != pBkImg); // // int w = pBkImg->width(); // // int h = pBkImg->height(); // // pPlot->getMainAxisX()->setRangeMinMax(0, w, true); // // pPlot->getMainAxisY()->setRangeMinMax(0, h, true); // nmDataLogFile::getInstance()->writeLog(filePath); //} QString sFilter = "PNG Images (*.png);;" "JPEG Images (*.jpg *.jpeg);;" "Bitmap Images (*.bmp);;" "All Files (*)"; QString sFile = QFileDialog::getOpenFileName( nullptr, tr("Open File"), ZxBaseUtil::getLastDir(), sFilter ); if(sFile.isEmpty() || !QFile::exists(sFile)) { return; } // 更新最后访问目录 QFileInfo fi(sFile); ZxBaseUtil::setLastDir(fi.absolutePath()); // 输出文件失败原因 QImageReader reader(sFile); if(!reader.canRead()) { // 输出具体错误 qDebug() << "Error Message: " << reader.errorString(); return; } if(m_pWxPlot == nullptr || m_pWxPlot->m_pPlot == nullptr) { return; } // 设置背景图片 ZxPlot* pPlot = m_pWxPlot->m_pPlot; pPlot->setBkImgFile(sFile); // 检查 ZxPlot 是否成功加载图片 QImage* pBkImg = pPlot->getBkImg(); Q_ASSERT(nullptr != pBkImg); if(pBkImg == nullptr) { return; } // 将图片信息存储到数据中心 nmDataAnalyzeManager* pManager = nmDataAnalyzeManager::getCurrentInstance(); if(pManager) { QImage loadedImage = reader.read(); // 假设已经读取成功 BackgroundImageInfo bgInfo; bgInfo.bIsVisible = true; // 默认设置为可见 bgInfo.sFilePath = sFile; // 当前背景文件路径 bgInfo.objImage = loadedImage; // 存储QImage对象 pManager->setBackgroundImageInfo(bgInfo); // 设置背景图片信息 } // 手动调整坐标轴(如果 ZxPlot 未自动调整) //int w = pBkImg->width(); //int h = pBkImg->height(); //if (w > 0 && h > 0) { // pPlot->getMainAxisX()->setRangeMinMax(0, w, true); // pPlot->getMainAxisY()->setRangeMinMax(0, h, true); //} } void nmSubWndMain::drawMeasuringScale() { this->triggerToolBarAction(NMOT_Line_MeasuringScale); } void nmSubWndMain::geologicalMapHide() { } void nmSubWndMain::setTopDocksVisible(bool visible) { // 获取主程序所有 Dock 视图 QList dockWidgets = m_pMainWindow->findChildren(); // 遍历所有 Dock 视图并移除位于左侧的 foreach(QDockWidget* dock, dockWidgets) { if(visible) { dock->show(); } else { dock->hide(); } } } void nmSubWndMain::drawPolygonOutline() { // 获取多边形边界的绘制,并模拟触发点击操作 // 目前多边形边界绘制是工具栏的第4个,所以用 3 // QList actions = m_pPlotToolBar->actions(); // if (!actions.isEmpty()) { // QAction *action = actions[3]; // action->trigger(); // } this->triggerToolBarAction(NMOT_PolygonOutline); } void nmSubWndMain::drawSquareOutline() { // 获取矩形边界的绘制,并模拟触发点击操作 this->triggerToolBarAction(NMOT_RectOutline); } void nmSubWndMain::drawRoundOutline() { // 获取矩形边界的绘制,并模拟触发点击操作 this->triggerToolBarAction(NMOT_RoundOutline); } void nmSubWndMain::drawFault() { this->triggerToolBarAction(NMOT_Line_Fault); } void nmSubWndMain::selectWell() { if(m_pWxPlot == nullptr || zxCurProject == nullptr) { return; } // 第一步:Map 当前选择状态使用 WellCode 保存,井名只用于界面显示。 QList selectedWellCodes; QVector vecMapWells = m_pWxPlot->getWellDatas(); for(int nIndex = 0; nIndex < vecMapWells.size(); ++nIndex) { ZxDataWell* pMapWell = vecMapWells[nIndex]; if(pMapWell != nullptr && !pMapWell->getCode().isEmpty()) { selectedWellCodes.append(pMapWell->getCode()); } } // 第二步:读取项目井库,并建立 WellCode 到项目井对象的稳定映射。 int wellCount = zxCurProject->getChildrenCount(iDataModelType::sTypeWell); ZxDataObjectList wellList = zxCurProject->getChildren(iDataModelType::sTypeWell); nmDataLogFile::getInstance()->writeLog(QString(" %1 %2").arg(wellCount).arg(wellList.size())); QStringList listWellNames; QList wellCodes; QMap wellCodeToObjMap; for(int i = 0; i < wellList.size(); i++) { ZxDataWell* wellObj = dynamic_cast(wellList[i]); if(wellObj == nullptr || wellObj->getCode().isEmpty()) { continue; } nmDataLogFile::getInstance()->writeLog(wellObj->getName() + " " + wellObj->getCode()); listWellNames.append(wellObj->getName()); wellCodes.append(wellObj->getCode()); wellCodeToObjMap.insert(wellObj->getCode(), wellObj); } // 第三步:恢复 Map 中已有井的勾选状态,允许用户增删其他项目井。 nmWxSelectWellsDlg dlg; nmWxSelectWellsWidget wellListWidget; wellListWidget.setSelectedValues(selectedWellCodes); wellListWidget.addItems(listWellNames, wellCodes); dlg.setWidget(&wellListWidget); if(dlg.exec() == QDialog::Accepted) { nmDataLogFile::getInstance()->writeLog(" OK "); QList selectedValues = wellListWidget.getSelectedValues(); QList selectWellList; // 第四步:按 WellCode 解析选择结果,避免同名井或井名修改导致关联错误。 QStringList selectedCodeTexts; for(int i = 0; i < selectedValues.count(); i++) { const QString sWellCode = selectedValues[i].toString(); ZxDataWell* pSelectedWell = wellCodeToObjMap.value(sWellCode, nullptr); if(pSelectedWell != nullptr) { selectWellList.append(pSelectedWell); selectedCodeTexts.append(sWellCode); } } this->updateSelectedWells(selectWellList); nmDataLogFile::getInstance()->writeLog( "===" + selectedCodeTexts.join(";") + "==="); } else { nmDataLogFile::getInstance()->writeLog(" Cancel "); } } // 新建井菜单暂时停用。 // void nmSubWndMain::drawWell() // { // this->triggerToolBarAction(NMOT_Point_Well); // nmDataLogFile::getInstance()->writeLog(" in drawWell "); // } void nmSubWndMain::drawCrack() { this->triggerToolBarAction(NMOT_Line_Fracture); } void nmSubWndMain::drawRegion() { this->triggerToolBarAction(NMOT_Region); } void nmSubWndMain::drawRegionMark() { this->triggerToolBarAction(NMOT_RegionMark); } void nmSubWndMain::drawMeasure() { nmGuiPlot* plot = dynamic_cast(m_pWxPlot); QWidget* measureDlg = showMeasureDialog(); if(plot == nullptr || measureDlg == nullptr) { return; } bindMeasureDialog(measureDlg); plot->setAllPlotObjectsEditable(false); this->triggerToolBarAction(NMOT_Line_Measure); } void nmSubWndMain::drawGeoRef() { nmWxGeoRefDlg dlg(nullptr); dlg.exec(); } void nmSubWndMain::drawPointerPos() { nmGuiPlot* plot = dynamic_cast(m_pWxPlot); if(plot) { showPointerPosDialog(); } } bool nmSubWndMain::isDialogCmdOpened(int nId) const { return m_openDialogCmdIds.contains(nId); } void nmSubWndMain::markDialogCmdOpened(int nId) { if(!m_openDialogCmdIds.contains(nId)) { m_openDialogCmdIds.append(nId); } } void nmSubWndMain::markDialogCmdClosed(int nId) { // 从后向前遍历,避免索引错乱 for (int i = m_openDialogCmdIds.size() - 1; i >= 0; --i) { if (m_openDialogCmdIds[i] == nId) { m_openDialogCmdIds.remove(i); } } } void nmSubWndMain::onDialogCmdDestroyed(QObject* obj) { if(obj == nullptr) { return; } bool bOk = false; int nId = obj->property("dialogCmdId").toInt(&bOk); if(bOk) { markDialogCmdClosed(nId); } } void nmSubWndMain::bindDialogCmdDestroyed(QWidget* dialog, int nId) { if(dialog == nullptr) { return; } dialog->setProperty("dialogCmdId", nId); connect(dialog, SIGNAL(destroyed(QObject*)), this, SLOT(onDialogCmdDestroyed(QObject*))); } void nmSubWndMain::bindMeasureDialog(QWidget* measureDlg) { nmGuiPlot* plot = dynamic_cast(m_pWxPlot); if(plot == nullptr || measureDlg == nullptr) { return; } disconnect(plot, SIGNAL(sigMeasureStarted()), measureDlg, SLOT(resetButtons())); connect(plot, SIGNAL(sigMeasureStarted()), measureDlg, SLOT(resetButtons())); disconnect(measureDlg, SIGNAL(needDeleteMeasureObj()), plot, SLOT(finishMeasure())); connect(measureDlg, SIGNAL(needDeleteMeasureObj()), plot, SLOT(finishMeasure())); } QWidget* nmSubWndMain::showMeasureDialog() { nmGuiPlot* plot = dynamic_cast(m_pWxPlot); if(plot == nullptr) { return nullptr; } nmPlotDialogContextProvider* pDialogProvider = nmPlotDialogContext::provider(); if(pDialogProvider == nullptr) { return nullptr; } QWidget* measureDlg = pDialogProvider->createMeasureDialog(plot); if(measureDlg == nullptr) { return nullptr; } markDialogCmdOpened(5129); measureDlg->setObjectName("nmMeasureDialog"); measureDlg->setAttribute(Qt::WA_DeleteOnClose, true); bindDialogCmdDestroyed(measureDlg, 5129); nmDataMeasure* measureData = nmDataAnalyzeManager::getCurrentInstance()->getMeasureData(); if(measureData != nullptr) { connect(measureData, SIGNAL(sigMeasureDataChanged()), measureDlg, SLOT(updateDisplay())); } measureDlg->show(); return measureDlg; } QWidget* nmSubWndMain::showPointerPosDialog() { nmGuiPlot* plot = dynamic_cast(m_pWxPlot); if(plot == nullptr) { return nullptr; } nmPlotDialogContextProvider* pDialogProvider = nmPlotDialogContext::provider(); if(pDialogProvider == nullptr) { return nullptr; } QWidget* pointerPosDlg = pDialogProvider->createPointerPosDialog(plot); if(pointerPosDlg == nullptr) { return nullptr; } markDialogCmdOpened(5128); pointerPosDlg->setObjectName("nmPointerPosDialog"); pointerPosDlg->setAttribute(Qt::WA_DeleteOnClose, true); bindDialogCmdDestroyed(pointerPosDlg, 5128); plot->bindPointerPosDialog(pointerPosDlg); pointerPosDlg->show(); return pointerPosDlg; } QWidget* nmSubWndMain::showPropertyInterpolationDialog() { nmWxPropertyInterpolationDlg* interpolationDlg = new nmWxPropertyInterpolationDlg( m_pWxPlot, nmDataAnalyzeManager::getCurrentInstance(), this); markDialogCmdOpened(5126); interpolationDlg->setObjectName("nmPropertyInterpolationDialog"); // 关闭窗口时自动释放,并恢复菜单命令的可用状态 interpolationDlg->setAttribute(Qt::WA_DeleteOnClose, true); bindDialogCmdDestroyed(interpolationDlg, 5126); interpolationDlg->show(); interpolationDlg->raise(); interpolationDlg->activateWindow(); return interpolationDlg; } void nmSubWndMain::generationMesh() { // // TODO,还不支持原型边界 // // 拿到多边形和点的位置信息 // QVector pObjVec = m_pWxPlot->getObjsByTag("nObjPolygonOutline"); // nmDataLogFile::getInstance()->writeLog(" ---------- " + QString::number(pObjVec.count())); // QVector pWellObjVec = m_pWxPlot->getObjsByTag("nObjPointWell"); // nmDataLogFile::getInstance()->writeLog(" ---------- " + QString::number(pWellObjVec.count())); // // 生成geo文件 // // 调用gmsh生成vtk文件 // // 弹出网格的dialog,渲染文件 // if(true) { // QDialog* dlg = new QDialog; // QVBoxLayout* layout = new QVBoxLayout; // dlg->setLayout(layout); // nmWxGridVTKContainerWidget* gridVTKContainerWidget = new nmWxGridVTKContainerWidget(dlg); // layout->addWidget(gridVTKContainerWidget); // dlg->resize(800, 600); // dlg->show(); // } } void nmSubWndMain::solveAndAnalyze() { if(!s_pPendingSolverWindow.isNull() || !s_pRunningSolverTask.isNull() || m_bSolverResultCompletionPending) { QMessageBox::information(this, tr("solver error"), tr("task is running!")); return; } // Qt 4.8 下复用已经隐藏的进度框,避免窗口析构触发主界面重绘重入。 if (m_pProgressDlg != nullptr) { m_pProgressDlg->reset(); QPushButton* pCancelButton = m_pProgressDlg->findChild(); if(pCancelButton != nullptr) { pCancelButton->setEnabled(true); } } // 新一次启动前清空旧上下文;后续所有检查通过后再成对写入。 m_pSolverDataManager = nullptr; m_pSolverFitting = nullptr; // 根据当前数据中心的分析切换到对应的流动段分析窗口 ZxMainWindow* pMainWnd1 = const_cast(m_pMainWindow); if(nullptr == pMainWnd1) { return; } ZxTabWidget* pTabWx = pMainWnd1->getCurTabWx(); iSubWndFitting* pSubWndFit = nmDataAnalyzeManager::getCurrentFitting(); Q_ASSERT(pSubWndFit != nullptr); if(pSubWndFit == nullptr) { return; } if(pTabWx == nullptr) { return; } pTabWx->setCurrentWidget(pSubWndFit); nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::findManagerByFitting(pSubWndFit); if(pDataManager == nullptr) { return; } // 第一步:求解时间轴必须由至少一口主动井的产注制度驱动。 // 观察井可以完全不传产量,但不能在所有井都是观察井时启动无源汇计算。 QVector vecEffectiveWells = pDataManager->getEffectiveCalculationWells(); QHash mapWellsByCode; const QVector vecAllWells = pDataManager->getWellDataList(); for(int nIndex = 0; nIndex < vecAllWells.size(); ++nIndex) { nmDataWellBase* pWellData = vecAllWells[nIndex]; if(pWellData != nullptr) { mapWellsByCode.insert(pWellData->getWellCode(), pWellData); } } bool bHasRateControlledWell = false; for(int nIndex = 0; nIndex < vecEffectiveWells.size(); ++nIndex) { const nmCalculationWellRef& oWellRef = vecEffectiveWells[nIndex]; nmDataWellBase* pWellData = mapWellsByCode.value(oWellRef.m_sWellCode, nullptr); if(pWellData == nullptr) { QMessageBox::warning(this, tr("solver error"), tr("A selected well is missing from the map.")); return; } if(oWellRef.m_eMode != NM_CaseWell_RateControlled) { continue; } bHasRateControlledWell = true; if(pWellData->getFlowPoints().size() < 2) { QMessageBox::warning( this, tr("solver error"), tr("Rate-controlled well '%1' has no valid rate schedule.") .arg(pWellData->getWellName())); return; } } if(!bHasRateControlledWell) { QMessageBox::warning( this, tr("solver error"), tr("At least one active production or injection well is required.")); return; } // 手动求解可能直接启动, 因此必须在创建求解线程前初始化拟压力转换器. // pSubWndFit 保存的是当前拟合窗口克隆后的 PVT、拟压力及模型选项数据. // configPsAbouts() 会据此初始化常规、三区或组分转换器并设置当前模式. if(pDataManager->getSolverModelType() == SMT_Gas_VariablePvt) { iAnalRun* pAnalRun = pSubWndFit->getAnalRun(); if(pAnalRun == nullptr || !pAnalRun->configPsAbouts(true, pSubWndFit->getModelOption(), false, pSubWndFit->getAllWxPtr())) { QMessageBox::warning(this, tr("solver error"), tr("Gas pseudo-pressure data is unavailable or invalid.")); return; } } if(m_pSolverTask != nullptr) { // 上一次线程已结束但指针还未清空时,先交给Qt事件循环安全释放。 m_pSolverTask->deleteLater(); m_pSolverTask = nullptr; } // 第一步:捕获本次求解的完整窗口上下文,完成回调不得再读取全局当前窗口。 m_pSolverDataManager = pDataManager; m_pSolverFitting = pSubWndFit; m_bSolverCancelRequested = false; // 第二步:先显示真实进度框,再在下一轮事件中捕获输入并启动线程。 if (m_pProgressDlg == nullptr) { m_pProgressDlg = new QProgressDialog( tr("Preparing input snapshot..."), tr("Stop"), 0, 0, getMainWindow()); m_pProgressDlg->setWindowTitle(tr("Solver Progress")); // 保持 8 月 25 日前的稳定行为。任务和输入快照负责数据安全,进度框不参与 // 全局窗口阻塞,避免首次创建结果窗口时进入 Qt 4.8 模态布局重入。 m_pProgressDlg->setWindowModality(Qt::NonModal); // 某些系统下,对话框右上角的关闭按钮可能仍存 // 使用 WindowFlags 彻底禁用关闭按钮 m_pProgressDlg->setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); m_pProgressDlg->setMinimumDuration(0); m_pProgressDlg->setAutoClose(false); m_pProgressDlg->setAutoReset(false); connect(m_pProgressDlg, SIGNAL(canceled()), this, SLOT(onSolverCancelRequested())); } m_pProgressDlg->setLabelText(tr("Preparing input snapshot...")); m_pProgressDlg->setRange(0, 0); m_pProgressDlg->show(); m_pProgressDlg->raise(); // 提升层级到最前 m_pProgressDlg->activateWindow(); // 激活窗口焦点 m_bSolverStartPending = true; s_pPendingSolverWindow = this; QTimer::singleShot(0, this, SLOT(startSolverTask())); } void nmSubWndMain::startSolverTask() { if(!m_bSolverStartPending) { return; } if(m_pProgressDlg == nullptr || m_pSolverDataManager.isNull() || m_pSolverFitting.isNull()) { m_bSolverStartPending = false; if(s_pPendingSolverWindow == this) { s_pPendingSolverWindow = nullptr; } if(m_pProgressDlg != nullptr) { m_pProgressDlg->hide(); } m_pSolverDataManager = nullptr; m_pSolverFitting = nullptr; return; } if(waitForPebiGridGeneration()) { return; } // 先创建不自动捕获的任务,使停止按钮在第一批井输入开始前就有明确接收者。 m_pSolverTask = new nmCalculationDllPebiSolverTask( QString(), m_pSolverDataManager.data(), QString(), nullptr, true); s_pRunningSolverTask = m_pSolverTask; connect(m_pSolverTask, SIGNAL(sig_calculateDone(bool)), this, SLOT(on_solverTaskFinished(bool)), Qt::QueuedConnection); connect(m_pSolverTask, SIGNAL(sigStageChanged(QString,int,int)), this, SLOT(onSolverStageChanged(QString,int,int)), Qt::QueuedConnection); // 给 Windows 一次完成首帧绘制的机会;后续每个事件循环只捕获一口井。 QTimer::singleShot(20, this, SLOT(continueSolverInputCapture())); } void nmSubWndMain::continueSolverInputCapture() { if(!m_bSolverStartPending || m_pSolverTask == nullptr) { return; } if(waitForPebiGridGeneration()) { return; } bool bFinished = false; const bool bSucceeded = m_pSolverTask->captureManualInputStep(1, bFinished); if(!bSucceeded) { nmCalculationDllPebiSolverTask* pTask = m_pSolverTask; const bool bCancelled = pTask->wasCancelled(); const bool bInputChanged = pTask->hasInputChanged(m_pSolverDataManager.data()); m_pSolverTask = nullptr; if(pTask == s_pRunningSolverTask) { s_pRunningSolverTask = nullptr; } if(s_pPendingSolverWindow == this) { s_pPendingSolverWindow = nullptr; } m_bSolverStartPending = false; disconnectPebiGridGenerationWait(); disconnect(pTask, nullptr, this, nullptr); pTask->deleteLater(); if(bCancelled) { if(m_pProgressDlg != nullptr) { m_pProgressDlg->hide(); } m_pSolverDataManager = nullptr; m_pSolverFitting = nullptr; } else { scheduleSolverResultCompletion(false, -1, -1, bInputChanged); } m_bSolverCancelRequested = false; return; } if(bFinished) { // 临时井对象指针已全部释放,只有完整值快照可以进入后台线程。 m_bSolverStartPending = false; if(s_pPendingSolverWindow == this) { s_pPendingSolverWindow = nullptr; } disconnectPebiGridGenerationWait(); m_pSolverTask->start(); return; } QTimer::singleShot(0, this, SLOT(continueSolverInputCapture())); } void nmSubWndMain::onSolverStageChanged( QString sStage, int nCurrent, int nTotal) { if(sender() != m_pSolverTask || m_pProgressDlg == nullptr || m_bSolverCancelRequested) { return; } m_pProgressDlg->setLabelText(sStage); if(nTotal <= 0) { m_pProgressDlg->setRange(0, 0); } else { m_pProgressDlg->setRange(0, nTotal); m_pProgressDlg->setValue(qBound(0, nCurrent, nTotal)); } } void nmSubWndMain::onSolverCancelRequested() { if(m_bSolverStartPending && (m_pSolverTask == nullptr || !m_pSolverTask->isRunning())) { // 等待建网或分批捕获期间尚未进入工作线程,可以立即撤销并释放值快照。 nmCalculationDllPebiSolverTask* pTask = m_pSolverTask; m_pSolverTask = nullptr; m_bSolverStartPending = false; if(s_pPendingSolverWindow == this) { s_pPendingSolverWindow = nullptr; } disconnectPebiGridGenerationWait(); if(pTask != nullptr) { pTask->requestCancel(); if(pTask == s_pRunningSolverTask) { s_pRunningSolverTask = nullptr; } disconnect(pTask, nullptr, this, nullptr); pTask->deleteLater(); } // QProgressDialog::cancel() 已隐藏窗口;保留对象供下一次求解复用。 m_pSolverDataManager = nullptr; m_pSolverFitting = nullptr; m_bSolverCancelRequested = false; return; } if(m_pSolverTask != nullptr) { m_bSolverCancelRequested = true; m_pSolverTask->requestCancel(); if(m_pProgressDlg != nullptr) { // DLL 没有取消导出;若已进入 DLL,只能等待其返回后丢弃结果。 m_pProgressDlg->setLabelText( tr("Stopping; waiting for the current solver call to return...")); m_pProgressDlg->setRange(0, 0); QPushButton* pCancelButton = m_pProgressDlg->findChild(); if(pCancelButton != nullptr) { pCancelButton->setEnabled(false); } m_pProgressDlg->show(); m_pProgressDlg->raise(); } } } bool nmSubWndMain::waitForPebiGridGeneration() { nmDataAnalyzeManager* pDataManager = m_pSolverDataManager.data(); if(!m_bSolverStartPending || pDataManager == nullptr || !pDataManager->isPebiGridGenerationActive()) { disconnectPebiGridGenerationWait(); return false; } // 这里只等待主线程提交信号,不获取网格锁;进度框和停止按钮保持可响应。 connect(pDataManager, SIGNAL(sigPebiGridGenerationFinished(quint64,bool)), this, SLOT(onPebiGridGenerationFinished(quint64,bool)), Qt::UniqueConnection); if(m_pProgressDlg != nullptr) { m_pProgressDlg->setLabelText( tr("Waiting for current grid generation...")); m_pProgressDlg->setRange(0, 0); } return true; } void nmSubWndMain::disconnectPebiGridGenerationWait() { nmDataAnalyzeManager* pDataManager = m_pSolverDataManager.data(); if(pDataManager == nullptr) { return; } disconnect(pDataManager, SIGNAL(sigPebiGridGenerationFinished(quint64,bool)), this, SLOT(onPebiGridGenerationFinished(quint64,bool))); } void nmSubWndMain::onPebiGridGenerationFinished( quint64 nGridInputRevision, bool bSucceeded) { Q_UNUSED(nGridInputRevision); Q_UNUSED(bSucceeded); nmDataAnalyzeManager* pDataManager = m_pSolverDataManager.data(); if(sender() != pDataManager || !m_bSolverStartPending || pDataManager == nullptr || pDataManager->isPebiGridGenerationActive()) { return; } disconnectPebiGridGenerationWait(); if(m_pProgressDlg != nullptr) { m_pProgressDlg->setLabelText(tr("Preparing input snapshot...")); m_pProgressDlg->setRange(0, 0); } // 失败或版本过期也恢复原流程,由快照缓存检查决定是否重新生成一次网格。 if(m_pSolverTask == nullptr) { QTimer::singleShot(0, this, SLOT(startSolverTask())); } else { QTimer::singleShot(0, this, SLOT(continueSolverInputCapture())); } } void nmSubWndMain::triggerToolBarAction(int index) { // 获取多边形边界的绘制,并模拟触发点击操作 // 目前多边形边界绘制是工具栏的第4个,所以用 3 if (m_pPlotToolBar == nullptr) return; QList actions = m_pPlotToolBar->actions(); if(!actions.isEmpty() && index >= 0 && index < actions.count()) { QAction *action = actions[index]; action->trigger(); } } bool nmSubWndMain::mergeAnaResultToFitting() { //QMessageBox::information(this, tr("solver success"), tr("solver succeed!")); /*if (m_parameterPropertyWindow != nullptr) { delete m_parameterPropertyWindow; m_parameterPropertyWindow = nullptr; } m_parameterPropertyWindow = new nmWxParameterProperty(this); m_parameterPropertyWindow->show();*/ ZxMainWindow* pMainWnd1 = const_cast(m_pMainWindow); if(nullptr == pMainWnd1) { return false; } iSubWndFitting* pSubWndFit = !m_pSolverFitting.isNull() ? m_pSolverFitting.data() : nmDataAnalyzeManager::getCurrentFitting(); Q_ASSERT(pSubWndFit != nullptr); if(pSubWndFit == nullptr) { return false; } // 历史压力数据 QVector> vvecHistoryData; // 双对数数据 QVector> vvecLogPreData; // 半对数数据 QVector> vvecSemiLogPreData; nmDataAnalyzeManager* pInstance = !m_pSolverDataManager.isNull() ? m_pSolverDataManager.data() : nmDataAnalyzeManager::getCurrentInstance(); if(pInstance == nullptr) { return false; } // 捕获的拟合窗口和数据管理器必须仍保持原来的映射关系。 if(nmDataAnalyzeManager::findManagerByFitting(pSubWndFit) != pInstance) { return false; } // 新一轮主动刷新替代尚未执行的旧请求;已经投递的旧槽会检测标记并直接返回。 m_bPendingResultWidgetRefresh = false; m_pPendingResultDataManager = nullptr; m_pPendingResultFitting = nullptr; m_bPendingResultCompletionMessage = false; m_bPendingResultWindowReady = false; m_nPendingResultPebiCount = -1; m_nPendingResultSolveTimeMs = -1; QVector vecWellsOrder = pInstance->hasPebiResults() ? pInstance->getResultSolverWellOrder() : pInstance->getSolverWellOrder(); QString sCurrentResultWellCode = pInstance->getCurrentResultWellCode(); // 当前结果井必须同时位于最后一次结果顺序并具备结果展示资格。 // 无流量观察井虽然有内部压力结果,但不能成为曲线窗口的当前结果井。 const bool bResultWellAvailable = pInstance->isWellAvailableForResult(sCurrentResultWellCode); bool bResultWellFound = false; for(int nIndex = 0; nIndex < vecWellsOrder.size(); ++nIndex) { const nmSolverWellRef& oWellRef = vecWellsOrder[nIndex]; if(oWellRef.m_eEntryKind != NM_SolverEntry_Well) { continue; } if(bResultWellAvailable && oWellRef.m_sWellCode == sCurrentResultWellCode) { bResultWellFound = true; break; } } if(!bResultWellFound) { for(int nIndex = 0; nIndex < vecWellsOrder.size(); ++nIndex) { if(vecWellsOrder[nIndex].m_eEntryKind == NM_SolverEntry_Well && pInstance->isWellAvailableForResult( vecWellsOrder[nIndex].m_sWellCode)) { sCurrentResultWellCode = vecWellsOrder[nIndex].m_sWellCode; break; } } } nmDataWellBase* pWellData = NULL; if(pInstance->getGridType() == NM_Grid_PEBI) { pWellData = pInstance->findWellByCode(sCurrentResultWellCode); if(pWellData == NULL) { return false; } pInstance->setCurrentResultWellCode(sCurrentResultWellCode); // 获取该井下存储的三条数据 vvecHistoryData = pWellData->getResultPressure(); vvecLogPreData = pWellData->getResultLogLog(); vvecSemiLogPreData = pWellData->getResultSemiLog(); } // 计算完成后同步查看井下拉框选中项 nmWxNumericalDesign::notifyResultWellSelectorChanged( pInstance, sCurrentResultWellCode); // 半对数 QVector vecHalfLog; this->inithalfLog(vvecSemiLogPreData, vecHalfLog, false); FitSubRstTag tag = FSRT_SemiLog; // 半对数 QString errorMessage; pSubWndFit->adjustFitSubPlotBy(tag, vecHalfLog, true, &errorMessage); // 双对数 QVector vecLogLog; this->initloglog(vvecLogPreData, vecLogLog, false); tag = FSRT_DoubleLog; pSubWndFit->adjustFitSubPlotBy(tag, vecLogLog, true, &errorMessage); // 历史压力曲线 QVector vecHistory; this->initPreHistory(vvecHistoryData, vecHistory, false); tag = FSRT_Hist; QString errorMessage4; pSubWndFit->adjustFitSubPlotBy(tag, vecHistory, true, &errorMessage4); if(pInstance->getGridType() != NM_Grid_PEBI) { return true; } // getFitSubRstWxOf(..., true) 会同步创建 iWxResult、挂入 MDI 并切换当前页。 // 首次创建时只建立外层载体,待本轮 show/resize/layout 事件结束后再填充内容。 QWidget* p3DWidget = pSubWndFit->getFitSubRstWxOf( FSRT_Nm3D, false, &errorMessage); if(p3DWidget == nullptr) { p3DWidget = pSubWndFit->getFitSubRstWxOf( FSRT_Nm3D, true, &errorMessage); if(p3DWidget == nullptr) { return false; } m_pPendingResultDataManager = pInstance; m_pPendingResultFitting = pSubWndFit; m_bPendingResultWidgetRefresh = true; return true; } return populateResultWidgets(pSubWndFit, pInstance); } bool nmSubWndMain::populateResultWidgets( iSubWndFitting* pSubWndFit, nmDataAnalyzeManager* pDataManager) { if(pSubWndFit == nullptr || pDataManager == nullptr || pDataManager->getGridType() != NM_Grid_PEBI || nmDataAnalyzeManager::findManagerByFitting(pSubWndFit) != pDataManager) { return false; } QString sErrorMessage; QWidget* p3DWidget = pSubWndFit->getFitSubRstWxOf( FSRT_Nm3D, true, &sErrorMessage); if(p3DWidget == nullptr) { return false; } nmWxPostprocessingAnimationWidget* pVtkWidget = p3DWidget->findChild(); if(pVtkWidget != nullptr) { // 复用既有渲染窗口和 VTK 管道,只切换本轮已整体提交的数据。 pVtkWidget->refreshResult(); } else { pVtkWidget = new nmWxPostprocessingAnimationWidget(NULL, pDataManager); QLayout* p3DLayout = p3DWidget->layout(); if(p3DLayout == nullptr) { p3DLayout = new QVBoxLayout(p3DWidget); } else { QLayoutItem* pItem = nullptr; while((pItem = p3DLayout->takeAt(0)) != nullptr) { if(pItem->widget() != nullptr) { delete pItem->widget(); } delete pItem; } } p3DLayout->addWidget(pVtkWidget); } // 结果参数界面 QWidget* pResultPage = pSubWndFit->getFitSubRstWxOf( FSRT_NmRst, true, &sErrorMessage); if(pResultPage == nullptr) { return false; } nmWxParaPropertyPebi* pResultWidget = pResultPage->findChild(); if(pResultWidget == nullptr) { // 与左侧共用属性面板类,结果区使用独立实例和只读参数列表。 pResultWidget = new nmWxParaPropertyPebi; pResultWidget->initUI(); pResultWidget->setDataManager(pDataManager); // 保持 8 月 24 日版本的稳定顺序:离屏完成参数构建后再加入可见布局。 pResultWidget->rebuildResultParas(); QLayout* pResultLayout = pResultPage->layout(); if(pResultLayout == nullptr) { pResultLayout = new QVBoxLayout(pResultPage); } else { QLayoutItem* pItem = nullptr; while((pItem = pResultLayout->takeAt(0)) != nullptr) { if(pItem->widget() != nullptr) { delete pItem->widget(); } delete pItem; } } pResultLayout->addWidget(pResultWidget); return true; } pResultWidget->setDataManager(pDataManager); pResultWidget->rebuildResultParas(); return true; } void nmSubWndMain::schedulePendingResultWidgets() { if(m_bPendingResultWidgetRefresh) { QTimer::singleShot(0, this, SLOT(populatePendingResultWidgets())); } } void nmSubWndMain::populatePendingResultWidgets() { if(!m_bPendingResultWidgetRefresh) { return; } iSubWndFitting* pSubWndFit = m_pPendingResultFitting.data(); nmDataAnalyzeManager* pDataManager = m_pPendingResultDataManager.data(); m_bPendingResultWidgetRefresh = false; m_pPendingResultDataManager = nullptr; m_pPendingResultFitting = nullptr; const bool bResultWindowReady = populateResultWidgets(pSubWndFit, pDataManager); if(m_bPendingResultCompletionMessage) { m_bPendingResultWindowReady = bResultWindowReady; QTimer::singleShot( 0, this, SLOT(showPendingSolverResultCompletion())); } } void nmSubWndMain::showPendingSolverResultCompletion() { if(!m_bPendingResultCompletionMessage) { return; } const bool bResultWindowReady = m_bPendingResultWindowReady; const int nPebiCount = m_nPendingResultPebiCount; const int nSolveTimeMs = m_nPendingResultSolveTimeMs; m_bPendingResultCompletionMessage = false; m_bPendingResultWindowReady = false; m_nPendingResultPebiCount = -1; m_nPendingResultSolveTimeMs = -1; showSolverResultCompletion(bResultWindowReady, nPebiCount, nSolveTimeMs); } bool nmSubWndMain::inithalfLog(QVector> & vvecLogPreData, QVector& vecDescs, bool isHistoryData) { // 半对数压力曲线 iCurveDesc curveDesc; if(vvecLogPreData.size() != 2) { vecDescs.append(curveDesc); return false; } // X轴数据 curveDesc.m_vecX = vvecLogPreData[0]; // Y轴数据 curveDesc.m_vecY = vvecLogPreData[1]; curveDesc.m_oCurveType = POT_CurvePressure; // 判断是历史数据还是计算数据 if(isHistoryData) { curveDesc.m_sCurveName = s_SemiSouce_Curve; curveDesc.m_bLineVisible = false; // 关闭线模式可见性 curveDesc.m_bPointVisible = true; // 开启点模式可见性 curveDesc.m_oDot.setColor(Qt::red); // 设置点的颜色 curveDesc.m_oDot.setStyle(ZxDotType::DTS_Cross); // x形状 } else { curveDesc.m_sCurveName = s_SemiTheorySouce_Curve; // 理论曲线 curveDesc.m_bLineVisible = true; // 开启线模式可见性 curveDesc.m_bPointVisible = false; // 关闭点模式可见性 curveDesc.m_oPen.setColor(Qt::green); // 设置线的颜色 } // 将 curveDesc 添加到 QVector vecDescs.append(curveDesc); return true; } bool nmSubWndMain::initloglog(QVector > &vvecLogPreData, QVector& vecDescs, bool isHistoryData) { // 双对数压力曲线 iCurveDesc curveDesc1, curveDesc2; if(vvecLogPreData.size() != 3) { vecDescs.append(curveDesc1); vecDescs.append(curveDesc2); return false; } // 确保每个内部 QVector 都不为空,然后移除其最后一个元素 // 移除 vvecLogPreData[0] (vX) 中的最后一个元素 if(!vvecLogPreData[0].isEmpty()) { vvecLogPreData[0].remove(vvecLogPreData[0].size() - 1); } else { return false; } // 移除 vvecLogPreData[1] (vY) 中的最后一个元素 if(!vvecLogPreData[1].isEmpty()) { vvecLogPreData[1].remove(vvecLogPreData[1].size() - 1); } else { return false; } // 移除 vvecLogPreData[2] (vZ) 中的最后一个元素 if(!vvecLogPreData[2].isEmpty()) { vvecLogPreData[2].remove(vvecLogPreData[2].size() - 1); } else { return false; } // 曲线1 curveDesc1.m_vecX = vvecLogPreData[0]; curveDesc1.m_vecY = vvecLogPreData[1]; // 曲线2 curveDesc2.m_vecX = vvecLogPreData[0]; curveDesc2.m_vecY = vvecLogPreData[2]; curveDesc1.m_oCurveType = POT_CurveDiscrete; curveDesc2.m_oCurveType = POT_CurveDiscrete; // 判断是历史数据还是计算数据 if(isHistoryData) { curveDesc1.m_sCurveName = s_Souce_Curve; curveDesc2.m_sCurveName = s_Deriv_Curve; curveDesc1.m_bLineVisible = false; // 关闭线模式可见性 curveDesc1.m_bPointVisible = true; // 开启点模式可见性 curveDesc1.m_oDot.setColor(Qt::green); // 设置点的颜色 curveDesc1.m_oDot.setStyle(ZxDotType::DTS_Cross); // X形状 curveDesc2.m_bLineVisible = false; // 关闭线模式可见性 curveDesc2.m_bPointVisible = true; // 开启点模式可见性 curveDesc2.m_oDot.setColor(Qt::red); // 设置点的颜色 curveDesc2.m_oDot.setStyle(ZxDotType::DTS_Circle); // O形状 curveDesc2.m_oDot.setFilling(false); // 不填充 } else { curveDesc1.m_sCurveName = s_TheorySouce_Curve; curveDesc2.m_sCurveName = s_TheoryDeriv_Curve; curveDesc1.m_bLineVisible = true; // 关闭线模式可见性 curveDesc1.m_bPointVisible = false; // 开启点模式可见性 curveDesc1.m_oPen.setColor(Qt::red); // 设置线的颜色 curveDesc2.m_bLineVisible = true; // 关闭线模式可见性 curveDesc2.m_bPointVisible = false; // 开启点模式可见性 curveDesc2.m_oPen.setColor(Qt::green); // 设置线的颜色 } // 将 curveDesc 添加到 QVector vecDescs.append(curveDesc1); // 将 curveDesc 添加到 QVector vecDescs.append(curveDesc2); return true; } bool nmSubWndMain::initPreHistory(QVector > &vvecHistoryData, QVector& vecDescs, bool isHistoryData) { // 压力曲线 iCurveDesc curveDesc; if(vvecHistoryData.size() != 2) { vecDescs.append(curveDesc); return false; } // X轴数据 curveDesc.m_vecX = vvecHistoryData[0]; // Y轴数据 curveDesc.m_vecY = vvecHistoryData[1]; curveDesc.m_oCurveType = POT_CurvePressure; // 判断是历史数据还是计算数据 if(isHistoryData) { curveDesc.m_sCurveName = s_HistorySouce_CurveP; curveDesc.m_bLineVisible = false; // 关闭线模式可见性 curveDesc.m_bPointVisible = true; // 开启点模式可见性 curveDesc.m_oDot.setColor(Qt::red); // 设置点的颜色 curveDesc.m_oDot.setStyle(ZxDotType::DTS_Plus); // +形状 } else { curveDesc.m_sCurveName = s_HistoryFit_CurveP; curveDesc.m_bLineVisible = true; // 关闭线模式可见性 curveDesc.m_bPointVisible = false; // 开启点模式可见性 curveDesc.m_oPen.setColor(Qt::green); // 设置线的颜色 } // 将 curveDesc 添加到 QVector vecDescs.append(curveDesc); return true; } bool nmSubWndMain::onConfirmClosing() { // 正常关闭入口在求解结束前保持当前成果和 DataManager 存活;应用退出等绕过 // 该确认流程的路径仍由析构函数 wait() 兜底。 if(m_bSolverStartPending || (m_pSolverTask != nullptr && m_pSolverTask->isRunning())) { QMessageBox::information(this, tr("solver error"), tr("task is running!")); return false; } return iSubWndBaseAF::onConfirmClosing(); } void nmSubWndMain::reservoirCharacteristics() { // 第一步:取得当前分析窗口对应的数据管理器,避免新对话框访问全局单例。 nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::getCurrentInstance(); if (pDataManager == nullptr) { QMessageBox::warning(this, tr("Reservoir properties"), tr("The current numerical well-test data is unavailable.")); return; } // 第二步:在入口处组装数据源和编辑器,两个对象的生命周期覆盖整个模态会话。 nmAnalyzeManagerReservoirDataSource oDataSource(pDataManager); nmWxReservoirPropertiesEditor oEditor(&oDataSource, this); // 第三步:先建立工作副本;初始化失败时不打开不完整的参数界面。 QString sError; if (!oEditor.initialize(sError)) { QMessageBox::warning(this, tr("Reservoir properties"), sError); return; } // 第四步:确定时由编辑会话一次性提交,取消时真实数据保持不变。 oEditor.exec(); } void nmSubWndMain::geoLayering() { nmWxGeometryLayerDlg dlg; if(dlg.exec() == QDialog::Rejected) { return; } else { QVector layers = dlg.getLayerData(); // 添加到数据中心 nmDataAnalyzeManager::getCurrentInstance()->setLayers(layers); //for (int i = 0; i < layers.size(); ++i) { // nmDataLayer* layer = layers[i]; // qDebug() << "Layer" << i + 1 << "Top:" << layer->getTop() // << "Bottom:" << layer->getBottom() // << "Thickness:" << layer->getThickness() // << "IsChecked:" << layer->getIsChecked(); //} } } bool nmSubWndMain::on_calculationFinished(NM_Calculation_Result result) { if(result == NM_Calculation_Result_Success) { return this->mergeAnaResultToFitting(); } else { // 如果失败,进度条其实没必要拉满了,直接提示错误 QMessageBox::warning(this, tr("solver error"), tr("solver failed!")); return false; } } void nmSubWndMain::scheduleSolverResultCompletion( bool bSucceeded, int nPebiCount, int nSolveTimeMs, bool bInputChanged) { s_pPendingSolverWindow = this; m_bSolverResultCompletionPending = true; m_bPendingSolverSucceeded = bSucceeded; m_bPendingSolverInputChanged = bInputChanged; m_nPendingSolverPebiCount = nPebiCount; m_nPendingSolverTimeMs = nSolveTimeMs; // 当前完成信号栈只登记后续工作;隐藏和结果刷新各占一轮独立事件。 QTimer::singleShot(0, this, SLOT(dismissPendingSolverProgress())); } void nmSubWndMain::dismissPendingSolverProgress() { if(m_pProgressDlg != nullptr) { m_pProgressDlg->hide(); } QTimer::singleShot(0, this, SLOT(completePendingSolverResult())); } void nmSubWndMain::completePendingSolverResult() { if(!m_bSolverResultCompletionPending) { return; } const bool bSucceeded = m_bPendingSolverSucceeded; const bool bInputChanged = m_bPendingSolverInputChanged; const int nPebiCount = m_nPendingSolverPebiCount; const int nSolveTimeMs = m_nPendingSolverTimeMs; m_bSolverResultCompletionPending = false; m_bPendingSolverSucceeded = false; m_bPendingSolverInputChanged = false; m_nPendingSolverPebiCount = -1; m_nPendingSolverTimeMs = -1; bool bResultWindowReady = false; if(bInputChanged) { QMessageBox::warning( this, tr("Solver result discarded"), tr("The input changed while the solver was preparing or running. " "The outdated result was discarded.")); } else { bResultWindowReady = on_calculationFinished(bSucceeded ? NM_Calculation_Result_Success : NM_Calculation_Result_Fail); } // mergeAnaResultToFitting() 已使用完启动时捕获的上下文。 m_pSolverDataManager = nullptr; m_pSolverFitting = nullptr; m_bSolverCancelRequested = false; if(bSucceeded && bResultWindowReady && m_bPendingResultWidgetRefresh) { // 首次结果页尚未填充,最终提示必须等页面真实创建结果返回后再显示。 m_bPendingResultCompletionMessage = true; m_nPendingResultPebiCount = nPebiCount; m_nPendingResultSolveTimeMs = nSolveTimeMs; } else if(bSucceeded) { showSolverResultCompletion(bResultWindowReady, nPebiCount, nSolveTimeMs); } // 首次结果载体创建后再跨一个事件循环填充,避免 Qt 4.8 布局重入。 schedulePendingResultWidgets(); if(s_pPendingSolverWindow == this) { s_pPendingSolverWindow = nullptr; } } void nmSubWndMain::showSolverResultCompletion( bool bResultWindowReady, int nPebiCount, int nSolveTimeMs) { if(bResultWindowReady && nPebiCount >= 0 && nSolveTimeMs >= 0) { QMessageBox::information(this, tr("Solver completed"), tr("Grid count: %1\nSolve time: %2 ms") .arg(nPebiCount) .arg(nSolveTimeMs)); } else if(!bResultWindowReady) { QMessageBox::warning( this, tr("Result display error"), tr("Solver completed and the results were committed, but the " "result window could not be opened.")); } } void nmSubWndMain::on_solverTaskFinished(bool isSuccessed) { // 第一步:只接受当前窗口实际启动的任务,忽略已经失效的旧回调。 nmCalculationDllPebiSolverTask* pTask = qobject_cast(sender()); if(pTask == nullptr || pTask != m_pSolverTask) { return; } int nPebiCount = pTask != nullptr ? pTask->getPebiCount() : -1; int nSolveTimeMs = pTask != nullptr ? pTask->getSolveTimeMs() : -1; const bool bCancelled = pTask->wasCancelled(); bool bInputChanged = false; // 后台线程只生成局部结果快照。回到主线程后先验证所属 DataManager 和 // 输入版本,再整体替换旧成果;输入变化与真正的求解失败分别提示。 if(isSuccessed && !bCancelled) { if(m_pProgressDlg != nullptr) { m_pProgressDlg->setLabelText(tr("Committing results...")); m_pProgressDlg->setRange(0, 0); } bInputChanged = pTask->hasInputChanged(m_pSolverDataManager.data()); isSuccessed = pTask->commitResult(m_pSolverDataManager.data()); bInputChanged = !isSuccessed && bInputChanged; } else if(bCancelled) { isSuccessed = false; } m_pSolverTask = nullptr; if(pTask == s_pRunningSolverTask) { s_pRunningSolverTask = nullptr; } // 正常完成路径由主线程延迟释放任务;析构路径则会等待并直接释放。 pTask->deleteLater(); // 第二步:停止不属于失败,也不允许触发结果提交或覆盖旧成果。 if(bCancelled) { if(m_pProgressDlg != nullptr) { m_pProgressDlg->hide(); } m_pSolverDataManager = nullptr; m_pSolverFitting = nullptr; m_bSolverCancelRequested = false; } else { scheduleSolverResultCompletion(isSuccessed, nPebiCount, nSolveTimeMs, bInputChanged); } } void nmSubWndMain::deleteOneObj() { this->triggerToolBarAction(NMOT_Delete); } void nmSubWndMain::setLocked() { if(m_lockState) { // 锁定 m_lockState = false; int objCount = this->m_pWxPlot->m_pPlot->getObjCount(); for(int i = 0; i < objCount; i++) { ZxObjBase* obj = this->m_pWxPlot->m_pPlot->getObjByIndex(i); if(!_isSame("nObjPointWell", obj->getTagName())) { obj->setReadOnly(false); obj->setLockPos(false); } } QStringList nIDs; nIDs.append("5125"); nIDs.append("5124"); nIDs.append("5123"); nIDs.append("5114"); nIDs.append("5115"); nIDs.append("5116"); nIDs.append("5117"); nIDs.append("5118"); nIDs.append("5119"); nIDs.append("5102"); nIDs.append("5120"); nIDs.append("5103"); nIDs.append("5104"); nIDs.append("5105"); nIDs.append("5106"); nIDs.append("5107"); // nIDs.append("5108"); // 新建井菜单暂时停用 nIDs.append("5109"); nIDs.append("5110"); nIDs.append("5111"); nIDs.append("5121"); nIDs.append("5112"); nIDs.append("5113"); nIDs.append("5122"); nIDs.append("9002"); nIDs.append("9003"); nIDs.append("9101"); nIDs.append("9102"); nIDs.append("9103"); nIDs.append("9104"); nIDs.append("3104"); emit sigFreshRnStates(nIDs); /* if (m_pMainWindow != NULL) { //m_pMainWindow->slotFreshRnStates(nIDs); } */ //m_pMainWindow->getMainRibbon } else { m_lockState = true; int objCount = this->m_pWxPlot->m_pPlot->getObjCount(); for(int i = 0; i < objCount; i++) { ZxObjBase* obj = this->m_pWxPlot->m_pPlot->getObjByIndex(i); if(!_isSame("nObjPointWell", obj->getTagName())) { obj->setReadOnly(true); obj->setLockPos(true); } } QStringList nIDs; nIDs.append("5125"); nIDs.append("5124"); nIDs.append("5123"); nIDs.append("5114"); nIDs.append("5115"); nIDs.append("5116"); nIDs.append("5117"); nIDs.append("5118"); nIDs.append("5119"); nIDs.append("5102"); nIDs.append("5120"); nIDs.append("5103"); nIDs.append("5104"); nIDs.append("5105"); nIDs.append("5106"); nIDs.append("5107"); // nIDs.append("5108"); // 新建井菜单暂时停用 nIDs.append("5109"); nIDs.append("5110"); nIDs.append("5111"); nIDs.append("5121"); nIDs.append("5112"); nIDs.append("5113"); nIDs.append("5122"); //nIDs.append("9002"); //nIDs.append("9003"); nIDs.append("9101"); nIDs.append("9102"); nIDs.append("9103"); nIDs.append("9104"); nIDs.append("3104"); emit sigFreshRnStates(nIDs); /* if (m_pMainWindow != NULL) { m_pMainWindow->slotFreshRnStates(nIDs); } */ /* emit sigFreshRnStates(nIDs); if (m_pMainWindow != NULL) { m_pMainWindow->slotFreshRnStates(nIDs); } bool b; QString sName = "NmUnlocked"; int nID = 5119; bool bLicensed; nmSubWndMain::checkCmdEnable(b, sName, nID, bLicensed); nID = 5125; sName = "NmPrint"; nmSubWndMain::checkCmdEnable(b, sName, nID, bLicensed); */ } } nmGuiPlot * nmSubWndMain::getWxPlot() const { return m_pWxPlot; } void nmSubWndMain::onWellSelected(const QString& sWellCode) { nmDataAnalyzeManager* pInstance = nmDataAnalyzeManager::getCurrentInstance(); if(pInstance == nullptr) { return; } nmDataWellBase* pWellData = pInstance->findWellByCode(sWellCode); if(pWellData == NULL) return; // 切换结果井只影响曲线展示,不再改变参数编辑井或主分析井。 pInstance->setCurrentResultWellCode(sWellCode); // 1.获取流动段分析窗口 iSubWndFitting* pSubWndFit = nmDataAnalyzeManager::getCurrentFitting(); Q_ASSERT(pSubWndFit != nullptr); if(!pSubWndFit) return; // 2.根据选择的井名获取计算数据 QVector> vvecHistoryData; QVector> vvecLogPreData; QVector> vvecSemiLogPreData; if(pInstance->getGridType() == NM_Grid_PEBI) { vvecHistoryData = pWellData->getResultPressure(); vvecLogPreData = pWellData->getResultLogLog(); vvecSemiLogPreData = pWellData->getResultSemiLog(); } // 调用函数 QVector> vvecHistoryPressureData; //压力历史数据 QVector> vvecHistoryLogData; // 历史双对数曲线数据 QVector> vvecHistorySemiLogData; // 历史半对数曲线数据 pInstance->calculationLogData(pWellData, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData); // 删除末尾无效元素 //if (!vvecHistoryLogData.empty() && // vvecHistoryLogData.size() >= 3 && // 确保有3个子数组 // !vvecHistoryLogData[0].empty() && // 检查x数组非空 // !vvecHistoryLogData[1].empty() && // 检查y数组非空 // !vvecHistoryLogData[2].empty()) { // 检查z数组非空 // // 同时删除x、y、z的最后一个数据点 // vvecHistoryLogData[0].removeLast(); // 删除最后一个x // vvecHistoryLogData[1].removeLast(); // 删除最后一个y // vvecHistoryLogData[2].removeLast(); // 删除最后一个z //} // 窗口标识,半对数、双对数、历史压力 FitSubRstTag tag; // 错误信息 QString errorMessage; // 3、半对数曲线 QVector vecHistoryHalfLog; this->inithalfLog(vvecHistorySemiLogData, vecHistoryHalfLog, true); tag = FSRT_SemiLog; // 删除原来的线,并将当前查看井的历史半对数曲线渲染出来 pSubWndFit->adjustFitSubPlotBy(tag, vecHistoryHalfLog, true, &errorMessage); //计算出来的半对数数据,同样需要删除原来的 QVector vecHalfLog; this->inithalfLog(vvecSemiLogPreData, vecHalfLog, false); pSubWndFit->adjustFitSubPlotBy(tag, vecHalfLog, true, &errorMessage); // 双对数 QVector vecHistoryLog; this->initloglog(vvecHistoryLogData, vecHistoryLog, true); tag = FSRT_DoubleLog; // 删除原来的线,并将当前查看井的历史双对数曲线渲染出来 pSubWndFit->adjustFitSubPlotBy(tag, vecHistoryLog, true, &errorMessage); //计算出来的双对数数据,同样需要删除原来的 QVector vecLogLog; this->initloglog(vvecLogPreData, vecLogLog, false); pSubWndFit->adjustFitSubPlotBy(tag, vecLogLog, true, &errorMessage); // 历史压力曲线 QVector vecHistoryPre; this->initPreHistory(vvecHistoryPressureData, vecHistoryPre, true); tag = FSRT_Hist; // 删除原来的线,并将当前查看井的历史压力曲线渲染出来 pSubWndFit->adjustFitSubPlotBy(tag, vecHistoryPre, true, &errorMessage); //计算出来的压力数据,同样需要删除原来的 QVector vecHistory; this->initPreHistory(vvecHistoryData, vecHistory, false); pSubWndFit->adjustFitSubPlotBy(tag, vecHistory, true, &errorMessage); } #include //void nmSubWndMain::calculationLogData( // nmDataWellBase* pWellData, // QVector>& vvecHistoryData, // QVector>& vvecLogPreData, // QVector>& vvecSemiLogPreData) //{ // // 清空输出参数 // vvecHistoryData.clear(); // vvecLogPreData.clear(); // vvecSemiLogPreData.clear(); // // if(pWellData == nullptr) { // return; // } // // // 初始化二维数组结构 // vvecHistoryData.resize(2); // [0]=x, [1]=y // vvecLogPreData.resize(3); // [0]=x, [1]=y, [2]=z // vvecSemiLogPreData.resize(2); // [0]=x, [1]=pointData[0] // // // 准备压力数据 // QVector vecPressure = pWellData->getPressurePoints(); // std::vector wellPressureData; // // // 填充 wellPressureData 和 vvecHistoryData // foreach(const QPointF& qpoint, vecPressure) { // // wellPressureData // Point pt; // pt.x = qpoint.x(); // pt.y = qpoint.y(); // pt.z = 0.0; // wellPressureData.push_back(pt); // // // vvecHistoryData // vvecHistoryData[0].append(qpoint.x()); // x // vvecHistoryData[1].append(qpoint.y()); // y // } // // // 准备流量段数据 // QVector vecTimeQ = pWellData->getFlowPoints(); // int nTimeNumQ = vecTimeQ.size() - 1; // std::vector timeQ(nTimeNumQ); // std::vector q(nTimeNumQ); // // for(int i = 0; i < nTimeNumQ; ++i) { // timeQ[i] = vecTimeQ[i + 1].x(); // q[i] = vecTimeQ[i + 1].y(); // } // // // 调用 DLL 计算双对数曲线 // std::vector logPre; // int iSectionFlowIndex = pWellData->getIndexF(); // // HMODULE hMod_solver = LoadLibrary(L"singlePhaseSolverDll.dll"); // // if(hMod_solver) { // typedef bool (*PreLog)(const std::vector&, const int&, double*, double*, int, std::vector&); // PreLog preLogFun = (PreLog)GetProcAddress(hMod_solver, "logLogPre"); // // if(nullptr == preLogFun) { // FreeLibrary(hMod_solver); // std::cout << "preLogFun failed!\n"; // return; // } // // preLogFun(wellPressureData, iSectionFlowIndex, timeQ.data(), q.data(), nTimeNumQ, logPre); // // // 不添加最后一个元素 // for(uint i = 0; i < logPre.size() - 1; i++) { // //logFile << logPre[i].x << "\t" << logPre[i].y << "\t" << logPre[i].z << "\t" << std::endl; // vvecLogPreData[0].append(logPre[i].x); // x // vvecLogPreData[1].append(logPre[i].y); // y // vvecLogPreData[2].append(logPre[i].z); // z // } // // // 填充半对数曲线数据 (x, pointData[0]) // foreach(const auto& point, logPre) { // vvecSemiLogPreData[0].append(point.x); // x // double y_value = point.pointData.empty() ? 0.0 : point.pointData[0]; // vvecSemiLogPreData[1].append(y_value); // pointData[0] 或默认值 // } // FreeLibrary(hMod_solver); // } //} void nmSubWndMain::onGenerateButtonClicked() { if(nmDataAnalyzeManager::getCurrentInstance()->getGridType() == NM_Grid_PEBI) { // 求解任务创建时会捕获完整输入。网格无效时,工作线程只基于值快照生成 // 一次局部网格,求解成功后再和全部结果一起回主线程提交。 this->solveAndAnalyze(); } } void nmSubWndMain::generateDFN() { nmWxDFN dfnDialog(nullptr); dfnDialog.exec(); } void nmSubWndMain::displaySetting() { nmWxDisplaySettings disPlayDlg; disPlayDlg.exec(); } void nmSubWndMain::updateMapByDataManager(nmDataAnalyzeManager* pDataManager) { // 删除原来的图元信息 if(m_pWxPlot == nullptr || pDataManager == nullptr) { return; } // 一个Map同一时间只展示一个分析的数据,切换前先解除其他DataManager对当前Plot的绑定。 foreach(nmDataAnalyzeManager* pManager, nmDataAnalyzeManager::s_mapDataAnalManager.values()) { if(pManager != nullptr && pManager != pDataManager && pManager->getPlot() == m_pWxPlot) { pManager->setPlot(nullptr); } } // 更新对应的Plot成员变量 pDataManager->setPlot(m_pWxPlot); m_pWxPlot->deleteAllPlotObjs(); // 切换分析时更新 Binder 绑定的 DataManager, // 确保用户新画断层时添加到正确的 DataManager if(m_pWxPlot->graphicBinder()) { m_pWxPlot->graphicBinder()->updateDataMgr(pDataManager); } // 根据数据中心内容绘制新图元 m_pWxPlot->setPlotsByDataManger(pDataManager); }