You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmSubWnd/nmSubWndMain.cpp

2743 lines
84 KiB
C++

#include "zxLogInstance.h"
#include "ZxBaHelper.h"
#include <QDebug>
#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 <QVector>
#include <QSet>
#include <QHash>
#include <QPushButton>
#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 <QPointer>
namespace
{
// 保留原来求解器单例的防重入语义全局同一时间只允许一个DLL求解任务运行。
// QPointer会在QObject销毁后自动变空避免保留已经释放的线程指针。
QPointer<nmCalculationDllPebiSolverTask> s_pRunningSolverTask;
QPointer<nmSubWndMain> 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_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<ZxMainWindow*>(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<nmObjBase*> 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<nmGuiPlot*>(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();
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<QAction *> 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<ZxDataGaugeP*>(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<QPointF> 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<ZxObjCurve*>(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<ZxDataWell *> wellObjList)
{
// if(wellObjList.count() == 0) {
// return;
// }
// setp 1获取现在画布上所有井的 ZxDataWell* 数据指针
QVector<nmObjPointWell*> pWellPlotList = m_pWxPlot->getWellPlots();
// QMap<ZxDataWell*, nmObjPointWell*> 用于快速查找和构建当前画布上的井数据列表
QMap<ZxDataWell*, nmObjPointWell*> 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<ZxDataWell*> pWellPlotDataList = pWellDataToPlotMap.keys();
QSet<ZxDataWell*> pWellPlotDataSet;
for(int i = 0; i < pWellPlotDataList.count(); ++i) {
pWellPlotDataSet.insert(pWellPlotDataList[i]);
}
QSet<ZxDataWell*> selectedWellDataSet;
for(int i = 0; i < wellObjList.count(); ++i) {
if(wellObjList[i] != nullptr) {
selectedWellDataSet.insert(wellObjList[i]);
}
}
// setp 2找出需要 REMOVE (删除) 的图元
QVector<nmObjPointWell*> 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<ZxDataWell*> 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<QPointF> 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<QDockWidget*> dockWidgets = m_pMainWindow->findChildren<QDockWidget*>();
// 遍历所有 Dock 视图并移除位于左侧的
foreach(QDockWidget* dock, dockWidgets) {
if(visible) {
dock->show();
} else {
dock->hide();
}
}
}
void nmSubWndMain::drawPolygonOutline()
{
// 获取多边形边界的绘制,并模拟触发点击操作
// 目前多边形边界绘制是工具栏的第4个所以用 3
// QList<QAction *> 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<QVariant> selectedWellCodes;
QVector<ZxDataWell*> 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<QVariant> wellCodes;
QMap<QString, ZxDataWell*> wellCodeToObjMap;
for(int i = 0; i < wellList.size(); i++) {
ZxDataWell* wellObj = dynamic_cast<ZxDataWell*>(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<QVariant> selectedValues = wellListWidget.getSelectedValues();
QList<ZxDataWell*> 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<nmGuiPlot*>(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<nmGuiPlot*>(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<nmGuiPlot*>(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<nmGuiPlot*>(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<nmGuiPlot*>(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<ZxObjBase*> pObjVec = m_pWxPlot->getObjsByTag("nObjPolygonOutline");
// nmDataLogFile::getInstance()->writeLog(" ---------- " + QString::number(pObjVec.count()));
// QVector<ZxObjBase*> 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()) {
QMessageBox::information(this, tr("solver error"), tr("task is running!"));
return;
}
// 强制清理旧的(以防万一上次没删掉)
if (m_pProgressDlg != nullptr) {
delete m_pProgressDlg;
m_pProgressDlg = nullptr;
}
// 新一次启动前清空旧上下文;后续所有检查通过后再成对写入。
m_pSolverDataManager = nullptr;
m_pSolverFitting = nullptr;
// 根据当前数据中心的分析切换到对应的流动段分析窗口
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(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<nmCalculationWellRef> vecEffectiveWells =
pDataManager->getEffectiveCalculationWells();
QHash<QString, nmDataWellBase*> mapWellsByCode;
const QVector<nmDataWellBase*> 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"));
// 后台只读取任务值快照;应用级模态仍用于阻止重复启动、切换成果等会让
// 用户误判当前计算归属的操作,数据安全不再依赖该对话框冻结界面。
m_pProgressDlg->setWindowModality(Qt::ApplicationModal);
// 某些系统下,对话框右上角的关闭按钮可能仍存
// 使用 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->deleteLater();
m_pProgressDlg = nullptr;
}
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)));
connect(m_pSolverTask, SIGNAL(sigStageChanged(QString,int,int)),
this, SLOT(onSolverStageChanged(QString,int,int)));
// 给 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();
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) {
delete m_pProgressDlg;
m_pProgressDlg = nullptr;
} else {
on_calculationFinished(NM_Calculation_Result_Fail);
}
m_pSolverDataManager = nullptr;
m_pSolverFitting = nullptr;
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();
}
// canceled() 由进度框自身发出,延迟释放避免在信号调用栈内销毁发送者。
if(m_pProgressDlg != nullptr) {
m_pProgressDlg->deleteLater();
}
m_pProgressDlg = nullptr;
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<QPushButton*>();
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<QAction *> actions = m_pPlotToolBar->actions();
if(!actions.isEmpty() && index >= 0 && index < actions.count()) {
QAction *action = actions[index];
action->trigger();
}
}
void 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<ZxMainWindow*>(m_pMainWindow);
if(nullptr == pMainWnd1) {
return;
}
iSubWndFitting* pSubWndFit = !m_pSolverFitting.isNull()
? m_pSolverFitting.data() : nmDataAnalyzeManager::getCurrentFitting();
Q_ASSERT(pSubWndFit != nullptr);
if(pSubWndFit == nullptr) {
return;
}
// 历史压力数据
QVector<QVector<double >> vvecHistoryData;
// 双对数数据
QVector<QVector<double >> vvecLogPreData;
// 半对数数据
QVector<QVector<double >> vvecSemiLogPreData;
nmDataAnalyzeManager* pInstance = !m_pSolverDataManager.isNull()
? m_pSolverDataManager.data()
: nmDataAnalyzeManager::getCurrentInstance();
if(pInstance == nullptr) {
return;
}
// 捕获的拟合窗口和数据管理器必须仍保持原来的映射关系。
if(nmDataAnalyzeManager::findManagerByFitting(pSubWndFit) != pInstance) {
return;
}
QVector<nmSolverWellRef> vecWellsOrder = 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;
}
pInstance->setCurrentResultWellCode(sCurrentResultWellCode);
// 获取该井下存储的三条数据
vvecHistoryData = pWellData->getResultPressure();
vvecLogPreData = pWellData->getResultLogLog();
vvecSemiLogPreData = pWellData->getResultSemiLog();
}
// 计算完成后同步查看井下拉框选中项
nmWxNumericalDesign::notifyResultWellSelectorChanged(
pInstance,
sCurrentResultWellCode);
// 半对数
QVector<iCurveDesc> vecHalfLog;
this->inithalfLog(vvecSemiLogPreData, vecHalfLog, false);
FitSubRstTag tag = FSRT_SemiLog; // 半对数
QString errorMessage;
pSubWndFit->adjustFitSubPlotBy(tag, vecHalfLog, true, &errorMessage);
// 双对数
QVector<iCurveDesc> vecLogLog;
this->initloglog(vvecLogPreData, vecLogLog, false);
tag = FSRT_DoubleLog;
pSubWndFit->adjustFitSubPlotBy(tag, vecLogLog, true, &errorMessage);
// 历史压力曲线
QVector<iCurveDesc> vecHistory;
this->initPreHistory(vvecHistoryData, vecHistory, false);
tag = FSRT_Hist;
QString errorMessage4;
pSubWndFit->adjustFitSubPlotBy(tag, vecHistory, true, &errorMessage4);
QWidget* widget1 = pSubWndFit->getFitSubRstWxOf(FSRT_Nm3D, true, &errorMessage);
if(widget1 == nullptr || pInstance->getGridType() != NM_Grid_PEBI) {
return;
}
nmWxPostprocessingAnimationWidget* vtkWidget =
widget1->findChild<nmWxPostprocessingAnimationWidget*>();
if(vtkWidget != nullptr) {
// 复用既有渲染窗口和 VTK 管道,只切换本轮已整体提交的数据。
vtkWidget->refreshResult();
} else {
vtkWidget = new nmWxPostprocessingAnimationWidget(NULL, pInstance);
QLayout* layout1 = widget1->layout();
if(layout1) {
QLayoutItem* item;
while((item = layout1->takeAt(0))) {
if(item->widget()) {
delete item->widget();
}
delete item;
}
delete layout1;
}
widget1->setLayout(nullptr);
QVBoxLayout* newLayout1 = new QVBoxLayout(widget1);
newLayout1->addWidget(vtkWidget);
widget1->setLayout(newLayout1);
}
// 结果参数界面
QWidget* widget2 = pSubWndFit->getFitSubRstWxOf(FSRT_NmRst, true, &errorMessage);
if(widget2 == nullptr) {
return;
}
nmWxParaPropertyPebi* resultWidget =
widget2->findChild<nmWxParaPropertyPebi*>();
if(resultWidget == nullptr) {
// 与左侧共用属性面板类,结果区使用独立实例和只读参数列表。
resultWidget = new nmWxParaPropertyPebi;
resultWidget->initUI();
QLayout* layout2 = widget2->layout();
if(layout2) {
QLayoutItem* item;
while((item = layout2->takeAt(0))) {
if(item->widget()) {
delete item->widget();
}
delete item;
}
delete layout2;
}
widget2->setLayout(nullptr);
QVBoxLayout* newLayout2 = new QVBoxLayout(widget2);
newLayout2->addWidget(resultWidget);
widget2->setLayout(newLayout2);
}
resultWidget->setDataManager(pInstance);
resultWidget->rebuildResultParas();
}
bool nmSubWndMain::inithalfLog(QVector<QVector<double >> & vvecLogPreData, QVector<iCurveDesc>& 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<QVector<double> > &vvecLogPreData, QVector<iCurveDesc>& 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<QVector<double> > &vvecHistoryData, QVector<iCurveDesc>& 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<nmDataLayer*> 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();
//}
}
}
void nmSubWndMain::on_calculationFinished(NM_Calculation_Result result)
{
// 结果提交成功后仍保留进度框,直到曲线和结果页刷新全部完成。
if (m_pProgressDlg) {
m_pProgressDlg->setLabelText(tr("Updating result views..."));
m_pProgressDlg->setRange(0, 0);
}
if(result == NM_Calculation_Result_Success) {
this->mergeAnaResultToFitting();
} else {
// 如果失败,进度条其实没必要拉满了,直接提示错误
QMessageBox::warning(this, tr("solver error"), tr("solver failed!"));
}
if (m_pProgressDlg != nullptr) {
delete m_pProgressDlg;
m_pProgressDlg = nullptr;
}
}
void nmSubWndMain::on_solverTaskFinished(bool isSuccessed)
{
// 第一步:只接受当前窗口实际启动的任务,忽略已经失效的旧回调。
nmCalculationDllPebiSolverTask* pTask = qobject_cast<nmCalculationDllPebiSolverTask*>(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();
// 后台线程只生成局部结果快照。回到主线程后先验证所属 DataManager 和
// 输入版本,再整体替换旧成果;提交失败与求解失败使用同一条错误路径。
if(isSuccessed && !bCancelled) {
if(m_pProgressDlg != nullptr) {
m_pProgressDlg->setLabelText(tr("Committing results..."));
m_pProgressDlg->setRange(0, 0);
}
isSuccessed = pTask->commitResult(m_pSolverDataManager.data());
} else if(bCancelled) {
isSuccessed = false;
}
m_pSolverTask = nullptr;
if(pTask == s_pRunningSolverTask) {
s_pRunningSolverTask = nullptr;
}
// 正常完成路径由主线程延迟释放任务;析构路径则会等待并直接释放。
pTask->deleteLater();
// 第二步:停止不属于失败,也不允许触发结果提交或覆盖旧成果。
if(bCancelled) {
delete m_pProgressDlg;
m_pProgressDlg = nullptr;
} else {
NM_Calculation_Result result = isSuccessed
? NM_Calculation_Result_Success
: NM_Calculation_Result_Fail;
this->on_calculationFinished(result);
}
// 第三步:结果处理结束后释放非拥有型上下文引用。
m_pSolverDataManager = nullptr;
m_pSolverFitting = nullptr;
m_bSolverCancelRequested = false;
if(isSuccessed && nPebiCount >= 0 && nSolveTimeMs >= 0) {
QMessageBox::information(this, tr("Solver completed"),
tr("Grid count: %1\nSolve time: %2 ms")
.arg(nPebiCount)
.arg(nSolveTimeMs));
}
}
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<QVector<double>> vvecHistoryData;
QVector<QVector<double>> vvecLogPreData;
QVector<QVector<double>> vvecSemiLogPreData;
if(pInstance->getGridType() == NM_Grid_PEBI) {
vvecHistoryData = pWellData->getResultPressure();
vvecLogPreData = pWellData->getResultLogLog();
vvecSemiLogPreData = pWellData->getResultSemiLog();
}
// 调用函数
QVector<QVector<double>> vvecHistoryPressureData; //压力历史数据
QVector<QVector<double>> vvecHistoryLogData; // 历史双对数曲线数据
QVector<QVector<double>> 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<iCurveDesc> vecHistoryHalfLog;
this->inithalfLog(vvecHistorySemiLogData, vecHistoryHalfLog, true);
tag = FSRT_SemiLog;
// 删除原来的线,并将当前查看井的历史半对数曲线渲染出来
pSubWndFit->adjustFitSubPlotBy(tag, vecHistoryHalfLog, true, &errorMessage);
//计算出来的半对数数据,同样需要删除原来的
QVector<iCurveDesc> vecHalfLog;
this->inithalfLog(vvecSemiLogPreData, vecHalfLog, false);
pSubWndFit->adjustFitSubPlotBy(tag, vecHalfLog, true, &errorMessage);
// 双对数
QVector<iCurveDesc> vecHistoryLog;
this->initloglog(vvecHistoryLogData, vecHistoryLog, true);
tag = FSRT_DoubleLog;
// 删除原来的线,并将当前查看井的历史双对数曲线渲染出来
pSubWndFit->adjustFitSubPlotBy(tag, vecHistoryLog, true, &errorMessage);
//计算出来的双对数数据,同样需要删除原来的
QVector<iCurveDesc> vecLogLog;
this->initloglog(vvecLogPreData, vecLogLog, false);
pSubWndFit->adjustFitSubPlotBy(tag, vecLogLog, true, &errorMessage);
// 历史压力曲线
QVector<iCurveDesc> vecHistoryPre;
this->initPreHistory(vvecHistoryPressureData, vecHistoryPre, true);
tag = FSRT_Hist;
// 删除原来的线,并将当前查看井的历史压力曲线渲染出来
pSubWndFit->adjustFitSubPlotBy(tag, vecHistoryPre, true, &errorMessage);
//计算出来的压力数据,同样需要删除原来的
QVector<iCurveDesc> vecHistory;
this->initPreHistory(vvecHistoryData, vecHistory, false);
pSubWndFit->adjustFitSubPlotBy(tag, vecHistory, true, &errorMessage);
}
#include <Windows.h>
//void nmSubWndMain::calculationLogData(
// nmDataWellBase* pWellData,
// QVector<QVector<double>>& vvecHistoryData,
// QVector<QVector<double>>& vvecLogPreData,
// QVector<QVector<double>>& 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<QPointF> vecPressure = pWellData->getPressurePoints();
// std::vector<Point> 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<QPointF> vecTimeQ = pWellData->getFlowPoints();
// int nTimeNumQ = vecTimeQ.size() - 1;
// std::vector<double> timeQ(nTimeNumQ);
// std::vector<double> q(nTimeNumQ);
//
// for(int i = 0; i < nTimeNumQ; ++i) {
// timeQ[i] = vecTimeQ[i + 1].x();
// q[i] = vecTimeQ[i + 1].y();
// }
//
// // 调用 DLL 计算双对数曲线
// std::vector<Point> logPre;
// int iSectionFlowIndex = pWellData->getIndexF();
//
// HMODULE hMod_solver = LoadLibrary(L"singlePhaseSolverDll.dll");
//
// if(hMod_solver) {
// typedef bool (*PreLog)(const std::vector<Point>&, const int&, double*, double*, int, std::vector<Point>&);
// 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);
}