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

2656 lines
81 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
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;
}
// 先创建不自动捕获的任务,使停止按钮在第一批井输入开始前就有明确接收者。
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;
}
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;
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;
}
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) {
// 尚未创建任务时可立即撤销DataManager 也尚未登记后台使用权。
m_bSolverStartPending = false;
if(s_pPendingSolverWindow == this) {
s_pPendingSolverWindow = nullptr;
}
// 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();
}
}
}
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);
}