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

2899 lines
89 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 "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);
// 完整清理、Binder切换和快照刷新均由Map重建入口统一完成。
m_pWxPlot->setPlotsByDataManger(pDataManager);
}