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

2907 lines
89 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 "nmPebiResultSnapshot.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_pPendingResultDataManager = nullptr;
m_pPendingResultFitting = nullptr;
m_bPendingResultWidgetRefresh = false;
m_bPendingResultCompletionMessage = false;
m_bPendingResultWindowReady = false;
m_nPendingResultPebiCount = -1;
m_nPendingResultSolveTimeMs = -1;
m_bSolverResultCompletionPending = false;
m_bPendingSolverSucceeded = false;
m_bPendingSolverInputChanged = false;
m_nPendingSolverPebiCount = -1;
m_nPendingSolverTimeMs = -1;
m_bSolverStartPending = false;
m_bSolverCancelRequested = false;
m_pPlotToolBar = nullptr;
}
nmSubWndMain::~nmSubWndMain()
{
if(m_pWxPlot != nullptr) {
// Map窗口析构时解除DataManager中保存的Plot引用避免后续访问已释放画布。
foreach(nmDataAnalyzeManager* pManager, nmDataAnalyzeManager::s_mapDataAnalManager.values()) {
if(pManager != nullptr && pManager->getPlot() == m_pWxPlot) {
pManager->setPlot(nullptr);
}
}
}
#ifdef QT_DEBUG
// 坚持谁创建谁析构原则,删除(首先需要去除绑定)
if(NULL != m_pWxDockTemp) {
//QObject* pParent = m_pWxDockTemp->parent();
//ZxMainWindow* p = dynamic_cast<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();
this->schedulePendingResultWidgets();
return true;
case 5121:
this->deleteOneObj();
return true;
case 5119:
this->setLocked();
return true;
case 5122:
this->generateDFN();
return true;
case 5123:
this->displaySetting();
return true;
case 5124:
return m_pWxPlot != nullptr &&
m_pWxPlot->runAction("print_Preview");
case 5125:
return m_pWxPlot != nullptr &&
m_pWxPlot->runAction("Print");
/*
case 5117:
this->initData();
return true;
*/
case 5129:
if(isDialogCmdOpened(5129)) {
return true;
}
this->drawMeasure();
return true;
case 5127:
this->drawGeoRef();
return true;
case 5128:
if(isDialogCmdOpened(5128)) {
return true;
}
this->drawPointerPos();
return true;
case 5126:
if(isDialogCmdOpened(5126)) {
return true;
}
this->showPropertyInterpolationDialog();
return true;
}
if(_isSame(s, "RunGrid")) {
if(NULL != m_pWxPlot) {
//m_pWxPlot->refreshGrid(true);
}
return true;
}
return false;
}
bool nmSubWndMain::checkCmdEnable(bool &b, \
QString sName, int nID, \
bool bLicensed)
{
bool b1 = (NULL != zxCurProject);
bool b2 = (NULL != m_pDataWell);
// 判断当前锁定状态
if(m_lockState) {
/*
if(nID >= 5102 && nID <= 5125) {
b = (b1 && b2 && bLicensed && !isReadonly() && NULL != m_pWxPlot);
return true;
}
*/
/*
if ((nID >= 5122 && nID <= 5125) || (nID >= 5112 && nID <= 5119 ))
{
b = true;
return true;
}
*/
/*
if(nID >= 5102 && nID <= 5124) {
b = true;
return true;
}
*/
// 可使用菜单栏
if(sName == "NmMap" || sName == "NmUnlocked" || sName == "NmGeometry" ||
sName == "NmProperties" || sName == "NmDFN" || sName == "NmCaculation" ||
sName == "NmBack" || sName == "NmResult" || sName == "NmGrid" ||
sName == "NmInterpolation" ||
sName == "NmShow" || sName == "NmPrintPreview" || sName == "NmPrint" ||
sName == "VisibleAnalBasic" || sName == "VisibleAnalPVT" || sName == "VisibleAnalDiff" ||
sName == "VisibleAnalPseu" || sName == "nmFitModel" || sName == "NmAnalChange") {
b = true;
return true;
} else {
b = false;
return false;
}
} else {
if((nID >= 5102 && nID <= 5130) /*|| (nID >= 9002 && nID <= 9003) */ || (nID >= 9101 && nID <= 9104) || nID == 3104) {
b = (b1 && b2 && bLicensed && !isReadonly() && NULL != m_pWxPlot);
return true;
}
}
return iSubWndBaseFit::checkCmdEnable(b, sName, nID, bLicensed);
}
void nmSubWndMain::reAdjustToolbar(QToolBar* pToolBar)
{
iSubWndBaseAF::reAdjustToolbar(pToolBar);
m_pPlotToolBar = pToolBar;
// 获取所有动作的列表
// QList<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()->isPrimaryWellGraphic(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() ||
m_bSolverResultCompletionPending) {
QMessageBox::information(this, tr("solver error"), tr("task is running!"));
return;
}
// Qt 4.8 下复用已经隐藏的进度框,避免窗口析构触发主界面重绘重入。
if (m_pProgressDlg != nullptr) {
m_pProgressDlg->reset();
QPushButton* pCancelButton =
m_pProgressDlg->findChild<QPushButton*>();
if(pCancelButton != nullptr) {
pCancelButton->setEnabled(true);
}
}
// 新一次启动前清空旧上下文;后续所有检查通过后再成对写入。
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(!nmIsValidWellCategory(pWellData->getWellCategory()) ||
pWellData->getFlowSegmentCount() <= 0 ||
!pWellData->hasValidFlowSectionIndex()) {
QMessageBox::warning(
this,
tr("solver error"),
tr("Rate-controlled well '%1' has no valid rate schedule.")
.arg(pWellData->getWellName()));
return;
}
if(!nmSolverModelSupportsWellCategory(
pDataManager->getSolverModelType(),
pWellData->getWellCategory())) {
QMessageBox::warning(
this,
tr("solver error"),
tr("Well '%1' category is not supported by the current solver model.")
.arg(pWellData->getWellName()));
return;
}
}
if(!bHasRateControlledWell) {
QMessageBox::warning(
this,
tr("solver error"),
tr("At least one active production or injection well is required."));
return;
}
// 手动求解可能直接启动, 因此必须在创建求解线程前初始化拟压力转换器.
// pSubWndFit 保存的是当前拟合窗口克隆后的 PVT、拟压力及模型选项数据.
// configPsAbouts() 会据此初始化常规、三区或组分转换器并设置当前模式.
if(pDataManager->getSolverModelType() == SMT_Gas_VariablePvt) {
iAnalRun* pAnalRun = pSubWndFit->getAnalRun();
if(pAnalRun == nullptr
|| !pAnalRun->configPsAbouts(true,
pSubWndFit->getModelOption(),
false,
pSubWndFit->getAllWxPtr())) {
QMessageBox::warning(this, tr("solver error"),
tr("Gas pseudo-pressure data is unavailable or invalid."));
return;
}
}
if(m_pSolverTask != nullptr) {
// 上一次线程已结束但指针还未清空时先交给Qt事件循环安全释放。
m_pSolverTask->deleteLater();
m_pSolverTask = nullptr;
}
// 第一步:捕获本次求解的完整窗口上下文,完成回调不得再读取全局当前窗口。
m_pSolverDataManager = pDataManager;
m_pSolverFitting = pSubWndFit;
m_bSolverCancelRequested = false;
// 第二步:先显示真实进度框,再在下一轮事件中捕获输入并启动线程。
if (m_pProgressDlg == nullptr)
{
m_pProgressDlg = new QProgressDialog(
tr("Preparing input snapshot..."),
tr("Stop"),
0,
0,
getMainWindow());
m_pProgressDlg->setWindowTitle(tr("Solver Progress"));
// 保持 8 月 25 日前的稳定行为。任务和输入快照负责数据安全,进度框不参与
// 全局窗口阻塞,避免首次创建结果窗口时进入 Qt 4.8 模态布局重入。
m_pProgressDlg->setWindowModality(Qt::NonModal);
// 某些系统下,对话框右上角的关闭按钮可能仍存
// 使用 WindowFlags 彻底禁用关闭按钮
m_pProgressDlg->setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
m_pProgressDlg->setMinimumDuration(0);
m_pProgressDlg->setAutoClose(false);
m_pProgressDlg->setAutoReset(false);
connect(m_pProgressDlg, SIGNAL(canceled()),
this, SLOT(onSolverCancelRequested()));
}
m_pProgressDlg->setLabelText(tr("Preparing input snapshot..."));
m_pProgressDlg->setRange(0, 0);
m_pProgressDlg->show();
m_pProgressDlg->raise(); // 提升层级到最前
m_pProgressDlg->activateWindow(); // 激活窗口焦点
m_bSolverStartPending = true;
s_pPendingSolverWindow = this;
QTimer::singleShot(0, this, SLOT(startSolverTask()));
}
void nmSubWndMain::startSolverTask()
{
if(!m_bSolverStartPending) {
return;
}
if(m_pProgressDlg == nullptr || m_pSolverDataManager.isNull() ||
m_pSolverFitting.isNull()) {
m_bSolverStartPending = false;
if(s_pPendingSolverWindow == this) {
s_pPendingSolverWindow = nullptr;
}
if(m_pProgressDlg != nullptr) {
m_pProgressDlg->hide();
}
m_pSolverDataManager = nullptr;
m_pSolverFitting = nullptr;
return;
}
if(waitForPebiGridGeneration()) {
return;
}
// 先创建不自动捕获的任务,使停止按钮在第一批井输入开始前就有明确接收者。
m_pSolverTask = new nmCalculationDllPebiSolverTask(
QString(),
m_pSolverDataManager.data(),
QString(),
nullptr,
true);
s_pRunningSolverTask = m_pSolverTask;
connect(m_pSolverTask, SIGNAL(sig_calculateDone(bool)),
this, SLOT(on_solverTaskFinished(bool)),
Qt::QueuedConnection);
connect(m_pSolverTask, SIGNAL(sigStageChanged(QString,int,int)),
this, SLOT(onSolverStageChanged(QString,int,int)),
Qt::QueuedConnection);
// 给 Windows 一次完成首帧绘制的机会;后续每个事件循环只捕获一口井。
QTimer::singleShot(20, this, SLOT(continueSolverInputCapture()));
}
void nmSubWndMain::continueSolverInputCapture()
{
if(!m_bSolverStartPending || m_pSolverTask == nullptr) {
return;
}
if(waitForPebiGridGeneration()) {
return;
}
bool bFinished = false;
const bool bSucceeded =
m_pSolverTask->captureManualInputStep(1, bFinished);
if(!bSucceeded) {
nmCalculationDllPebiSolverTask* pTask = m_pSolverTask;
const bool bCancelled = pTask->wasCancelled();
const bool bInputChanged =
pTask->hasInputChanged(m_pSolverDataManager.data());
m_pSolverTask = nullptr;
if(pTask == s_pRunningSolverTask) {
s_pRunningSolverTask = nullptr;
}
if(s_pPendingSolverWindow == this) {
s_pPendingSolverWindow = nullptr;
}
m_bSolverStartPending = false;
disconnectPebiGridGenerationWait();
disconnect(pTask, nullptr, this, nullptr);
pTask->deleteLater();
if(bCancelled) {
if(m_pProgressDlg != nullptr) {
m_pProgressDlg->hide();
}
m_pSolverDataManager = nullptr;
m_pSolverFitting = nullptr;
} else {
scheduleSolverResultCompletion(false, -1, -1, bInputChanged);
}
m_bSolverCancelRequested = false;
return;
}
if(bFinished) {
// 临时井对象指针已全部释放,只有完整值快照可以进入后台线程。
m_bSolverStartPending = false;
if(s_pPendingSolverWindow == this) {
s_pPendingSolverWindow = nullptr;
}
disconnectPebiGridGenerationWait();
m_pSolverTask->start();
return;
}
QTimer::singleShot(0, this, SLOT(continueSolverInputCapture()));
}
void nmSubWndMain::onSolverStageChanged(
QString sStage,
int nCurrent,
int nTotal)
{
if(sender() != m_pSolverTask || m_pProgressDlg == nullptr ||
m_bSolverCancelRequested) {
return;
}
m_pProgressDlg->setLabelText(sStage);
if(nTotal <= 0) {
m_pProgressDlg->setRange(0, 0);
} else {
m_pProgressDlg->setRange(0, nTotal);
m_pProgressDlg->setValue(qBound(0, nCurrent, nTotal));
}
}
void nmSubWndMain::onSolverCancelRequested()
{
if(m_bSolverStartPending &&
(m_pSolverTask == nullptr || !m_pSolverTask->isRunning())) {
// 等待建网或分批捕获期间尚未进入工作线程,可以立即撤销并释放值快照。
nmCalculationDllPebiSolverTask* pTask = m_pSolverTask;
m_pSolverTask = nullptr;
m_bSolverStartPending = false;
if(s_pPendingSolverWindow == this) {
s_pPendingSolverWindow = nullptr;
}
disconnectPebiGridGenerationWait();
if(pTask != nullptr) {
pTask->requestCancel();
if(pTask == s_pRunningSolverTask) {
s_pRunningSolverTask = nullptr;
}
disconnect(pTask, nullptr, this, nullptr);
pTask->deleteLater();
}
// QProgressDialog::cancel() 已隐藏窗口;保留对象供下一次求解复用。
m_pSolverDataManager = nullptr;
m_pSolverFitting = nullptr;
m_bSolverCancelRequested = false;
return;
}
if(m_pSolverTask != nullptr) {
m_bSolverCancelRequested = true;
m_pSolverTask->requestCancel();
if(m_pProgressDlg != nullptr) {
// DLL 没有取消导出;若已进入 DLL只能等待其返回后丢弃结果。
m_pProgressDlg->setLabelText(
tr("Stopping; waiting for the current solver call to return..."));
m_pProgressDlg->setRange(0, 0);
QPushButton* pCancelButton =
m_pProgressDlg->findChild<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();
}
}
bool nmSubWndMain::mergeAnaResultToFitting()
{
//QMessageBox::information(this, tr("solver success"), tr("solver succeed!"));
/*if (m_parameterPropertyWindow != nullptr) {
delete m_parameterPropertyWindow;
m_parameterPropertyWindow = nullptr;
}
m_parameterPropertyWindow = new nmWxParameterProperty(this);
m_parameterPropertyWindow->show();*/
ZxMainWindow* pMainWnd1 = const_cast<ZxMainWindow*>(m_pMainWindow);
if(nullptr == pMainWnd1) {
return false;
}
iSubWndFitting* pSubWndFit = !m_pSolverFitting.isNull()
? m_pSolverFitting.data() : nmDataAnalyzeManager::getCurrentFitting();
Q_ASSERT(pSubWndFit != nullptr);
if(pSubWndFit == nullptr) {
return false;
}
nmDataAnalyzeManager* pInstance = !m_pSolverDataManager.isNull()
? m_pSolverDataManager.data()
: nmDataAnalyzeManager::getCurrentInstance();
if(pInstance == nullptr) {
return false;
}
// 捕获的拟合窗口和数据管理器必须仍保持原来的映射关系。
if(nmDataAnalyzeManager::findManagerByFitting(pSubWndFit) != pInstance) {
return false;
}
// 新一轮主动刷新替代尚未执行的旧请求;已经投递的旧槽会检测标记并直接返回。
m_bPendingResultWidgetRefresh = false;
m_pPendingResultDataManager = nullptr;
m_pPendingResultFitting = nullptr;
m_bPendingResultCompletionMessage = false;
m_bPendingResultWindowReady = false;
m_nPendingResultPebiCount = -1;
m_nPendingResultSolveTimeMs = -1;
if(pInstance->getGridType() == NM_Grid_PEBI) {
QSharedPointer<const nmPebiResultSnapshot> pSnapshot =
pInstance->getPebiResultSnapshot();
if(pSnapshot.isNull()) {
return false;
}
QString sWellInstanceId =
pInstance->getCurrentResultWellInstanceId();
if(!pSnapshot->isDisplayWell(sWellInstanceId)) {
pInstance->setCurrentResultWellInstanceId(QString());
sWellInstanceId =
pInstance->getCurrentResultWellInstanceId();
}
// 曲线及下拉框都使用同一 UUID显示列表为空时曲线入口收到空值并被清空。
if(!refreshPebiResultCurves(pSubWndFit,
pInstance,
sWellInstanceId)) {
return false;
}
nmWxNumericalDesign::notifyResultWellSelectorChanged(
pInstance, sWellInstanceId);
}
if(pInstance->getGridType() != NM_Grid_PEBI) {
return true;
}
QString errorMessage;
// getFitSubRstWxOf(..., true) 会同步创建 iWxResult、挂入 MDI 并切换当前页。
// 首次创建时只建立外层载体,待本轮 show/resize/layout 事件结束后再填充内容。
QWidget* p3DWidget = pSubWndFit->getFitSubRstWxOf(
FSRT_Nm3D, false, &errorMessage);
if(p3DWidget == nullptr) {
p3DWidget = pSubWndFit->getFitSubRstWxOf(
FSRT_Nm3D, true, &errorMessage);
if(p3DWidget == nullptr) {
return false;
}
m_pPendingResultDataManager = pInstance;
m_pPendingResultFitting = pSubWndFit;
m_bPendingResultWidgetRefresh = true;
return true;
}
return populateResultWidgets(pSubWndFit, pInstance);
}
bool nmSubWndMain::populateResultWidgets(
iSubWndFitting* pSubWndFit,
nmDataAnalyzeManager* pDataManager)
{
if(pSubWndFit == nullptr || pDataManager == nullptr ||
pDataManager->getGridType() != NM_Grid_PEBI ||
nmDataAnalyzeManager::findManagerByFitting(pSubWndFit) != pDataManager) {
return false;
}
QString sErrorMessage;
QWidget* p3DWidget = pSubWndFit->getFitSubRstWxOf(
FSRT_Nm3D, true, &sErrorMessage);
if(p3DWidget == nullptr) {
return false;
}
nmWxPostprocessingAnimationWidget* pVtkWidget =
p3DWidget->findChild<nmWxPostprocessingAnimationWidget*>();
if(pVtkWidget != nullptr) {
// 复用既有渲染窗口和 VTK 管道,只切换本轮已整体提交的数据。
pVtkWidget->refreshResult();
} else {
pVtkWidget = new nmWxPostprocessingAnimationWidget(NULL, pDataManager);
QLayout* p3DLayout = p3DWidget->layout();
if(p3DLayout == nullptr) {
p3DLayout = new QVBoxLayout(p3DWidget);
} else {
QLayoutItem* pItem = nullptr;
while((pItem = p3DLayout->takeAt(0)) != nullptr) {
if(pItem->widget() != nullptr) {
delete pItem->widget();
}
delete pItem;
}
}
p3DLayout->addWidget(pVtkWidget);
}
// 结果参数界面
QWidget* pResultPage = pSubWndFit->getFitSubRstWxOf(
FSRT_NmRst, true, &sErrorMessage);
if(pResultPage == nullptr) {
return false;
}
nmWxParaPropertyPebi* pResultWidget =
pResultPage->findChild<nmWxParaPropertyPebi*>();
if(pResultWidget == nullptr) {
// 与左侧共用属性面板类,结果区使用独立实例和只读参数列表。
pResultWidget = new nmWxParaPropertyPebi;
pResultWidget->initUI();
pResultWidget->setDataManager(pDataManager);
// 保持 8 月 24 日版本的稳定顺序:离屏完成参数构建后再加入可见布局。
pResultWidget->rebuildResultParas();
QLayout* pResultLayout = pResultPage->layout();
if(pResultLayout == nullptr) {
pResultLayout = new QVBoxLayout(pResultPage);
} else {
QLayoutItem* pItem = nullptr;
while((pItem = pResultLayout->takeAt(0)) != nullptr) {
if(pItem->widget() != nullptr) {
delete pItem->widget();
}
delete pItem;
}
}
pResultLayout->addWidget(pResultWidget);
return true;
}
pResultWidget->setDataManager(pDataManager);
pResultWidget->rebuildResultParas();
return true;
}
void nmSubWndMain::schedulePendingResultWidgets()
{
if(m_bPendingResultWidgetRefresh) {
QTimer::singleShot(0, this, SLOT(populatePendingResultWidgets()));
}
}
void nmSubWndMain::populatePendingResultWidgets()
{
if(!m_bPendingResultWidgetRefresh) {
return;
}
iSubWndFitting* pSubWndFit = m_pPendingResultFitting.data();
nmDataAnalyzeManager* pDataManager = m_pPendingResultDataManager.data();
m_bPendingResultWidgetRefresh = false;
m_pPendingResultDataManager = nullptr;
m_pPendingResultFitting = nullptr;
const bool bResultWindowReady =
populateResultWidgets(pSubWndFit, pDataManager);
if(m_bPendingResultCompletionMessage) {
m_bPendingResultWindowReady = bResultWindowReady;
QTimer::singleShot(
0,
this,
SLOT(showPendingSolverResultCompletion()));
}
}
void nmSubWndMain::showPendingSolverResultCompletion()
{
if(!m_bPendingResultCompletionMessage) {
return;
}
const bool bResultWindowReady = m_bPendingResultWindowReady;
const int nPebiCount = m_nPendingResultPebiCount;
const int nSolveTimeMs = m_nPendingResultSolveTimeMs;
m_bPendingResultCompletionMessage = false;
m_bPendingResultWindowReady = false;
m_nPendingResultPebiCount = -1;
m_nPendingResultSolveTimeMs = -1;
showSolverResultCompletion(bResultWindowReady,
nPebiCount,
nSolveTimeMs);
}
bool nmSubWndMain::inithalfLog(QVector<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();
//}
}
}
bool nmSubWndMain::on_calculationFinished(NM_Calculation_Result result)
{
if(result == NM_Calculation_Result_Success) {
return this->mergeAnaResultToFitting();
} else {
// 如果失败,进度条其实没必要拉满了,直接提示错误
QMessageBox::warning(this, tr("solver error"), tr("solver failed!"));
return false;
}
}
void nmSubWndMain::scheduleSolverResultCompletion(
bool bSucceeded,
int nPebiCount,
int nSolveTimeMs,
bool bInputChanged)
{
s_pPendingSolverWindow = this;
m_bSolverResultCompletionPending = true;
m_bPendingSolverSucceeded = bSucceeded;
m_bPendingSolverInputChanged = bInputChanged;
m_nPendingSolverPebiCount = nPebiCount;
m_nPendingSolverTimeMs = nSolveTimeMs;
// 当前完成信号栈只登记后续工作;隐藏和结果刷新各占一轮独立事件。
QTimer::singleShot(0, this, SLOT(dismissPendingSolverProgress()));
}
void nmSubWndMain::dismissPendingSolverProgress()
{
if(m_pProgressDlg != nullptr) {
m_pProgressDlg->hide();
}
QTimer::singleShot(0, this, SLOT(completePendingSolverResult()));
}
void nmSubWndMain::completePendingSolverResult()
{
if(!m_bSolverResultCompletionPending) {
return;
}
const bool bSucceeded = m_bPendingSolverSucceeded;
const bool bInputChanged = m_bPendingSolverInputChanged;
const int nPebiCount = m_nPendingSolverPebiCount;
const int nSolveTimeMs = m_nPendingSolverTimeMs;
m_bSolverResultCompletionPending = false;
m_bPendingSolverSucceeded = false;
m_bPendingSolverInputChanged = false;
m_nPendingSolverPebiCount = -1;
m_nPendingSolverTimeMs = -1;
bool bResultWindowReady = false;
if(bInputChanged) {
QMessageBox::warning(
this,
tr("Solver result discarded"),
tr("The input changed while the solver was preparing or running. "
"The outdated result was discarded."));
} else {
bResultWindowReady = on_calculationFinished(bSucceeded
? NM_Calculation_Result_Success
: NM_Calculation_Result_Fail);
}
// mergeAnaResultToFitting() 已使用完启动时捕获的上下文。
m_pSolverDataManager = nullptr;
m_pSolverFitting = nullptr;
m_bSolverCancelRequested = false;
if(bSucceeded && bResultWindowReady &&
m_bPendingResultWidgetRefresh) {
// 首次结果页尚未填充,最终提示必须等页面真实创建结果返回后再显示。
m_bPendingResultCompletionMessage = true;
m_nPendingResultPebiCount = nPebiCount;
m_nPendingResultSolveTimeMs = nSolveTimeMs;
} else if(bSucceeded) {
showSolverResultCompletion(bResultWindowReady,
nPebiCount,
nSolveTimeMs);
}
// 首次结果载体创建后再跨一个事件循环填充,避免 Qt 4.8 布局重入。
schedulePendingResultWidgets();
if(s_pPendingSolverWindow == this) {
s_pPendingSolverWindow = nullptr;
}
}
void nmSubWndMain::showSolverResultCompletion(
bool bResultWindowReady,
int nPebiCount,
int nSolveTimeMs)
{
if(bResultWindowReady && nPebiCount >= 0 && nSolveTimeMs >= 0) {
QMessageBox::information(this, tr("Solver completed"),
tr("Grid count: %1\nSolve time: %2 ms")
.arg(nPebiCount)
.arg(nSolveTimeMs));
} else if(!bResultWindowReady) {
QMessageBox::warning(
this,
tr("Result display error"),
tr("Solver completed and the results were committed, but the "
"result window could not be opened."));
}
}
void nmSubWndMain::on_solverTaskFinished(bool isSuccessed)
{
// 第一步:只接受当前窗口实际启动的任务,忽略已经失效的旧回调。
nmCalculationDllPebiSolverTask* pTask = qobject_cast<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();
bool bInputChanged = false;
// 后台线程只生成局部结果快照。回到主线程后先验证所属 DataManager 和
// 输入版本,再整体替换旧成果;输入变化与真正的求解失败分别提示。
if(isSuccessed && !bCancelled) {
if(m_pProgressDlg != nullptr) {
m_pProgressDlg->setLabelText(tr("Committing results..."));
m_pProgressDlg->setRange(0, 0);
}
bInputChanged =
pTask->hasInputChanged(m_pSolverDataManager.data());
isSuccessed = pTask->commitResult(m_pSolverDataManager.data());
bInputChanged = !isSuccessed && bInputChanged;
} else if(bCancelled) {
isSuccessed = false;
}
m_pSolverTask = nullptr;
if(pTask == s_pRunningSolverTask) {
s_pRunningSolverTask = nullptr;
}
// 正常完成路径由主线程延迟释放任务;析构路径则会等待并直接释放。
pTask->deleteLater();
// 第二步:停止不属于失败,也不允许触发结果提交或覆盖旧成果。
if(bCancelled) {
if(m_pProgressDlg != nullptr) {
m_pProgressDlg->hide();
}
m_pSolverDataManager = nullptr;
m_pSolverFitting = nullptr;
m_bSolverCancelRequested = false;
} else {
scheduleSolverResultCompletion(isSuccessed,
nPebiCount,
nSolveTimeMs,
bInputChanged);
}
}
void nmSubWndMain::deleteOneObj()
{
this->triggerToolBarAction(NMOT_Delete);
}
void nmSubWndMain::setLocked()
{
if(m_lockState) { // 锁定
m_lockState = false;
int objCount = this->m_pWxPlot->m_pPlot->getObjCount();
for(int i = 0; i < objCount; i++) {
ZxObjBase* obj = this->m_pWxPlot->m_pPlot->getObjByIndex(i);
if(!_isSame("nObjPointWell", obj->getTagName())) {
obj->setReadOnly(false);
obj->setLockPos(false);
}
}
QStringList nIDs;
nIDs.append("5125");
nIDs.append("5124");
nIDs.append("5123");
nIDs.append("5114");
nIDs.append("5115");
nIDs.append("5116");
nIDs.append("5117");
nIDs.append("5118");
nIDs.append("5119");
nIDs.append("5102");
nIDs.append("5120");
nIDs.append("5103");
nIDs.append("5104");
nIDs.append("5105");
nIDs.append("5106");
nIDs.append("5107");
// nIDs.append("5108"); // 新建井菜单暂时停用
nIDs.append("5109");
nIDs.append("5110");
nIDs.append("5111");
nIDs.append("5121");
nIDs.append("5112");
nIDs.append("5113");
nIDs.append("5122");
nIDs.append("9002");
nIDs.append("9003");
nIDs.append("9101");
nIDs.append("9102");
nIDs.append("9103");
nIDs.append("9104");
nIDs.append("3104");
emit sigFreshRnStates(nIDs);
/*
if (m_pMainWindow != NULL) {
//m_pMainWindow->slotFreshRnStates(nIDs);
}
*/
//m_pMainWindow->getMainRibbon
} else {
m_lockState = true;
int objCount = this->m_pWxPlot->m_pPlot->getObjCount();
for(int i = 0; i < objCount; i++) {
ZxObjBase* obj = this->m_pWxPlot->m_pPlot->getObjByIndex(i);
if(!_isSame("nObjPointWell", obj->getTagName())) {
obj->setReadOnly(true);
obj->setLockPos(true);
}
}
QStringList nIDs;
nIDs.append("5125");
nIDs.append("5124");
nIDs.append("5123");
nIDs.append("5114");
nIDs.append("5115");
nIDs.append("5116");
nIDs.append("5117");
nIDs.append("5118");
nIDs.append("5119");
nIDs.append("5102");
nIDs.append("5120");
nIDs.append("5103");
nIDs.append("5104");
nIDs.append("5105");
nIDs.append("5106");
nIDs.append("5107");
// nIDs.append("5108"); // 新建井菜单暂时停用
nIDs.append("5109");
nIDs.append("5110");
nIDs.append("5111");
nIDs.append("5121");
nIDs.append("5112");
nIDs.append("5113");
nIDs.append("5122");
//nIDs.append("9002");
//nIDs.append("9003");
nIDs.append("9101");
nIDs.append("9102");
nIDs.append("9103");
nIDs.append("9104");
nIDs.append("3104");
emit sigFreshRnStates(nIDs);
/*
if (m_pMainWindow != NULL) {
m_pMainWindow->slotFreshRnStates(nIDs);
}
*/
/*
emit sigFreshRnStates(nIDs);
if (m_pMainWindow != NULL) {
m_pMainWindow->slotFreshRnStates(nIDs);
}
bool b;
QString sName = "NmUnlocked";
int nID = 5119;
bool bLicensed;
nmSubWndMain::checkCmdEnable(b, sName, nID, bLicensed);
nID = 5125;
sName = "NmPrint";
nmSubWndMain::checkCmdEnable(b, sName, nID, bLicensed);
*/
}
}
nmGuiPlot * nmSubWndMain::getWxPlot() const
{
return m_pWxPlot;
}
bool nmSubWndMain::refreshPebiResultCurves(
iSubWndFitting* pSubWndFit,
nmDataAnalyzeManager* pDataManager,
const QString& sWellInstanceId)
{
if(pSubWndFit == nullptr || pDataManager == nullptr ||
pDataManager->getGridType() != NM_Grid_PEBI) {
return false;
}
QSharedPointer<const nmPebiResultSnapshot> pSnapshot =
pDataManager->getPebiResultSnapshot();
if(pSnapshot.isNull() ||
(!sWellInstanceId.isEmpty() &&
!pSnapshot->isDisplayWell(sWellInstanceId))) {
return false;
}
const nmPebiResultWellSnapshot* pWell = sWellInstanceId.isEmpty()
? NULL : pSnapshot->findWell(sWellInstanceId);
if(!sWellInstanceId.isEmpty() && pWell == NULL) {
return false;
}
if(!pDataManager->setCurrentResultWellInstanceId(sWellInstanceId)) {
return false;
}
nmPebiResultWellCurves oEmptyCurves;
const nmPebiResultWellCurves& oCurves = pWell != NULL
? pWell->m_oCurves : oEmptyCurves;
QString sErrorMessage;
// 旧绘图辅助函数会裁掉双对数末点,必须使用隐式共享局部副本,
// 触发写时复制后也不能改动发布后的快照容器。
QVector<QVector<double> > vecHistorySemiLog =
oCurves.m_vecHistorySemiLog;
QVector<QVector<double> > vecResultSemiLog =
oCurves.m_vecResultSemiLog;
QVector<QVector<double> > vecHistoryLogLog =
oCurves.m_vecHistoryLogLog;
QVector<QVector<double> > vecResultLogLog =
oCurves.m_vecResultLogLog;
QVector<QVector<double> > vecHistoryPressure =
oCurves.m_vecHistoryPressure;
QVector<QVector<double> > vecResultPressureData =
oCurves.m_vecResultPressure;
// 每个窗口先发布求解时冻结的历史曲线,再叠加同一快照中的计算曲线。
QVector<iCurveDesc> vecHistoryHalfLog;
this->inithalfLog(vecHistorySemiLog,
vecHistoryHalfLog, true);
pSubWndFit->adjustFitSubPlotBy(
FSRT_SemiLog, vecHistoryHalfLog, true, &sErrorMessage);
QVector<iCurveDesc> vecHalfLog;
this->inithalfLog(vecResultSemiLog, vecHalfLog, false);
pSubWndFit->adjustFitSubPlotBy(
FSRT_SemiLog, vecHalfLog, true, &sErrorMessage);
QVector<iCurveDesc> vecHistoryLog;
this->initloglog(vecHistoryLogLog,
vecHistoryLog, true);
pSubWndFit->adjustFitSubPlotBy(
FSRT_DoubleLog, vecHistoryLog, true, &sErrorMessage);
QVector<iCurveDesc> vecLogLog;
this->initloglog(vecResultLogLog, vecLogLog, false);
pSubWndFit->adjustFitSubPlotBy(
FSRT_DoubleLog, vecLogLog, true, &sErrorMessage);
QVector<iCurveDesc> vecHistoryPre;
this->initPreHistory(vecHistoryPressure,
vecHistoryPre, true);
pSubWndFit->adjustFitSubPlotBy(
FSRT_Hist, vecHistoryPre, true, &sErrorMessage);
QVector<iCurveDesc> vecResultPressure;
this->initPreHistory(vecResultPressureData,
vecResultPressure, false);
pSubWndFit->adjustFitSubPlotBy(
FSRT_Hist, vecResultPressure, true, &sErrorMessage);
return true;
}
void nmSubWndMain::onWellSelected(const QString& sWellInstanceId)
{
nmDataAnalyzeManager* pInstance =
nmDataAnalyzeManager::getCurrentInstance();
iSubWndFitting* pSubWndFit =
nmDataAnalyzeManager::getCurrentFitting();
if(pInstance == nullptr || pSubWndFit == nullptr) {
return;
}
// 删除的实时井仍可按快照 UUID 切换;本路径不得查询或重算实时井数据。
refreshPebiResultCurves(
pSubWndFit, pInstance, sWellInstanceId);
}
#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);
}