|
|
#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 "ZxObjCurveBar.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 "nmPebiResultPageInstaller.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 "nmWxGeoRefDlg.h"
|
|
|
|
|
|
#include "iSubWndFitting.h"
|
|
|
#include "iGuiPlotPF.h"
|
|
|
#include "iGuiPlotTableHelper.h"
|
|
|
#include "iGuiPlot.h"
|
|
|
#include "ZxObjCurveFlow.h"
|
|
|
#include "iAnalRun.h"
|
|
|
|
|
|
#include <QVector>
|
|
|
#include <QSet>
|
|
|
#include <QHash>
|
|
|
#include <QPushButton>
|
|
|
#include <QLabel>
|
|
|
#include <cmath>
|
|
|
#include <QMdiSubWindow>
|
|
|
#include "ZxMdiArea.h"
|
|
|
|
|
|
#include "nmSingalCenter.h"
|
|
|
|
|
|
#include "nmWxParaPropertyPebi.h"
|
|
|
#include "nmWxNumericalDesign.h"
|
|
|
#include "nmWxSensitive.h"
|
|
|
#include "nmWxForecast.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>
|
|
|
#include <QtCore/qnumeric.h>
|
|
|
|
|
|
namespace
|
|
|
{
|
|
|
// 保留原来求解器单例的防重入语义:全局同一时间只允许一个DLL求解任务运行。
|
|
|
// QPointer会在QObject销毁后自动变空,避免保留已经释放的线程指针。
|
|
|
QPointer<nmCalculationDllPebiSolverTask> s_pRunningSolverTask;
|
|
|
QPointer<nmSubWndMain> s_pPendingSolverWindow;
|
|
|
|
|
|
void clearResultCurveDataTables(iGuiPlot* plot)
|
|
|
{
|
|
|
// 框架弹出的表格是独立顶层窗口;刷新前清除本图的旧表,仍由框架管理其生命周期。
|
|
|
foreach(QWidget* window, QApplication::topLevelWidgets()) {
|
|
|
iGuiPlotTableHelper* table = qobject_cast<iGuiPlotTableHelper*>(window);
|
|
|
if(!table) continue;
|
|
|
for(int i = 0; i < plot->m_pPlot->getObjCount(); ++i) {
|
|
|
ZxObjCurve* curve = qobject_cast<ZxObjCurve*>(plot->m_pPlot->getObjByIndex(i));
|
|
|
if(curve && table->getMapTableViews()->contains(curve)) {
|
|
|
table->resetTableHelpers();
|
|
|
table->hide();
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QSet<QString> hiddenSensitiveCurves(iGuiPlot* plot)
|
|
|
{
|
|
|
// 刷新同一批次或重新打开窗口时保留本图的显示选择。
|
|
|
QSet<QString> hidden;
|
|
|
for(int i = 0; i < plot->m_pPlot->getObjCount(); ++i) {
|
|
|
ZxObjCurve* curve = qobject_cast<ZxObjCurve*>(plot->m_pPlot->getObjByIndex(i));
|
|
|
if(curve && !curve->isVisible()) hidden.insert(curve->getName());
|
|
|
}
|
|
|
return hidden;
|
|
|
}
|
|
|
|
|
|
class nmSensitivityHistogramBars : public ZxObjCurveBar
|
|
|
{
|
|
|
public:
|
|
|
explicit nmSensitivityHistogramBars(double binWidth) : m_binWidth(binWidth) {}
|
|
|
|
|
|
protected:
|
|
|
void drawSubBars(QPainter* painter, QVector<QPointF>& points) override
|
|
|
{
|
|
|
// 框架柱宽使用绘图坐标,按分箱宽度换算,缩放和窗口调整后仍对应同一区间。
|
|
|
const ZxSubAxisX* axis = getAxisX();
|
|
|
const double left = axis->getRangeMin();
|
|
|
m_dBarWidth = 0.95 * qAbs(axis->getPosForValue(left + m_binWidth) - axis->getPosForValue(left));
|
|
|
ZxObjCurveBar::drawSubBars(painter, points);
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
double m_binWidth;
|
|
|
};
|
|
|
|
|
|
// 历史窗口仍是单流量曲线结构,因此显示与双对数、半对数后处理一致的制度参考相。
|
|
|
const QVector<double>* resultReferenceRatesOf(
|
|
|
const nmPebiResultWellSnapshot* pWell)
|
|
|
{
|
|
|
if(pWell == NULL) {
|
|
|
return NULL;
|
|
|
}
|
|
|
switch(pWell->m_eFlowSchedulePhase) {
|
|
|
case PHASE_Oil:
|
|
|
return pWell->m_bUseOilRate ? &pWell->m_vecOilRates : NULL;
|
|
|
case PHASE_Gas:
|
|
|
return pWell->m_bUseGasRate ? &pWell->m_vecGasRates : NULL;
|
|
|
case PHASE_Water:
|
|
|
return pWell->m_bUseWaterRate ? &pWell->m_vecWaterRates : NULL;
|
|
|
default:
|
|
|
return NULL;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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_nPendingResultSolveTimeMs = -1;
|
|
|
m_bSolverResultCompletionPending = false;
|
|
|
m_bPendingSolverSucceeded = false;
|
|
|
m_bPendingSolverInputChanged = false;
|
|
|
m_nPendingSolverTimeMs = -1;
|
|
|
m_bSolverStartPending = false;
|
|
|
m_bSolverCancelRequested = false;
|
|
|
m_pResultRefreshDataManager = nullptr;
|
|
|
m_pResultRefreshFitting = nullptr;
|
|
|
m_sObservedPebiResultSnapshotId.clear();
|
|
|
m_bRefreshingPebiResultSnapshot = 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_pAuxiliarySolverTask.isNull()) {
|
|
|
disconnect(m_pAuxiliarySolverTask.data(), nullptr, this, nullptr);
|
|
|
m_pAuxiliarySolverTask->requestCancel();
|
|
|
m_pAuxiliarySolverTask->wait();
|
|
|
if(s_pRunningSolverTask == m_pAuxiliarySolverTask) s_pRunningSolverTask = nullptr;
|
|
|
m_pAuxiliarySolverTask = 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::bindPebiResultRefreshContext(
|
|
|
nmDataAnalyzeManager* pDataManager,
|
|
|
iSubWndFitting* pSubWndFitting)
|
|
|
{
|
|
|
// 自动拟合完成时始终刷新创建该数值面板的分析窗口,避免页签切换后取到其他分析。
|
|
|
m_pResultRefreshDataManager = pDataManager;
|
|
|
m_pResultRefreshFitting = pSubWndFitting;
|
|
|
QSharedPointer<const nmPebiResultSnapshot> pSnapshot =
|
|
|
pDataManager != nullptr
|
|
|
? pDataManager->getPebiResultSnapshot()
|
|
|
: QSharedPointer<const nmPebiResultSnapshot>();
|
|
|
m_sObservedPebiResultSnapshotId = pSnapshot.isNull()
|
|
|
? QString() : pSnapshot->getSnapshotId();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onPebiResultSnapshotChanged()
|
|
|
{
|
|
|
nmDataAnalyzeManager* pDataManager = m_pResultRefreshDataManager.data();
|
|
|
iSubWndFitting* pSubWndFitting = m_pResultRefreshFitting.data();
|
|
|
if(pDataManager == nullptr || pSubWndFitting == nullptr ||
|
|
|
sender() != pDataManager || m_bRefreshingPebiResultSnapshot) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
QSharedPointer<const nmPebiResultSnapshot> pSnapshot =
|
|
|
pDataManager->getPebiResultSnapshot();
|
|
|
if(pSnapshot.isNull()) {
|
|
|
m_sObservedPebiResultSnapshotId.clear();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const QString sSnapshotId = pSnapshot->getSnapshotId();
|
|
|
if(sSnapshotId == m_sObservedPebiResultSnapshotId) {
|
|
|
// 切换结果井时关闭上一口井的敏感性窗口及附属表格,保留批次数据供再次查看。
|
|
|
ZxMdiArea* area = pSubWndFitting->m_pMdiArea;
|
|
|
if(area) {
|
|
|
iGuiPlot* loglog = pSubWndFitting->getPlotByTag(s_Tag_ChartSensitivity);
|
|
|
bool closed = false;
|
|
|
foreach(QMdiSubWindow* window, area->subWindowList()) {
|
|
|
if(window->objectName() != "nmSensitivityHistory" &&
|
|
|
window->objectName() != "nmSensitivityHistogram" &&
|
|
|
!(loglog && window->isAncestorOf(loglog))) continue;
|
|
|
foreach(iGuiPlot* plot, window->findChildren<iGuiPlot*>())
|
|
|
clearResultCurveDataTables(plot);
|
|
|
foreach(QDialog* dialog, window->findChildren<QDialog*>()) {
|
|
|
if(dialog->isWindow()) dialog->close();
|
|
|
}
|
|
|
window->close();
|
|
|
closed = true;
|
|
|
}
|
|
|
if(closed && area->isVisible()) area->relayoutSubWnds();
|
|
|
}
|
|
|
// 同一快照内切换结果井仍由 onWellSelected() 处理,不重复刷新全部结果页面。
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 手工 Generate 有自己的完成和提示流程;这里只接管自动拟合提交的新快照。
|
|
|
if(m_pSolverTask != nullptr || m_bSolverStartPending ||
|
|
|
m_bSolverResultCompletionPending) {
|
|
|
m_sObservedPebiResultSnapshotId = sSnapshotId;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_bRefreshingPebiResultSnapshot = true;
|
|
|
QPointer<nmDataAnalyzeManager> pSavedSolverDataManager =
|
|
|
m_pSolverDataManager;
|
|
|
QPointer<iSubWndFitting> pSavedSolverFitting = m_pSolverFitting;
|
|
|
m_pSolverDataManager = pDataManager;
|
|
|
m_pSolverFitting = pSubWndFitting;
|
|
|
const bool bRefreshed = mergeAnaResultToFitting();
|
|
|
schedulePendingResultWidgets();
|
|
|
m_pSolverDataManager = pSavedSolverDataManager;
|
|
|
m_pSolverFitting = pSavedSolverFitting;
|
|
|
m_bRefreshingPebiResultSnapshot = false;
|
|
|
if(bRefreshed) {
|
|
|
m_sObservedPebiResultSnapshotId = sSnapshotId;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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(pWellPlot == nullptr) {
|
|
|
continue;
|
|
|
}
|
|
|
// 选择井列表只能移除普通数值井;固定主井始终保留在本次分析中。
|
|
|
if(m_pWxPlot->graphicBinder() &&
|
|
|
m_pWxPlot->graphicBinder()->isPrimaryWellGraphic(pWellPlot))
|
|
|
continue;
|
|
|
|
|
|
// 数据删除失败时保留图元,避免画布和DataManager出现半套状态。
|
|
|
if(!pWellPlot->removeBoundData()) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
// 按准确对象删除,不能按井名删除,否则同名井可能移除错误图元。
|
|
|
const int nObjCount = m_pWxPlot->m_pPlot->getObjCount();
|
|
|
for(int nObjIndex = 0; nObjIndex < nObjCount; ++nObjIndex) {
|
|
|
if(m_pWxPlot->m_pPlot->getObjByIndex(nObjIndex) == pWellPlot) {
|
|
|
m_pWxPlot->m_pPlot->removeObjByIndex(nObjIndex);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 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;
|
|
|
}
|
|
|
|
|
|
// 新一次启动前清空旧上下文;后续所有检查通过后再成对写入。
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
// 第一步:求解时间轴必须由至少一口主动井的产注制度驱动。
|
|
|
// 当前数值接入只开放油、气、水单相和油水两相;其余相态保留数据,
|
|
|
// 等求解器支持后再开放,不在这里改写或丢弃框架流量。
|
|
|
if(!nmIsSupportedPebiSolverModel(
|
|
|
pDataManager->getSolverModelType())) {
|
|
|
QMessageBox::warning(
|
|
|
this,
|
|
|
tr("solver error"),
|
|
|
tr("The solver currently supports oil, gas, and water single-phase models and the oil-water two-phase model only."));
|
|
|
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(!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;
|
|
|
|
|
|
// 第二步:先显示真实进度框,再在下一轮事件中捕获输入并启动线程。
|
|
|
showSolverProgress();
|
|
|
m_bSolverStartPending = true;
|
|
|
s_pPendingSolverWindow = this;
|
|
|
QTimer::singleShot(0, this, SLOT(startSolverTask()));
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::showSolverProgress()
|
|
|
{
|
|
|
// Qt 4.8 下保留已经隐藏的实例,普通求解与批量任务都从这里初始化。
|
|
|
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->reset();
|
|
|
QPushButton* pCancelButton = m_pProgressDlg->findChild<QPushButton*>();
|
|
|
if(pCancelButton != nullptr) pCancelButton->setEnabled(true);
|
|
|
m_pProgressDlg->setLabelText(tr("Preparing input snapshot..."));
|
|
|
m_pProgressDlg->setRange(0, 0);
|
|
|
m_pProgressDlg->show();
|
|
|
|
|
|
m_pProgressDlg->raise(); // 提升层级到最前
|
|
|
m_pProgressDlg->activateWindow(); // 激活窗口焦点
|
|
|
|
|
|
}
|
|
|
|
|
|
bool nmSubWndMain::prepareAuxiliarySolverTask(
|
|
|
QWidget* dialog, nmCalculationDllPebiSolverTask* task)
|
|
|
{
|
|
|
// 与普通求解共用防重入状态,避免两个任务同时写同一进度框。
|
|
|
if(!s_pPendingSolverWindow.isNull() || !s_pRunningSolverTask.isNull() ||
|
|
|
m_bSolverResultCompletionPending) {
|
|
|
QMessageBox::information(dialog, tr("solver error"), tr("task is running!"));
|
|
|
return false;
|
|
|
}
|
|
|
m_pAuxiliarySolverTask = task;
|
|
|
s_pRunningSolverTask = task;
|
|
|
m_bSolverCancelRequested = false;
|
|
|
// 先隐藏共享进度框,再让参数窗口的完成槽读取结果。
|
|
|
connect(task, SIGNAL(finished()), this, SLOT(onAuxiliarySolverTaskFinished()), Qt::QueuedConnection);
|
|
|
connect(task, SIGNAL(destroyed(QObject*)), this, SLOT(onAuxiliarySolverTaskDestroyed()));
|
|
|
showSolverProgress();
|
|
|
m_pProgressDlg->setLabelText(nmCalculationDllPebiSolverTask::tr("Solving model..."));
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onForecastTaskReady(
|
|
|
nmWxForecast* dialog, nmCalculationDllPebiSolverTask* task)
|
|
|
{
|
|
|
// 预测沿用共享进度框和停止路径,计算结果不进入正式成果提交。
|
|
|
if(!prepareAuxiliarySolverTask(dialog, task)) return;
|
|
|
connect(dialog, SIGNAL(sigForecastCancelRequested()),
|
|
|
this, SLOT(onSolverCancelRequested()), Qt::UniqueConnection);
|
|
|
dialog->startForecastCalculation();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onSensitivityTaskReady(
|
|
|
nmWxSensitive* dialog, nmCalculationDllPebiSolverTask* task)
|
|
|
{
|
|
|
// 整个批次保持同一个滚动进度,不订阅每组求解的阶段刷新信号。
|
|
|
if(!prepareAuxiliarySolverTask(dialog, task)) return;
|
|
|
connect(dialog, SIGNAL(sigSensitivityCancelRequested()),
|
|
|
this, SLOT(onSolverCancelRequested()), Qt::UniqueConnection);
|
|
|
// 坐标标题与单位跟随求解时的双对数窗口,后来切换模型不改变已有批次。
|
|
|
QSharedPointer<nmDataSensitive::BatchResult> result = task->getSensitivityBatchResult();
|
|
|
if(!m_pResultRefreshFitting.isNull()) {
|
|
|
iGuiPlot* source = qobject_cast<iGuiPlot*>(
|
|
|
m_pResultRefreshFitting->getFitSubRstWxOf(FSRT_DoubleLog, false));
|
|
|
if(source && source->getAxisOf(0) && source->getAxisOf(1)) {
|
|
|
result->m_sAxisXName = source->getAxisOf(0)->getName();
|
|
|
result->m_sAxisXUnit = source->getAxisOf(0)->getUnit();
|
|
|
result->m_sAxisYName = source->getAxisOf(1)->getName();
|
|
|
result->m_sAxisYUnit = source->getAxisOf(1)->getUnit();
|
|
|
}
|
|
|
}
|
|
|
dialog->startSensitivityCalculation();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onAuxiliarySolverTaskFinished()
|
|
|
{
|
|
|
// 完整退出线程后再释放进度框的占用,不进入普通求解的成果提交路径。
|
|
|
if(m_pAuxiliarySolverTask.isNull() || sender() != m_pAuxiliarySolverTask.data()) return;
|
|
|
m_pAuxiliarySolverTask->wait();
|
|
|
if(s_pRunningSolverTask == m_pAuxiliarySolverTask) s_pRunningSolverTask = nullptr;
|
|
|
disconnect(m_pAuxiliarySolverTask.data(), nullptr, this, nullptr);
|
|
|
m_pAuxiliarySolverTask = nullptr;
|
|
|
m_bSolverCancelRequested = false;
|
|
|
m_pProgressDlg->hide();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onAuxiliarySolverTaskDestroyed()
|
|
|
{
|
|
|
// 参数面板提前销毁时,其析构函数已等待任务结束,解除共享进度框占用。
|
|
|
m_pAuxiliarySolverTask = nullptr;
|
|
|
m_bSolverCancelRequested = false;
|
|
|
m_pProgressDlg->hide();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onShowForecastResult(const QString& wellName, const QVector<double>& times,
|
|
|
const QVector<double>& pressures, const QVector<double>& durations,
|
|
|
const QVector<double>& rates)
|
|
|
{
|
|
|
// 使用面板绑定的分析页面,切换活动分析不会改变预测结果的归属。
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data();
|
|
|
if(!fitting || !fitting->m_pMdiArea || !manager ||
|
|
|
nmDataAnalyzeManager::findManagerByFitting(fitting) != manager ||
|
|
|
!manager->isPebiResultSnapshotCurrent()) return;
|
|
|
ZxMdiArea* area = fitting->m_pMdiArea;
|
|
|
QMdiSubWindow* window = area->findChild<QMdiSubWindow*>("nmForecastResult");
|
|
|
const bool created = window == nullptr;
|
|
|
if(created) {
|
|
|
// 结果页面由 MDI 承载,关闭预测输入对话框不销毁结果。
|
|
|
QWidget* page = new QWidget;
|
|
|
QVBoxLayout* layout = new QVBoxLayout(page);
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
QSplitter* charts = new QSplitter(Qt::Vertical, page);
|
|
|
charts->setObjectName("nmForecastCharts");
|
|
|
charts->setChildrenCollapsible(false);
|
|
|
layout->addWidget(charts);
|
|
|
// 布局完成后统一适配画布,避免小窗口下两个绘图区缩放比例不同。
|
|
|
charts->installEventFilter(this);
|
|
|
connect(charts, SIGNAL(splitterMoved(int,int)), this, SLOT(fitForecastViews()), Qt::QueuedConnection);
|
|
|
for(int i = 0; i < 2; ++i) {
|
|
|
iGuiPlot* plot = new iGuiPlot(true, charts);
|
|
|
plot->setObjectName(i == 0 ? "nmForecastRate" : "nmForecastPressure");
|
|
|
plot->initUI(i == 0 ? tr("Production rate") : tr("Bottomhole pressure"));
|
|
|
connect(plot, SIGNAL(sigDoubleClicked(ZxPlot*,ZxObjBase*,const QPointF&,bool&)),
|
|
|
this, SLOT(onReadonlyCurveDoubleClicked(ZxPlot*,ZxObjBase*,const QPointF&,bool&)));
|
|
|
plot->m_pPlotView->setSimpleMode(true);
|
|
|
plot->setDropEnabled(false);
|
|
|
plot->setAxisX(tr("Time"), "hr");
|
|
|
plot->setAxisY(i == 0 ? tr("Flow") : tr("Pressure"), i == 0 ? "m^3/D" : "MPa");
|
|
|
plot->setAxisY(i == 0 ? tr("Cumulative production") : QString(), i == 0 ? "m^3" : "", true);
|
|
|
// 两侧纵轴适当加宽,留出数字与名称的间距,上下图采用相同宽度保持对齐。
|
|
|
plot->getAxisOf(1)->setSpan(16.0);
|
|
|
plot->getAxisOf(2)->setSpan(16.0);
|
|
|
for(int axis = 0; axis < 3; ++axis) plot->setAxisXYLog(false, axis);
|
|
|
plot->getAxisOf(2)->setVisible(true);
|
|
|
// 下图右侧保留空轴位,确保两张图的时间刻度在水平方向对齐。
|
|
|
if(i == 1) {
|
|
|
plot->getAxisOf(2)->setColor(Qt::transparent);
|
|
|
}
|
|
|
plot->m_pPlot->setBKColor(Qt::white);
|
|
|
plot->m_pPlot->setLegendVisible(false);
|
|
|
charts->addWidget(plot);
|
|
|
charts->setStretchFactor(i, 1);
|
|
|
connect(plot->getAxisOf(0), SIGNAL(sigRangeChanged(double,double,bool,bool)),
|
|
|
this, SLOT(syncForecastTimeAxis(double,double,bool,bool)));
|
|
|
}
|
|
|
window = area->addSubWindow(page);
|
|
|
window->setObjectName("nmForecastResult");
|
|
|
window->setAttribute(Qt::WA_DeleteOnClose, false);
|
|
|
}
|
|
|
window->setWindowTitle(tr("Forecast - %1").arg(wellName));
|
|
|
|
|
|
// 压力直接取求解器结果;阶梯产量及累计量按本次输入制度精确构造。
|
|
|
QVector<double> rateTimes, rateValues, cumulativeTimes, cumulativeValues;
|
|
|
double time = 0.0, cumulative = 0.0, maximumRate = 0.0;
|
|
|
// 零时刻先画关井基线,再接首段产量,显示开始生产时的竖直跳变。
|
|
|
rateTimes.append(0.0);
|
|
|
rateValues.append(0.0);
|
|
|
cumulativeTimes.append(0.0);
|
|
|
cumulativeValues.append(0.0);
|
|
|
for(int i = 0; i < durations.size(); ++i) {
|
|
|
rateTimes << time << time + durations[i];
|
|
|
rateValues << rates[i] << rates[i];
|
|
|
maximumRate = qMax(maximumRate, rates[i]);
|
|
|
time += durations[i];
|
|
|
cumulative += rates[i] * durations[i] / 24.0;
|
|
|
cumulativeTimes.append(time);
|
|
|
cumulativeValues.append(cumulative);
|
|
|
}
|
|
|
const QStringList names = QStringList() << tr("Bottomhole pressure") << tr("Production rate") << tr("Cumulative production");
|
|
|
QVector<iCurveDesc> curves;
|
|
|
for(int i = 0; i < 3; ++i) {
|
|
|
iCurveDesc curve;
|
|
|
curve.m_sCurveName = names[i];
|
|
|
curve.m_oCurveType = POT_CurveDiscrete;
|
|
|
curve.m_bLineVisible = true;
|
|
|
curve.m_bPointVisible = false;
|
|
|
curve.m_bUseY2 = i == 2;
|
|
|
curve.m_oPen = QPen(i == 0 ? Qt::red : i == 1 ? Qt::darkGreen : Qt::gray, 0.35);
|
|
|
curve.m_vecX = i == 0 ? times : i == 1 ? rateTimes : cumulativeTimes;
|
|
|
curve.m_vecY = i == 0 ? pressures : i == 1 ? rateValues : cumulativeValues;
|
|
|
curves.append(curve);
|
|
|
}
|
|
|
iGuiPlot* plots[] = {window->findChild<iGuiPlot*>("nmForecastRate"),
|
|
|
window->findChild<iGuiPlot*>("nmForecastPressure")};
|
|
|
for(int i = 0; i < 2; ++i) {
|
|
|
iGuiPlot* plot = plots[i];
|
|
|
// 表格引用本批曲线,替换图元前关闭旧表,避免显示过期数据。
|
|
|
clearResultCurveDataTables(plot);
|
|
|
// 重复生成只替换曲线,清理旧图元和撤销记录,保留上下图窗口。
|
|
|
for(int j = plot->m_pPlot->getObjCount() - 1; j >= 0; --j)
|
|
|
if(qobject_cast<ZxObjCurve*>(plot->m_pPlot->getObjByIndex(j))) plot->m_pPlot->removeObjByIndex(j);
|
|
|
plot->clearUndoStack();
|
|
|
plot->setCurveData(i == 0 ? curves.mid(1, 2) : curves.mid(0, 1));
|
|
|
plot->runUpdate();
|
|
|
plot->setReadOnly(true);
|
|
|
plot->freshAxisScales(true, true);
|
|
|
if(i == 0) {
|
|
|
// 产量和累计量均从零起算,顶部留少量空白;全程关井保留有效轴范围。
|
|
|
const double rateTop = maximumRate > 0.0 ? maximumRate : 1.0;
|
|
|
const double cumulativeTop = cumulative > 0.0 ? cumulative : 1.0;
|
|
|
VecDouble rateTicks, cumulativeTicks;
|
|
|
for(int tick = 0; tick <= 5; ++tick) {
|
|
|
rateTicks.append(rateTop * tick / 5.0);
|
|
|
cumulativeTicks.append(cumulativeTop * tick / 5.0);
|
|
|
}
|
|
|
plot->setAxisScaleY(0.0, rateTop * 1.05, rateTicks);
|
|
|
plot->setAxisScaleY2(0.0, cumulativeTop * 1.05, cumulativeTicks);
|
|
|
}
|
|
|
// 自动定标会压缩没有曲线的右轴,下图需要保留这块空白以对齐上图。
|
|
|
plot->getAxisOf(2)->setMinimizeAxis(false);
|
|
|
// 两张图共用包含完整 DLL 结果的时间范围,多井计算可能长于当前井输入制度。
|
|
|
const double endTime = qMax(time, times.last());
|
|
|
VecDouble ticks;
|
|
|
for(int tick = 0; tick <= 5; ++tick) ticks.append(endTime * tick / 5.0);
|
|
|
plot->setAxisScaleX(-endTime * 0.05, endTime, ticks);
|
|
|
// 时间刻度及标题只在下图显示,上图仍保留横轴数据与同步关系。
|
|
|
plot->getAxisOf(0)->setMinimizeAxis(i == 0);
|
|
|
}
|
|
|
const bool restore = window->isHidden();
|
|
|
window->widget()->show();
|
|
|
window->show();
|
|
|
// 隐藏的分析页交给框架显示事件布局,避免此时重排把结果重新隐藏。
|
|
|
if((created || restore) && area->isVisible()) area->relayoutSubWnds();
|
|
|
area->setActiveSubWindow(window);
|
|
|
QTimer::singleShot(0, this, SLOT(fitForecastViews()));
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onReadonlyCurveDoubleClicked(
|
|
|
ZxPlot* plot, ZxObjBase*, const QPointF& point, bool& processed)
|
|
|
{
|
|
|
iGuiPlot* widget = qobject_cast<iGuiPlot*>(sender());
|
|
|
if(!widget || !plot || processed) return;
|
|
|
for(int i = plot->getObjCount() - 1; i >= 0; --i) {
|
|
|
ZxObjCurve* curve = qobject_cast<ZxObjCurve*>(plot->getObjByIndex(i));
|
|
|
if(!curve || !curve->isVisible()) continue;
|
|
|
// 框架忽略只读曲线的命中,仅检查双击位置时临时解除,打开表格前恢复保护。
|
|
|
const bool readonly = curve->isReadOnly();
|
|
|
curve->setReadOnly(false);
|
|
|
int option = -1, index = -1;
|
|
|
const bool hit = curve->_runHitTest(point, option, index);
|
|
|
curve->setReadOnly(readonly);
|
|
|
if(!hit) continue;
|
|
|
processed = true;
|
|
|
widget->slotShowCurveData(curve);
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool nmSubWndMain::eventFilter(QObject* watched, QEvent* event)
|
|
|
{
|
|
|
// 子窗口缩放、重新显示均在内部控件完成尺寸更新后再适配。
|
|
|
if(watched->objectName() == "nmForecastCharts" &&
|
|
|
(event->type() == QEvent::Resize || event->type() == QEvent::Show))
|
|
|
QTimer::singleShot(0, this, SLOT(fitForecastViews()));
|
|
|
return iSubWndBaseFit::eventFilter(watched, event);
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::fitForecastViews()
|
|
|
{
|
|
|
// 只适配图形画布的显示尺寸,不改变用户选定的时间和压力范围。
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
if(!fitting || !fitting->m_pMdiArea) return;
|
|
|
QMdiSubWindow* window = fitting->m_pMdiArea->findChild<QMdiSubWindow*>("nmForecastResult");
|
|
|
if(!window) return;
|
|
|
foreach(iGuiPlot* plot, window->findChildren<iGuiPlot*>())
|
|
|
plot->m_pPlotView->setZoomMode(ZxRenderView::eZoomFitWindow, true);
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::syncForecastTimeAxis(double minimum, double maximum, bool recalcTicks, bool adjustZoom)
|
|
|
{
|
|
|
// 只同步本分析预测图的横轴;阻断回传,避免两张图相互触发范围更新。
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
if(!fitting || !fitting->m_pMdiArea) return;
|
|
|
QMdiSubWindow* window = fitting->m_pMdiArea->findChild<QMdiSubWindow*>("nmForecastResult");
|
|
|
if(!window) return;
|
|
|
iGuiPlot* rate = window->findChild<iGuiPlot*>("nmForecastRate");
|
|
|
iGuiPlot* pressure = window->findChild<iGuiPlot*>("nmForecastPressure");
|
|
|
if(!rate || !pressure) return;
|
|
|
iGuiPlot* target = sender() == rate->getAxisOf(0) ? pressure : rate;
|
|
|
ZxSubAxisBase* axis = target->getAxisOf(0);
|
|
|
const bool blocked = axis->blockSignals(true);
|
|
|
axis->setRangeMinMax(minimum, maximum, recalcTicks, adjustZoom);
|
|
|
axis->blockSignals(blocked);
|
|
|
target->m_pPlot->update();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::clearForecastResult()
|
|
|
{
|
|
|
// 由数据管理器通知主界面清场,不依赖预测输入对话框是否仍然打开。
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
if(sender() != m_pResultRefreshDataManager.data() || !fitting || !fitting->m_pMdiArea) return;
|
|
|
QMdiSubWindow* window = fitting->m_pMdiArea->findChild<QMdiSubWindow*>("nmForecastResult");
|
|
|
if(window) {
|
|
|
fitting->m_pMdiArea->removeSubWindow(window);
|
|
|
delete window;
|
|
|
// 直接移除窗口不会经过框架的关闭流程,清场后按当前布局重排其余窗口。
|
|
|
if(fitting->m_pMdiArea->isVisible()) fitting->m_pMdiArea->relayoutSubWnds();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onShowSensitiveResults()
|
|
|
{
|
|
|
// 使用固定所属分析的窗口与数据,不能把结果画到当前活动的其他分析中。
|
|
|
nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data();
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
if(!manager || !fitting || !manager->getSensitiveData()) return;
|
|
|
// 新批次或清空结果时同步移除上一批的上下限分组。
|
|
|
onSensitiveFilterChanged();
|
|
|
updateSensitiveHistoryPlot();
|
|
|
updateSensitiveHistogramPlot();
|
|
|
QSharedPointer<nmDataSensitive::BatchResult> result = manager->getSensitiveData()->getResults();
|
|
|
iGuiPlot* plot = fitting->getPlotByTag(s_Tag_ChartSensitivity);
|
|
|
if(plot) clearResultCurveDataTables(plot);
|
|
|
if(result.isNull()) {
|
|
|
// 新模型已丢弃旧批次,同时清除图元与撤销记录,避免窗口恢复或保存后残留旧曲线。
|
|
|
if(plot) {
|
|
|
for(int i = plot->m_pPlot->getObjCount() - 1; i >= 0; --i) {
|
|
|
if(qobject_cast<ZxObjCurve*>(plot->m_pPlot->getObjByIndex(i)))
|
|
|
plot->m_pPlot->removeObjByIndex(i);
|
|
|
}
|
|
|
plot->clearUndoStack();
|
|
|
plot->setCurveData(QVector<iCurveDesc>());
|
|
|
plot->runUpdate();
|
|
|
QMdiSubWindow* subWindow = nullptr;
|
|
|
for(QWidget* parent = plot->parentWidget(); parent && !subWindow; parent = parent->parentWidget())
|
|
|
subWindow = qobject_cast<QMdiSubWindow*>(parent);
|
|
|
if(subWindow) subWindow->close();
|
|
|
else plot->hide();
|
|
|
}
|
|
|
if(sender() == manager) fitting->setModified(true);
|
|
|
return;
|
|
|
}
|
|
|
const bool created = plot == nullptr;
|
|
|
// 通用取窗接口未实现敏感性类型,直接复用框架已有的内部 MDI 创建函数。
|
|
|
if(created) plot = fitting->initSensitivityPlot();
|
|
|
if(!plot) {
|
|
|
QMessageBox::warning(fitting, nmWxSensitive::tr("Sensitivity"),
|
|
|
tr("Failed to create sensitivity plot."));
|
|
|
return;
|
|
|
}
|
|
|
connect(plot, SIGNAL(sigDoubleClicked(ZxPlot*,ZxObjBase*,const QPointF&,bool&)),
|
|
|
this, SLOT(onReadonlyCurveDoubleClicked(ZxPlot*,ZxObjBase*,const QPointF&,bool&)), Qt::UniqueConnection);
|
|
|
installSensitiveShow(plot);
|
|
|
const QSet<QString> hidden = sender() == manager ? QSet<QString>() : hiddenSensitiveCurves(plot);
|
|
|
QVector<iCurveDesc> curves;
|
|
|
QVector<QVector<double> > history = result->m_vecHistoryLogLog;
|
|
|
if(history.size() == 3 && history[0].size() == history[1].size() &&
|
|
|
history[0].size() == history[2].size() && !history[0].isEmpty())
|
|
|
initloglog(history, curves, true);
|
|
|
for(size_t i = 0; i < result->m_runs.size(); ++i) {
|
|
|
const nmDataSensitive::RunResult& run = result->m_runs[i];
|
|
|
if(!run.m_bSucceeded || run.m_bCancelled) continue;
|
|
|
QVector<QVector<double> > data = run.m_vecLogLog;
|
|
|
if(data.size() != 3 || data[0].size() != data[1].size() ||
|
|
|
data[0].size() != data[2].size() || data[0].isEmpty()) continue;
|
|
|
QVector<iCurveDesc> runCurves;
|
|
|
if(!initloglog(data, runCurves, false)) continue;
|
|
|
// 每组压力与导数均使用实线,稳定名称和颜色避免组间曲线覆盖。
|
|
|
const QColor color = QColor::fromHsv(int((run.m_nRunIndex % 360) * 137 % 360), 190, 190);
|
|
|
runCurves[0].m_sCurveName = nmWxSensitive::tr("Run %1 - Pressure").arg(run.m_nRunIndex + 1);
|
|
|
runCurves[1].m_sCurveName = nmWxSensitive::tr("Run %1 - Derivative").arg(run.m_nRunIndex + 1);
|
|
|
runCurves[0].m_oPen.setColor(color);
|
|
|
runCurves[1].m_oPen.setColor(color);
|
|
|
runCurves[0].m_oPen.setStyle(Qt::SolidLine);
|
|
|
runCurves[1].m_oPen.setStyle(Qt::SolidLine);
|
|
|
curves += runCurves;
|
|
|
}
|
|
|
// 原模型数值解沿用普通双对数图的压力、导数样式,仅区分曲线名称。
|
|
|
QVector<QVector<double> > base = result->m_vecBaseLogLog;
|
|
|
if(base.size() == 3 && base[0].size() == base[1].size() &&
|
|
|
base[0].size() == base[2].size() && !base[0].isEmpty()) {
|
|
|
QVector<iCurveDesc> baseCurves;
|
|
|
if(initloglog(base, baseCurves, false)) {
|
|
|
baseCurves[0].m_sCurveName = nmWxSensitive::tr("Base model - Pressure");
|
|
|
baseCurves[1].m_sCurveName = nmWxSensitive::tr("Base model - Derivative");
|
|
|
curves += baseCurves;
|
|
|
}
|
|
|
}
|
|
|
// 对数坐标只接受有限正数,分别过滤压力与导数,保持各自时间和值对应。
|
|
|
for(int i = 0; i < curves.size(); ++i) {
|
|
|
QVector<double> x, y;
|
|
|
for(int point = 0; point < curves[i].m_vecX.size(); ++point) {
|
|
|
const double time = curves[i].m_vecX[point];
|
|
|
const double value = curves[i].m_vecY[point];
|
|
|
if(qIsFinite(time) && qIsFinite(value) && time > 0.0 && value > 0.0) {
|
|
|
x.append(time);
|
|
|
y.append(value);
|
|
|
}
|
|
|
}
|
|
|
curves[i].m_vecX = x;
|
|
|
curves[i].m_vecY = y;
|
|
|
}
|
|
|
plot->setDropEnabled(false);
|
|
|
plot->setAxisXYLog(true, 0);
|
|
|
plot->setAxisXYLog(true, 1);
|
|
|
if(!result->m_sAxisXName.isEmpty()) plot->setAxisX(result->m_sAxisXName, result->m_sAxisXUnit);
|
|
|
if(!result->m_sAxisYName.isEmpty()) plot->setAxisY(result->m_sAxisYName, result->m_sAxisYUnit);
|
|
|
// 框架刷新只创建或更新传入曲线,不删除缺席曲线,先移除已隐藏或上一批残留的图元。
|
|
|
QSet<QString> visibleNames;
|
|
|
for(int i = 0; i < curves.size(); ++i) visibleNames.insert(curves[i].m_sCurveName);
|
|
|
bool removed = false;
|
|
|
for(int i = plot->m_pPlot->getObjCount() - 1; i >= 0; --i) {
|
|
|
ZxObjCurve* curve = qobject_cast<ZxObjCurve*>(plot->m_pPlot->getObjByIndex(i));
|
|
|
if(curve && !visibleNames.contains(curve->getName())) {
|
|
|
plot->m_pPlot->removeObjByIndex(i);
|
|
|
removed = true;
|
|
|
}
|
|
|
}
|
|
|
// 清理删除曲线相关的撤销记录,避免后续操作恢复已经隐藏的图元。
|
|
|
if(removed) plot->clearUndoStack();
|
|
|
plot->setCurveData(curves);
|
|
|
plot->runUpdate();
|
|
|
for(int i = 0; i < curves.size(); ++i)
|
|
|
if(ZxObjCurve* curve = plot->getCurve(curves[i].m_sCurveName))
|
|
|
curve->setVisible(!hidden.contains(curve->getName()));
|
|
|
plot->setReadOnly(true);
|
|
|
plot->freshAxisScales(true, false);
|
|
|
// 只有新建或恢复窗口时才重新调整内部布局。
|
|
|
QMdiSubWindow* subWindow = nullptr;
|
|
|
for(QWidget* parent = plot->parentWidget(); parent && !subWindow; parent = parent->parentWidget())
|
|
|
subWindow = qobject_cast<QMdiSubWindow*>(parent);
|
|
|
const bool restore = subWindow && subWindow->isHidden();
|
|
|
plot->show();
|
|
|
if(subWindow) {
|
|
|
// 敏感性子窗口标题使用数值模块译文,与历史图、直方图保持一致。
|
|
|
subWindow->setWindowTitle(tr("Sensitivity loglog plot"));
|
|
|
subWindow->show();
|
|
|
if(created || restore) {
|
|
|
ZxMdiArea* area = qobject_cast<ZxMdiArea*>(subWindow->mdiArea());
|
|
|
fitting->layoutSubWnds(area ? area->getLayoutMode() : WLM_Cell);
|
|
|
}
|
|
|
}
|
|
|
if(sender() == manager) fitting->setModified(true);
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::updateSensitiveHistoryPlot()
|
|
|
{
|
|
|
// 复用框架的上下压力/流量控件,作为同一分析页面中的独立 MDI 子窗口。
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data();
|
|
|
if(!fitting || !fitting->m_pMdiArea || !manager || !manager->getSensitiveData()) return;
|
|
|
ZxMdiArea* area = fitting->m_pMdiArea;
|
|
|
QMdiSubWindow* window = area->findChild<QMdiSubWindow*>("nmSensitivityHistory");
|
|
|
const QSharedPointer<nmDataSensitive::BatchResult> batch = manager->getSensitiveData()->getResults();
|
|
|
if(batch.isNull()) {
|
|
|
// 新模型丢弃旧批次时一并销毁窗口,曲线和撤销记录不再残留。
|
|
|
if(window) {
|
|
|
area->removeSubWindow(window);
|
|
|
delete window;
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
const bool created = window == nullptr;
|
|
|
if(created) {
|
|
|
// PF 继承对话框,用普通页面承载,关闭子窗口时不进入其对话框关闭流程。
|
|
|
QWidget* page = new QWidget;
|
|
|
QVBoxLayout* layout = new QVBoxLayout(page);
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
iGuiPlotPF* history = new iGuiPlotPF(s_Tag_HIST_PF, true, true, page);
|
|
|
history->setWindowFlags(Qt::Widget);
|
|
|
history->initUI(tr("Sensitivity history plot"));
|
|
|
history->setTableHelperVisible(false);
|
|
|
layout->addWidget(history);
|
|
|
window = area->addSubWindow(page);
|
|
|
window->setObjectName("nmSensitivityHistory");
|
|
|
window->setWindowTitle(tr("Sensitivity history plot"));
|
|
|
window->setAttribute(Qt::WA_DeleteOnClose, false);
|
|
|
}
|
|
|
installSensitiveShow(window->widget());
|
|
|
iGuiPlotPF* history = window->findChild<iGuiPlotPF*>();
|
|
|
QVector<iCurveDesc> curves;
|
|
|
// 基准压力、实测压力和流量均取成功求解快照,未求解的参数或制度编辑不参与绘图。
|
|
|
const QSharedPointer<const nmPebiResultSnapshot> snapshot = manager->getPebiResultSnapshot();
|
|
|
for(int i = 0; !snapshot.isNull() && i < snapshot->getWellCount(); ++i) {
|
|
|
const nmPebiResultWellSnapshot* well = snapshot->getWellAt(i);
|
|
|
if(well->m_sWellCode != batch->m_sWellCode) continue;
|
|
|
QVector<QVector<double> > measured = well->m_oCurves.m_vecHistoryPressure;
|
|
|
QVector<QVector<double> > base = well->m_oCurves.m_vecResultPressure;
|
|
|
if(measured.size() == 2) initPreHistory(measured, curves, true);
|
|
|
if(base.size() == 2) {
|
|
|
initPreHistory(base, curves, false);
|
|
|
curves.last().m_sCurveName = nmWxSensitive::tr("Base model - Pressure");
|
|
|
}
|
|
|
const QVector<double>* rates = resultReferenceRatesOf(well);
|
|
|
if(rates && rates->size() == well->m_vecFlowDurations.size()) {
|
|
|
iCurveDesc flow;
|
|
|
flow.m_oCurveType = POT_CurveFlow;
|
|
|
flow.m_sCurveName = s_HistorySouce_CurveF;
|
|
|
flow.m_bLineVisible = true;
|
|
|
flow.m_bPointVisible = false;
|
|
|
flow.m_oPen.setColor(Qt::red);
|
|
|
// 与普通历史图一致,流量使用段时长并保留框架的首行占位点。
|
|
|
flow.m_vecX = well->m_vecFlowDurations;
|
|
|
flow.m_vecY = *rates;
|
|
|
flow.m_vecX.prepend(0.0);
|
|
|
flow.m_vecY.prepend(0.0);
|
|
|
curves.append(flow);
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
for(size_t i = 0; i < batch->m_runs.size(); ++i) {
|
|
|
const nmDataSensitive::RunResult& run = batch->m_runs[i];
|
|
|
if(!run.m_bSucceeded || run.m_bCancelled || run.m_vecPressure.size() != 2) continue;
|
|
|
QVector<QVector<double> > pressure = run.m_vecPressure;
|
|
|
initPreHistory(pressure, curves, false);
|
|
|
curves.last().m_sCurveName = nmWxSensitive::tr("Run %1 - Pressure").arg(run.m_nRunIndex + 1);
|
|
|
curves.last().m_oPen.setColor(QColor::fromHsv(int((run.m_nRunIndex % 360) * 137 % 360), 190, 190));
|
|
|
}
|
|
|
// 刷新前清除旧图元;同一批次保留本图的 Show 选择,新批次恢复全部显示。
|
|
|
iGuiPlot* plots[] = {history->m_pWxPlotP, history->m_pWxPlotF};
|
|
|
for(int i = 0; i < 2; ++i) {
|
|
|
iGuiPlot* plot = plots[i];
|
|
|
// 只读历史结果不进入 PF 的流动段编辑处理,双击统一打开框架数据表。
|
|
|
disconnect(plot, SIGNAL(sigDoubleClicked(ZxPlot*,ZxObjBase*,const QPointF&,bool&)),
|
|
|
history, SLOT(slotSubDoubleClicked(ZxPlot*,ZxObjBase*,const QPointF&,bool&)));
|
|
|
connect(plot, SIGNAL(sigDoubleClicked(ZxPlot*,ZxObjBase*,const QPointF&,bool&)),
|
|
|
this, SLOT(onReadonlyCurveDoubleClicked(ZxPlot*,ZxObjBase*,const QPointF&,bool&)), Qt::UniqueConnection);
|
|
|
clearResultCurveDataTables(plot);
|
|
|
const QSet<QString> hidden = sender() == manager ? QSet<QString>() : hiddenSensitiveCurves(plot);
|
|
|
for(int j = plot->m_pPlot->getObjCount() - 1; j >= 0; --j)
|
|
|
if(qobject_cast<ZxObjCurve*>(plot->m_pPlot->getObjByIndex(j))) plot->m_pPlot->removeObjByIndex(j);
|
|
|
plot->clearUndoStack();
|
|
|
plot->setDropEnabled(false);
|
|
|
plot->setAxisXYLog(false, 0);
|
|
|
plot->setAxisXYLog(false, 1);
|
|
|
// PF 的统一分配面向普通历史曲线;多组结果直接交给对应的上下绘图控件。
|
|
|
QVector<iCurveDesc> data;
|
|
|
for(int j = 0; j < curves.size(); ++j)
|
|
|
if((curves[j].m_oCurveType == POT_CurveFlow) == (i == 1)) data.append(curves[j]);
|
|
|
plot->setCurveData(data);
|
|
|
plot->runUpdate();
|
|
|
for(int j = 0; j < data.size(); ++j)
|
|
|
if(ZxObjCurve* curve = plot->getCurve(data[j].m_sCurveName))
|
|
|
curve->setVisible(!hidden.contains(curve->getName()));
|
|
|
plot->setReadOnly(true);
|
|
|
plot->freshAxisScales(true, true);
|
|
|
}
|
|
|
const bool restore = window->isHidden();
|
|
|
history->show();
|
|
|
window->widget()->show();
|
|
|
window->show();
|
|
|
// 历史图只显示曲线,隐藏框架默认的空数据表侧栏。
|
|
|
history->m_pTableHelper->hide();
|
|
|
if(created || restore) area->relayoutSubWnds();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::installSensitiveShow(QWidget* host)
|
|
|
{
|
|
|
// 使用布局的顶部栏,不改变框架绘图控件及子窗口的创建方式。
|
|
|
if(host->findChild<QPushButton*>("nmSensitivityShowButton")) return;
|
|
|
QWidget* bar = new QWidget(host);
|
|
|
QHBoxLayout* layout = new QHBoxLayout(bar);
|
|
|
layout->setContentsMargins(4, 2, 4, 2);
|
|
|
QPushButton* show = new QPushButton(tr("Show"), bar);
|
|
|
show->setObjectName("nmSensitivityShowButton");
|
|
|
layout->addWidget(show);
|
|
|
layout->addStretch();
|
|
|
host->layout()->setMenuBar(bar);
|
|
|
connect(show, SIGNAL(clicked()), this, SLOT(onSensitiveShowClicked()));
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onSensitiveShowClicked()
|
|
|
{
|
|
|
// Show 是普通按钮,每张图复用一个独立的表格窗口。
|
|
|
QPushButton* button = qobject_cast<QPushButton*>(sender());
|
|
|
nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data();
|
|
|
if(!button || !manager || !manager->getSensitiveData()) return;
|
|
|
QWidget* host = button->parentWidget()->parentWidget();
|
|
|
const QSharedPointer<nmDataSensitive::BatchResult> batch = manager->getSensitiveData()->getResults();
|
|
|
if(batch.isNull()) return;
|
|
|
nmWxSensitivityShow* dialog = host->findChild<nmWxSensitivityShow*>("nmSensitivityShowDialog");
|
|
|
if(!dialog) {
|
|
|
iGuiPlot* plot = qobject_cast<iGuiPlot*>(host);
|
|
|
const bool loglog = plot != nullptr;
|
|
|
if(!plot) {
|
|
|
iGuiPlotPF* history = host->findChild<iGuiPlotPF*>();
|
|
|
if(!history) return;
|
|
|
plot = history->m_pWxPlotP;
|
|
|
}
|
|
|
dialog = new nmWxSensitivityShow(batch, plot, host);
|
|
|
dialog->setWindowTitle(loglog ? tr("Show - Sensitivity loglog plot") : tr("Show - Sensitivity history plot"));
|
|
|
// 发布新批次或清除旧模型后关闭旧表格,不能再操作上一批曲线。
|
|
|
connect(manager, SIGNAL(sigSensitiveResultsChanged()), dialog, SLOT(reject()));
|
|
|
}
|
|
|
dialog->show();
|
|
|
dialog->raise();
|
|
|
dialog->activateWindow();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::updateSensitiveHistogramPlot()
|
|
|
{
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data();
|
|
|
if(!fitting || !fitting->m_pMdiArea || !manager || !manager->getSensitiveData()) return;
|
|
|
ZxMdiArea* area = fitting->m_pMdiArea;
|
|
|
QMdiSubWindow* window = area->findChild<QMdiSubWindow*>("nmSensitivityHistogram");
|
|
|
const QSharedPointer<nmDataSensitive::BatchResult> batch = manager->getSensitiveData()->getResults();
|
|
|
if(batch.isNull() || batch->m_calcType != nmDataSensitive::CALC_MONTE_CARLO) {
|
|
|
// 新模型或确定性批次不保留上一批 Monte-Carlo 的统计图。
|
|
|
if(window) {
|
|
|
area->removeSubWindow(window);
|
|
|
delete window;
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
const bool created = window == nullptr;
|
|
|
if(created) {
|
|
|
QWidget* page = new QWidget;
|
|
|
QVBoxLayout* layout = new QVBoxLayout(page);
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
QHBoxLayout* options = new QHBoxLayout;
|
|
|
QPushButton* scatter = new QPushButton(tr("Scatter plot"), page);
|
|
|
scatter->setObjectName("nmScatterButton");
|
|
|
QPushButton* settings = new QPushButton(tr("Histogram options"), page);
|
|
|
settings->setObjectName("nmHistogramOptionsButton");
|
|
|
options->addWidget(scatter);
|
|
|
options->addWidget(settings);
|
|
|
options->addStretch();
|
|
|
layout->addLayout(options);
|
|
|
iGuiPlot* plot = new iGuiPlot(true, page);
|
|
|
plot->initUI(tr("Sensitivity histogram plot"));
|
|
|
plot->setDropEnabled(false);
|
|
|
plot->setAxisX(tr("Goodness of fit"), "MPa");
|
|
|
plot->setAxisY(tr("Count"));
|
|
|
plot->setAxisY(tr("Cumulative probability"), "%", true);
|
|
|
plot->m_pPlot->setBKColor(Qt::white);
|
|
|
for(int i = 0; i < 3; ++i) {
|
|
|
plot->setAxisXYLog(false, i);
|
|
|
plot->getAxisOf(i)->setVisible(true);
|
|
|
}
|
|
|
layout->addWidget(plot);
|
|
|
window = area->addSubWindow(page);
|
|
|
window->setObjectName("nmSensitivityHistogram");
|
|
|
window->setWindowTitle(tr("Sensitivity histogram plot"));
|
|
|
window->setAttribute(Qt::WA_DeleteOnClose, false);
|
|
|
connect(scatter, SIGNAL(clicked()), this, SLOT(onSensitiveScatter()));
|
|
|
connect(settings, SIGNAL(clicked()), this, SLOT(onSensitiveHistogramOptions()));
|
|
|
}
|
|
|
iGuiPlot* plot = window->widget()->findChild<iGuiPlot*>();
|
|
|
const int binCount = batch->m_nHistogramBins;
|
|
|
// 拟合参数与误差共用分箱绘制,轴名称和单位随本批选择同步切换。
|
|
|
const int variable = batch->m_nHistogramVariable;
|
|
|
plot->setAxisX(variable < 0 ? tr("Goodness of fit") : batch->m_fitParameters[variable].getName(),
|
|
|
variable < 0 ? "MPa" : batch->m_fitParameters[variable].getUnit());
|
|
|
QVector<double> values;
|
|
|
for(size_t i = 0; i < batch->m_runs.size(); ++i) {
|
|
|
const nmDataSensitive::RunResult& run = batch->m_runs[i];
|
|
|
// 显示勾选不改变统计总体,失败、取消或无有效统计值的运行不参与统计。
|
|
|
double value;
|
|
|
if(batch->statisticValue(run, variable, value)) values.append(value);
|
|
|
}
|
|
|
double minimum = 0.0, maximum = 1.0;
|
|
|
if(!values.isEmpty()) {
|
|
|
minimum = maximum = values.first();
|
|
|
for(int i = 1; i < values.size(); ++i) {
|
|
|
minimum = qMin(minimum, values[i]);
|
|
|
maximum = qMax(maximum, values[i]);
|
|
|
}
|
|
|
// 单个结果或全部相同的统计值也需要非零横轴范围。
|
|
|
if(minimum == maximum) {
|
|
|
const double padding = qMax(1e-6, qAbs(maximum) * 0.05);
|
|
|
minimum -= padding;
|
|
|
if(variable < 0) minimum = qMax(0.0, minimum);
|
|
|
maximum += padding;
|
|
|
}
|
|
|
}
|
|
|
const double width = (maximum - minimum) / binCount;
|
|
|
QVector<int> counts(binCount, 0);
|
|
|
for(int i = 0; i < values.size(); ++i) {
|
|
|
// 等宽分箱,左闭右开,最大值归入最后一箱。
|
|
|
const int index = qMin(binCount - 1, int((values[i] - minimum) / width));
|
|
|
++counts[index];
|
|
|
}
|
|
|
for(int i = plot->m_pPlot->getObjCount() - 1; i >= 0; --i)
|
|
|
if(qobject_cast<ZxObjCurve*>(plot->m_pPlot->getObjByIndex(i))) plot->m_pPlot->removeObjByIndex(i);
|
|
|
plot->clearUndoStack();
|
|
|
QVector<iCurveDesc> curves;
|
|
|
int largestCount = 0;
|
|
|
if(!values.isEmpty()) {
|
|
|
QVector<QPointF> bars;
|
|
|
iCurveDesc cumulative;
|
|
|
cumulative.m_sCurveName = tr("Cumulative probability");
|
|
|
cumulative.m_oCurveType = POT_CurveDiscrete;
|
|
|
cumulative.m_bUseY2 = true;
|
|
|
cumulative.m_bLineVisible = true;
|
|
|
cumulative.m_bPointVisible = true;
|
|
|
cumulative.m_oPen.setColor(Qt::red);
|
|
|
cumulative.m_oDot = ZxDot(DTS_Circle, Qt::red, 0.6, true);
|
|
|
int total = 0;
|
|
|
for(int i = 0; i < binCount; ++i) {
|
|
|
const double center = minimum + (i + 0.5) * width;
|
|
|
bars.append(QPointF(center, counts[i]));
|
|
|
largestCount = qMax(largestCount, counts[i]);
|
|
|
total += counts[i];
|
|
|
cumulative.m_vecX.append(center);
|
|
|
cumulative.m_vecY.append(100.0 * total / values.size());
|
|
|
}
|
|
|
// 普通曲线入口不支持柱状图,直接使用框架柱状图元,累计概率仍走已有曲线入口。
|
|
|
nmSensitivityHistogramBars* histogram = new nmSensitivityHistogramBars(width);
|
|
|
histogram->init(tr("Count"), plot->m_pPlot->getMainAxisX(), plot->m_pPlot->getMainAxisY());
|
|
|
plot->m_pPlot->addOneObj(histogram);
|
|
|
histogram->setAllValues(bars);
|
|
|
histogram->setCurvePty(QPen(Qt::black), ZxDot(), true, false);
|
|
|
histogram->setBarFill(true);
|
|
|
histogram->setBarFillClr(QColor(85, 107, 47));
|
|
|
histogram->setShow3D(false);
|
|
|
histogram->setPinValue(false);
|
|
|
curves.append(cumulative);
|
|
|
}
|
|
|
plot->setCurveData(curves);
|
|
|
plot->runUpdate();
|
|
|
plot->setReadOnly(true);
|
|
|
VecDouble ticksX, ticksY, ticksY2;
|
|
|
for(int i = 0; i <= 5; ++i) {
|
|
|
ticksX.append(minimum + i * (maximum - minimum) / 5.0);
|
|
|
}
|
|
|
// 累计概率轴每隔 10% 标注,100% 以上保留顶部留白。
|
|
|
for(int i = 0; i <= 10; ++i) ticksY2.append(i * 10.0);
|
|
|
// 小样本时放大计数轴范围,使柱子保持较低;样本较多时自动扩展并留出顶部空间。
|
|
|
const double countRange = qMax(16.0, largestCount * 1.2);
|
|
|
const double step = std::ceil(countRange / 8.0);
|
|
|
const double top = std::ceil(countRange / step) * step;
|
|
|
// 保持原轴范围,将主刻度加密一倍,计数刻度仍使用整数。
|
|
|
const double tickStep = qMax(1.0, std::ceil(step / 2.0));
|
|
|
for(double tick = 0.0; tick < top; tick += tickStep) ticksY.append(tick);
|
|
|
ticksY.append(top);
|
|
|
plot->setAxisScaleX(minimum, maximum, ticksX);
|
|
|
plot->setAxisScaleY(0.0, top, ticksY);
|
|
|
plot->setAxisScaleY2(0.0, 110.0, ticksY2);
|
|
|
const bool restore = window->isHidden();
|
|
|
plot->show();
|
|
|
window->widget()->show();
|
|
|
window->show();
|
|
|
if(created || restore) area->relayoutSubWnds();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onSensitiveHistogramOptions()
|
|
|
{
|
|
|
// 设置窗口复用当前批次,分箱数在 Apply 或 OK 后才应用到直方图。
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data();
|
|
|
if(!fitting || !fitting->m_pMdiArea || !manager || !manager->getSensitiveData()) return;
|
|
|
QMdiSubWindow* window = fitting->m_pMdiArea->findChild<QMdiSubWindow*>("nmSensitivityHistogram");
|
|
|
const QSharedPointer<nmDataSensitive::BatchResult> batch = manager->getSensitiveData()->getResults();
|
|
|
if(!window || batch.isNull()) return;
|
|
|
nmWxSensitivityHistogramOptions* dialog = window->findChild<nmWxSensitivityHistogramOptions*>();
|
|
|
if(!dialog) {
|
|
|
dialog = new nmWxSensitivityHistogramOptions(batch, batch->m_nHistogramBins, window->widget());
|
|
|
connect(dialog, SIGNAL(settingsChanged(int,int)), this, SLOT(onSensitiveHistogramSettingsChanged(int,int)));
|
|
|
connect(manager, SIGNAL(sigSensitiveResultsChanged()), dialog, SLOT(reject()));
|
|
|
}
|
|
|
dialog->show();
|
|
|
dialog->raise();
|
|
|
dialog->activateWindow();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onSensitiveHistogramSettingsChanged(int bins, int variable)
|
|
|
{
|
|
|
// 分箱数与统计变量随批次保存,散点图独立选择自己的纵轴。
|
|
|
nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data();
|
|
|
if(!manager || !manager->getSensitiveData()) return;
|
|
|
const QSharedPointer<nmDataSensitive::BatchResult> batch = manager->getSensitiveData()->getResults();
|
|
|
if(batch.isNull()) return;
|
|
|
batch->m_nHistogramVariable = variable;
|
|
|
batch->m_nHistogramBins = bins;
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
if(!fitting || !fitting->m_pMdiArea) return;
|
|
|
QMdiSubWindow* window = fitting->m_pMdiArea->findChild<QMdiSubWindow*>("nmSensitivityHistogram");
|
|
|
if(!window) return;
|
|
|
fitting->setModified(true);
|
|
|
updateSensitiveHistogramPlot();
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onSensitiveFilterChanged()
|
|
|
{
|
|
|
// 只刷新所属分析的结果参数页,不触发会关闭散点窗的批次更新信号。
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
if(!fitting) return;
|
|
|
QWidget* page = fitting->getFitSubRstWxOf(FSRT_NmRst, false);
|
|
|
nmWxParaPropertyPebi* parameters = page ? page->findChild<nmWxParaPropertyPebi*>() : nullptr;
|
|
|
if(parameters) parameters->refreshSensitivityResults();
|
|
|
// 只有用户调整阈值才标记修改,加载成果时的面板刷新不触发保存提示。
|
|
|
if(qobject_cast<nmWxSensitivityScatter*>(sender())) fitting->setModified(true);
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::onSensitiveScatter()
|
|
|
{
|
|
|
// 与 Show 一样打开独立弹窗,重复点击时激活已有窗口。
|
|
|
iSubWndFitting* fitting = m_pResultRefreshFitting.data();
|
|
|
nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data();
|
|
|
if(!fitting || !fitting->m_pMdiArea || !manager || !manager->getSensitiveData()) return;
|
|
|
const QSharedPointer<nmDataSensitive::BatchResult> batch = manager->getSensitiveData()->getResults();
|
|
|
if(batch.isNull() || batch->m_calcType != nmDataSensitive::CALC_MONTE_CARLO) return;
|
|
|
QMdiSubWindow* window = fitting->m_pMdiArea->findChild<QMdiSubWindow*>("nmSensitivityHistogram");
|
|
|
if(!window) return;
|
|
|
nmWxSensitivityScatter* dialog = window->findChild<nmWxSensitivityScatter*>();
|
|
|
if(!dialog) {
|
|
|
double baseError = -1.0;
|
|
|
const QSharedPointer<const nmPebiResultSnapshot> snapshot = manager->getPebiResultSnapshot();
|
|
|
for(int i = 0; !snapshot.isNull() && i < snapshot->getWellCount(); ++i) {
|
|
|
const nmPebiResultWellSnapshot* well = snapshot->getWellAt(i);
|
|
|
if(well->m_sWellCode != batch->m_sWellCode) continue;
|
|
|
baseError = nmCalculationDllPebiSolverTask::calculatePressureRmse(
|
|
|
well->m_oCurves.m_vecHistoryPressure, well->m_oCurves.m_vecResultPressure);
|
|
|
break;
|
|
|
}
|
|
|
dialog = new nmWxSensitivityScatter(batch, baseError, window->widget());
|
|
|
connect(dialog, SIGNAL(filterChanged()), this, SLOT(onSensitiveFilterChanged()));
|
|
|
// 构造时已应用初始阈值,连接完成后补一次结果面板同步。
|
|
|
onSensitiveFilterChanged();
|
|
|
// 新结果或新模型生成后关闭弹窗,避免保留旧批次的点和阈值。
|
|
|
connect(manager, SIGNAL(sigSensitiveResultsChanged()), dialog, SLOT(reject()));
|
|
|
}
|
|
|
dialog->show();
|
|
|
dialog->raise();
|
|
|
dialog->activateWindow();
|
|
|
}
|
|
|
|
|
|
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());
|
|
|
const QString sFailureReason = pTask->getFailureReason();
|
|
|
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,
|
|
|
bInputChanged,
|
|
|
sFailureReason);
|
|
|
}
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
// 共享停止按钮只控制当前占用进度框的任务,DLL 返回后的处理各自保持原路径。
|
|
|
nmCalculationDllPebiSolverTask* pRunningTask = !m_pAuxiliarySolverTask.isNull()
|
|
|
? m_pAuxiliarySolverTask.data() : m_pSolverTask;
|
|
|
if(pRunningTask != nullptr && pRunningTask->isRunning()) {
|
|
|
m_bSolverCancelRequested = true;
|
|
|
pRunningTask->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_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)
|
|
|
{
|
|
|
return nmPebiResultPageInstaller::installOrSynchronize(
|
|
|
pSubWndFit, pDataManager);
|
|
|
}
|
|
|
|
|
|
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 nSolveTimeMs = m_nPendingResultSolveTimeMs;
|
|
|
m_bPendingResultCompletionMessage = false;
|
|
|
m_bPendingResultWindowReady = false;
|
|
|
m_nPendingResultSolveTimeMs = -1;
|
|
|
showSolverResultCompletion(bResultWindowReady,
|
|
|
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_pAuxiliarySolverTask.isNull() ||
|
|
|
(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,
|
|
|
const QString& sFailureReason)
|
|
|
{
|
|
|
if(result == NM_Calculation_Result_Success) {
|
|
|
return this->mergeAnaResultToFitting();
|
|
|
} else {
|
|
|
// 如果失败,进度条其实没必要拉满了,直接提示错误
|
|
|
const QString sMessage = sFailureReason.isEmpty()
|
|
|
? tr("No specific failure reason is available.")
|
|
|
: sFailureReason;
|
|
|
QMessageBox::warning(this, tr("Solver failed"), sMessage);
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::scheduleSolverResultCompletion(
|
|
|
bool bSucceeded,
|
|
|
int nSolveTimeMs,
|
|
|
bool bInputChanged,
|
|
|
const QString& sFailureReason)
|
|
|
{
|
|
|
s_pPendingSolverWindow = this;
|
|
|
m_bSolverResultCompletionPending = true;
|
|
|
m_bPendingSolverSucceeded = bSucceeded;
|
|
|
m_bPendingSolverInputChanged = bInputChanged;
|
|
|
m_sPendingSolverFailureReason = sFailureReason;
|
|
|
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 QString sFailureReason = m_sPendingSolverFailureReason;
|
|
|
const int nSolveTimeMs = m_nPendingSolverTimeMs;
|
|
|
m_bSolverResultCompletionPending = false;
|
|
|
m_bPendingSolverSucceeded = false;
|
|
|
m_bPendingSolverInputChanged = false;
|
|
|
m_sPendingSolverFailureReason.clear();
|
|
|
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,
|
|
|
sFailureReason);
|
|
|
}
|
|
|
|
|
|
// mergeAnaResultToFitting() 已使用完启动时捕获的上下文。
|
|
|
m_pSolverDataManager = nullptr;
|
|
|
m_pSolverFitting = nullptr;
|
|
|
m_bSolverCancelRequested = false;
|
|
|
|
|
|
if(bSucceeded && bResultWindowReady &&
|
|
|
m_bPendingResultWidgetRefresh) {
|
|
|
// 首次结果页尚未填充,最终提示必须等页面真实创建结果返回后再显示。
|
|
|
m_bPendingResultCompletionMessage = true;
|
|
|
m_nPendingResultSolveTimeMs = nSolveTimeMs;
|
|
|
} else if(bSucceeded) {
|
|
|
showSolverResultCompletion(bResultWindowReady,
|
|
|
nSolveTimeMs);
|
|
|
}
|
|
|
|
|
|
// 首次结果载体创建后再跨一个事件循环填充,避免 Qt 4.8 布局重入。
|
|
|
schedulePendingResultWidgets();
|
|
|
if(s_pPendingSolverWindow == this) {
|
|
|
s_pPendingSolverWindow = nullptr;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmSubWndMain::showSolverResultCompletion(
|
|
|
bool bResultWindowReady,
|
|
|
int nSolveTimeMs)
|
|
|
{
|
|
|
// 完成提示只显示 DLL 返回的本次求解耗时。
|
|
|
if(bResultWindowReady && nSolveTimeMs >= 0) {
|
|
|
QMessageBox oMessageBox(QMessageBox::Information, tr("Solver completed"),
|
|
|
tr("Solve time: %1 ms").arg(nSolveTimeMs),
|
|
|
QMessageBox::Ok, this);
|
|
|
// 单行耗时在提示区域水平、垂直居中,与左侧图标对齐。
|
|
|
QLabel* pMessageLabel = oMessageBox.findChild<QLabel*>("qt_msgbox_label");
|
|
|
if(pMessageLabel != nullptr) {
|
|
|
pMessageLabel->setAlignment(Qt::AlignCenter);
|
|
|
pMessageLabel->setMinimumHeight(
|
|
|
oMessageBox.iconPixmap().height());
|
|
|
}
|
|
|
oMessageBox.exec();
|
|
|
} 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 nSolveTimeMs = pTask != nullptr ? pTask->getSolveTimeMs() : -1;
|
|
|
const bool bCancelled = pTask->wasCancelled();
|
|
|
QString sFailureReason = pTask->getFailureReason();
|
|
|
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;
|
|
|
if(!isSuccessed) {
|
|
|
sFailureReason = pTask->getFailureReason();
|
|
|
}
|
|
|
} 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,
|
|
|
nSolveTimeMs,
|
|
|
bInputChanged,
|
|
|
sFailureReason);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
|
// 压力和流量属于同一口结果井;切井时必须一起替换,避免下图残留上一口井制度。
|
|
|
const QVector<double>* pReferenceRates =
|
|
|
resultReferenceRatesOf(pWell);
|
|
|
iCurveDesc oFlowCurve;
|
|
|
oFlowCurve.m_oCurveType = POT_CurveFlow;
|
|
|
oFlowCurve.m_sCurveName = s_HistorySouce_CurveF;
|
|
|
oFlowCurve.m_bLineVisible = true;
|
|
|
oFlowCurve.m_bPointVisible = false;
|
|
|
oFlowCurve.m_oPen.setColor(Qt::red);
|
|
|
if(pWell != NULL && pReferenceRates != NULL &&
|
|
|
pReferenceRates->size() == pWell->m_vecFlowDurations.size()) {
|
|
|
// 快照只保存真实流量段;绘图对象仍按框架原始格式使用首个
|
|
|
// (0,0) 占位点,一基流动段索引因此可以直接恢复而无需换算。
|
|
|
oFlowCurve.m_vecX = pWell->m_vecFlowDurations;
|
|
|
oFlowCurve.m_vecY = *pReferenceRates;
|
|
|
oFlowCurve.m_vecX.prepend(0.0);
|
|
|
oFlowCurve.m_vecY.prepend(0.0);
|
|
|
}
|
|
|
QVector<iCurveDesc> vecHistoryFlow;
|
|
|
vecHistoryFlow.append(oFlowCurve);
|
|
|
pSubWndFit->adjustFitSubPlotBy(
|
|
|
FSRT_Hist, vecHistoryFlow, true, &sErrorMessage);
|
|
|
|
|
|
// 框架流动段索引与 ZxSegmentInfo 相同,均为一基。这里只更新只读结果图元,
|
|
|
// 阻断选择信号,不能让结果井切换反向修改当前分析井的流动段配置。
|
|
|
QWidget* pHistoryWidget = pSubWndFit->getFitSubRstWxOf(
|
|
|
FSRT_Hist, false, &sErrorMessage);
|
|
|
iGuiPlotPF* pHistoryPlot = qobject_cast<iGuiPlotPF*>(pHistoryWidget);
|
|
|
if(pHistoryPlot != NULL) {
|
|
|
ZxObjCurveBase* pFlowObject = NULL;
|
|
|
if(pHistoryPlot->getChartDataObjOfPF(pFlowObject, false) &&
|
|
|
pFlowObject != NULL) {
|
|
|
ZxObjCurveFlow* pFlowCurve =
|
|
|
dynamic_cast<ZxObjCurveFlow*>(pFlowObject);
|
|
|
if(pFlowCurve != NULL) {
|
|
|
const bool bSignalsBlocked = pFlowCurve->blockSignals(true);
|
|
|
pFlowCurve->setCurSegPtIndex(
|
|
|
pWell == NULL ? 0 :
|
|
|
pWell->m_nFlowSectionIndex);
|
|
|
pFlowCurve->blockSignals(bSignalsBlocked);
|
|
|
pHistoryPlot->runUpdate();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
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();
|
|
|
// }
|
|
|
//
|
|
|
// // 压力曲线统一由 HX_NWTM_PLOT 计算,旧曲线库调用示例已删除。
|
|
|
//}
|
|
|
|
|
|
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);
|
|
|
}
|