diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index 56cf88a..f5e67e3 100644 Binary files a/Bin/Config/Lang/cn/nmNum_cn.qm and b/Bin/Config/Lang/cn/nmNum_cn.qm differ diff --git a/Bin/Config/Lang/cn/nmNum_cn.ts b/Bin/Config/Lang/cn/nmNum_cn.ts index f699e02..65b9b87 100644 --- a/Bin/Config/Lang/cn/nmNum_cn.ts +++ b/Bin/Config/Lang/cn/nmNum_cn.ts @@ -6554,6 +6554,22 @@ Average pressure in contour: %2 MPa Property Interpolation 属性插值 + + Dataset: + 数据组: + + + Add dataset + 新建数据组 + + + Delete dataset + 删除数据组 + + + Dataset #%1 + 数据组 #%1 + Property: 属性: @@ -6610,6 +6626,14 @@ Average pressure in contour: %2 MPa Clear 清空 + + Show measurement points + 显示测点 + + + Show labels + 显示标签 + Kriging Parameters Kriging参数 diff --git a/Include/nmNum/nmData/nmDataAnalyzeManager.h b/Include/nmNum/nmData/nmDataAnalyzeManager.h index c908ac2..9512d01 100644 --- a/Include/nmNum/nmData/nmDataAnalyzeManager.h +++ b/Include/nmNum/nmData/nmDataAnalyzeManager.h @@ -51,6 +51,47 @@ struct BackgroundImageInfo { QImage objImage; // 具体的QImage对象 }; +/* 插值测点数据 */ +struct NM_DATA_EXPORT nmPropertyInterpolationPointData { + nmPropertyInterpolationPointData() + : x(0.0), y(0.0), value(0.0) + { + } + + nmPropertyInterpolationPointData(double xValue, double yValue, double propertyValue) + : x(xValue), y(yValue), value(propertyValue) + { + } + + double x; + double y; + double value; +}; + +/* 一组属性插值数据及其Kriging参数 */ +struct NM_DATA_EXPORT nmPropertyInterpolationDataSet { + nmPropertyInterpolationDataSet() + : property("k"), + showPoints(true), + showLabels(true), + nugget(0.01), + sill(100.0), + range(1000.0), + model(0) + { + } + + QString name; + QString property; + QVector points; + bool showPoints; + bool showLabels; + double nugget; + double sill; + double range; + int model; +}; + enum NM_SOLVER_MODEL_TYPE { SMT_Oil_ConstPvt = 1, SMT_Oil_VariablePvt = 2, @@ -433,6 +474,10 @@ class NM_DATA_EXPORT nmDataAnalyzeManager : public ZxDataObjectBin // 获取背景图片信息 const BackgroundImageInfo& getBackgroundImageInfo() const; + /* 获取及设置属性插值数据组 */ + QVector getPropertyInterpolationDataSets() const; + void setPropertyInterpolationDataSets(const QVector& dataSets); + // 统一的读写整个项目数据的方法 // @param filePath JSON文件的路径 // @return 读取成功返回 true,否则返回 false @@ -647,6 +692,9 @@ class NM_DATA_EXPORT nmDataAnalyzeManager : public ZxDataObjectBin // 显示设置信息 QVector m_vecDisplaySettings; + /* 属性插值数据组 */ + QVector m_vecPropertyInterpolationDataSets; + // 背景图片信息 BackgroundImageInfo m_backgroundImageInfo; diff --git a/Include/nmNum/nmPlot/nmGuiPlot.h b/Include/nmNum/nmPlot/nmGuiPlot.h index 3a57ae6..9bb7cce 100644 --- a/Include/nmNum/nmPlot/nmGuiPlot.h +++ b/Include/nmNum/nmPlot/nmGuiPlot.h @@ -42,6 +42,9 @@ class NM_PLOT_EXPORT nmGuiPlot : public iGuiPlot { // Action命令执行 virtual bool runAction(QString sAction) override; + // 取消当前地图绘图操作. + void cancelActiveTools(); + // 创建添加一个Obj nmObjBase* appendOneObj(NM_Obj_Type o, QString& sName, QVector& vec); nmObjBase* _createOneObj(NM_Obj_Type o, QString& sName); diff --git a/Include/nmNum/nmPlot/nmGuiPlotCmdHelper.h b/Include/nmNum/nmPlot/nmGuiPlotCmdHelper.h index 58a8fd4..8c026d9 100644 --- a/Include/nmNum/nmPlot/nmGuiPlotCmdHelper.h +++ b/Include/nmNum/nmPlot/nmGuiPlotCmdHelper.h @@ -19,6 +19,9 @@ public: virtual bool runAction(QString sAction) override; + // 取消当前地图绘图操作,并恢复默认操作状态. + void cancelActiveTools(); + // 插入背景 void insertBkImg(); diff --git a/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.h b/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.h index 9c8dc7f..4d9572f 100644 --- a/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.h +++ b/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.h @@ -2,42 +2,118 @@ #define NMWXPROPERTYINTERPOLATIONDLG_H #include "iDlgBase.h" +#include "nmDataAnalyzeManager.h" #include "nmSubWxs_global.h" +#include +#include +#include + +class QCheckBox; class QComboBox; class QDoubleSpinBox; +class QEvent; class QLineEdit; +class QPointF; class QPushButton; class QTableWidget; +class QTableWidgetItem; +class nmGuiPlot; +class nmObjPoint; class NM_SUB_WXS_EXPORT nmWxPropertyInterpolationDlg : public iDlgBase { + Q_OBJECT + public: - explicit nmWxPropertyInterpolationDlg(QWidget* parent = 0); + explicit nmWxPropertyInterpolationDlg(nmGuiPlot* plot, + nmDataAnalyzeManager* dataManager, + QWidget* parent = 0); + ~nmWxPropertyInterpolationDlg(); + +protected: + bool eventFilter(QObject* watched, QEvent* event); + +private slots: + void onDataSetChanged(int index); + void onAddDataSet(); + void onDeleteDataSet(); + void onDataSetNameChanged(const QString& name); + void onCurrentDataSetEdited(); + void onMapPickToggled(bool checked); + void onMapClicked(const QPointF& position); + void onPointSelectionChanged(); + void onPointItemChanged(QTableWidgetItem* item); + void onDeleteSelectedPoint(); + void onClearPoints(); + void onShowPointsToggled(bool checked); + void onShowLabelsToggled(bool checked); private: - // 初始化属性插值界面 + // 初始化界面及持久化数据组. void initInterpolationUI(); + void initializeDataSets(); + nmPropertyInterpolationDataSet createDefaultDataSet(); + QString nextDataSetName(); + void loadDataSet(int index); + void clearCurrentDataSet(); + void saveCurrentDataSet(); + void syncDataSetsToManager(bool markModified); + void updateDataSetButtons(); + + // 管理测点表格和地图上的临时标记. + void appendPoint(const QPointF& valuePoint); + void appendPointRow(const nmPropertyInterpolationPointData& point, + const QString& markerName); + QString createMarker(const QPointF& valuePosition, + double value, + bool showPoints, + bool showLabels); + void createMarkersForAllDataSets(); + void removeDataSetMarkers(int dataSetIndex); + void removeAllMarkers(); + QString markerNameAt(int row) const; + nmObjPoint* markerByName(const QString& markerName) const; + nmObjPoint* markerAt(int row) const; + void removeMarkerByName(const QString& markerName); + void clearTableSelection(); + void updatePointButtons(); + void updateCurrentMarkerVisibility(); private: - // 插值属性及名称 + QPointer m_pPlot; + QPointer m_pDataManager; + + // 多组插值数据. + QComboBox* m_pDataSetCombo; + QPushButton* m_pAddDataSetButton; + QPushButton* m_pDeleteDataSetButton; + QVector m_vecDataSets; + QVector m_vecMarkerNames; + int m_nCurrentDataSetIndex; + int m_nNextPointId; + bool m_bUpdatingUi; + + // 当前数据组的属性及名称. QComboBox* m_pPropertyCombo; QLineEdit* m_pNameEdit; - // 测点数据录入 + // 测点数据录入. QDoubleSpinBox* m_pNewValueSpin; QPushButton* m_pMapPickButton; QTableWidget* m_pPointTable; QPushButton* m_pDeletePointButton; QPushButton* m_pClearPointsButton; + QCheckBox* m_pShowPointsCheck; + QCheckBox* m_pShowLabelsCheck; - // Kriging插值参数 + // Kriging插值参数. QDoubleSpinBox* m_pNuggetSpin; QDoubleSpinBox* m_pSillSpin; QDoubleSpinBox* m_pRangeSpin; QComboBox* m_pModelCombo; - // 结果预览及应用 + // 结果预览及应用. QPushButton* m_pPreviewButton; QPushButton* m_pApplyButton; }; diff --git a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp index 87c9380..343d60f 100644 --- a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp +++ b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp @@ -65,6 +65,7 @@ #include "nmDataTimeStepSetting.h" +#include #include #include #include @@ -3353,6 +3354,17 @@ const BackgroundImageInfo& nmDataAnalyzeManager::getBackgroundImageInfo() const return m_backgroundImageInfo; } +QVector nmDataAnalyzeManager::getPropertyInterpolationDataSets() const +{ + return m_vecPropertyInterpolationDataSets; +} + +void nmDataAnalyzeManager::setPropertyInterpolationDataSets( + const QVector& dataSets) +{ + m_vecPropertyInterpolationDataSets = dataSets; +} + // 从 JSON 文件读取数据到 C++ 对象 bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath) { @@ -3400,6 +3412,7 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath) } m_vecLayers.clear(); + m_vecPropertyInterpolationDataSets.clear(); if(m_reservoirData) { delete m_reservoirData; @@ -3517,7 +3530,66 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath) } } - // 解析 "Reservoir" 对象 + /* 读取属性插值数据组;旧成果没有该节点时保持为空 */ + if(doc.HasMember("PropertyInterpolationDataSets") && + doc["PropertyInterpolationDataSets"].IsArray()) { + const rapidjson::Value& dataSetsJson = doc["PropertyInterpolationDataSets"]; + + for(rapidjson::SizeType i = 0; i < dataSetsJson.Size(); ++i) { + const rapidjson::Value& dataSetJson = dataSetsJson[i]; + if(!dataSetJson.IsObject()) { + continue; + } + + nmPropertyInterpolationDataSet dataSet; + if(dataSetJson.HasMember("Name") && dataSetJson["Name"].IsString()) { + dataSet.name = QString::fromUtf8(dataSetJson["Name"].GetString()); + } + if(dataSetJson.HasMember("Property") && dataSetJson["Property"].IsString()) { + dataSet.property = QString::fromUtf8(dataSetJson["Property"].GetString()); + } + if(dataSetJson.HasMember("ShowPoints") && dataSetJson["ShowPoints"].IsBool()) { + dataSet.showPoints = dataSetJson["ShowPoints"].GetBool(); + } + if(dataSetJson.HasMember("ShowLabels") && dataSetJson["ShowLabels"].IsBool()) { + dataSet.showLabels = dataSetJson["ShowLabels"].GetBool(); + } + if(dataSetJson.HasMember("Nugget") && dataSetJson["Nugget"].IsNumber()) { + dataSet.nugget = dataSetJson["Nugget"].GetDouble(); + } + if(dataSetJson.HasMember("Sill") && dataSetJson["Sill"].IsNumber()) { + dataSet.sill = dataSetJson["Sill"].GetDouble(); + } + if(dataSetJson.HasMember("Range") && dataSetJson["Range"].IsNumber()) { + dataSet.range = dataSetJson["Range"].GetDouble(); + } + if(dataSetJson.HasMember("Model") && dataSetJson["Model"].IsInt()) { + dataSet.model = dataSetJson["Model"].GetInt(); + } + + if(dataSetJson.HasMember("Points") && dataSetJson["Points"].IsArray()) { + const rapidjson::Value& pointsJson = dataSetJson["Points"]; + for(rapidjson::SizeType pointIndex = 0; pointIndex < pointsJson.Size(); ++pointIndex) { + const rapidjson::Value& pointJson = pointsJson[pointIndex]; + if(!pointJson.IsObject() || + !pointJson.HasMember("X") || !pointJson["X"].IsNumber() || + !pointJson.HasMember("Y") || !pointJson["Y"].IsNumber() || + !pointJson.HasMember("Value") || !pointJson["Value"].IsNumber()) { + continue; + } + + dataSet.points.append(nmPropertyInterpolationPointData( + pointJson["X"].GetDouble(), + pointJson["Y"].GetDouble(), + pointJson["Value"].GetDouble())); + } + } + + m_vecPropertyInterpolationDataSets.append(dataSet); + } + } + + /* 解析 "Reservoir" 对象 */ if(doc.HasMember("Reservoir") && doc["Reservoir"].IsObject()) { m_reservoirData = new nmDataReservoir; m_reservoirData->FromJsonValue(doc["Reservoir"]); @@ -3736,6 +3808,42 @@ bool nmDataAnalyzeManager::WriteProjectData(const QString & filePath) doc.AddMember("Layers", layersJsonArray, allocator); + /* 保存属性插值数据组 */ + rapidjson::Value interpolationDataSetsJson(rapidjson::kArrayType); + for(int dataSetIndex = 0; + dataSetIndex < m_vecPropertyInterpolationDataSets.size(); + ++dataSetIndex) { + const nmPropertyInterpolationDataSet& dataSet = + m_vecPropertyInterpolationDataSets[dataSetIndex]; + rapidjson::Value dataSetJson(rapidjson::kObjectType); + + QByteArray nameUtf8 = dataSet.name.toUtf8(); + QByteArray propertyUtf8 = dataSet.property.toUtf8(); + dataSetJson.AddMember("Name", + rapidjson::Value(nameUtf8.constData(), allocator).Move(), allocator); + dataSetJson.AddMember("Property", + rapidjson::Value(propertyUtf8.constData(), allocator).Move(), allocator); + dataSetJson.AddMember("ShowPoints", dataSet.showPoints, allocator); + dataSetJson.AddMember("ShowLabels", dataSet.showLabels, allocator); + dataSetJson.AddMember("Nugget", dataSet.nugget, allocator); + dataSetJson.AddMember("Sill", dataSet.sill, allocator); + dataSetJson.AddMember("Range", dataSet.range, allocator); + dataSetJson.AddMember("Model", dataSet.model, allocator); + + rapidjson::Value pointsJson(rapidjson::kArrayType); + for(int pointIndex = 0; pointIndex < dataSet.points.size(); ++pointIndex) { + const nmPropertyInterpolationPointData& point = dataSet.points[pointIndex]; + rapidjson::Value pointJson(rapidjson::kObjectType); + pointJson.AddMember("X", point.x, allocator); + pointJson.AddMember("Y", point.y, allocator); + pointJson.AddMember("Value", point.value, allocator); + pointsJson.PushBack(pointJson, allocator); + } + dataSetJson.AddMember("Points", pointsJson, allocator); + interpolationDataSetsJson.PushBack(dataSetJson, allocator); + } + doc.AddMember("PropertyInterpolationDataSets", interpolationDataSetsJson, allocator); + // 序列化 "Reservoir" if(m_reservoirData) { rapidjson::Value reservoirJson(rapidjson::kObjectType); diff --git a/Src/nmNum/nmData/nmDataJsonTools.cpp b/Src/nmNum/nmData/nmDataJsonTools.cpp index cadf570..36ba197 100644 --- a/Src/nmNum/nmData/nmDataJsonTools.cpp +++ b/Src/nmNum/nmData/nmDataJsonTools.cpp @@ -7,7 +7,6 @@ // RapidJSON 内部使用 char*,这里需要转换为 std::string #include #include -#include #include #include "rapidjson/document.h" @@ -211,39 +210,34 @@ bool nmDataJsonTools::WriteDomToFile(const rapidjson::Document& doc, const QStri rapidjson::PrettyWriter writer(buffer); // 使用 PrettyWriter 进行美化输出 doc.Accept(writer); - // 将字符串写入文件 + /* RapidJSON输出为UTF-8字节,直接写入可避免中文被本地编码再次转换 */ QFile file(filePath); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { qDebug() << "Error: Could not open file for writing:" << filePath; return false; } - QTextStream out(&file); - out << buffer.GetString(); + const qint64 bufferSize = static_cast(buffer.GetSize()); + const qint64 writtenSize = file.write(buffer.GetString(), bufferSize); file.close(); - return true; + return writtenSize == bufferSize; } // 从文件读取 JSON 并解析到 RapidJSON Document bool nmDataJsonTools::ReadDomFromFile(const QString& filePath, rapidjson::Document& doc) { QFile file(filePath); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + if (!file.open(QIODevice::ReadOnly)) { qDebug() << "Error: Could not open file for reading:" << filePath; return false; } - QTextStream in(&file); - QString jsonString = in.readAll(); + const QByteArray jsonData = file.readAll(); file.close(); - // 将 QString 转换为 std::string - QByteArray byteArray = jsonString.toUtf8(); - std::string stdJsonString = byteArray.constData(); - - // 解析 JSON 字符串到 RapidJSON Document - doc.Parse(stdJsonString.c_str()); + /* 文件内容保持RapidJSON所需的UTF-8编码 */ + doc.Parse(jsonData.constData()); if (doc.HasParseError()) { qDebug() << "Error parsing JSON from file:" << filePath << "Error:" << doc.GetParseError(); @@ -251,4 +245,4 @@ bool nmDataJsonTools::ReadDomFromFile(const QString& filePath, rapidjson::Docume } return true; -} \ No newline at end of file +} diff --git a/Src/nmNum/nmPlot/nmGuiPlot.cpp b/Src/nmNum/nmPlot/nmGuiPlot.cpp index dd41ffe..2e39630 100644 --- a/Src/nmNum/nmPlot/nmGuiPlot.cpp +++ b/Src/nmNum/nmPlot/nmGuiPlot.cpp @@ -167,6 +167,14 @@ bool nmGuiPlot::runAction(QString sAction) return m_pCmdHelper->runAction(sAction); } +void nmGuiPlot::cancelActiveTools() +{ + nmGuiPlotCmdHelper* helper = dynamic_cast(m_pCmdHelper); + if(helper != NULL) { + helper->cancelActiveTools(); + } +} + void nmGuiPlot::initDefultGeoObj() { diff --git a/Src/nmNum/nmPlot/nmGuiPlotCmdHelper.cpp b/Src/nmNum/nmPlot/nmGuiPlotCmdHelper.cpp index 03d6d16..836329e 100644 --- a/Src/nmNum/nmPlot/nmGuiPlotCmdHelper.cpp +++ b/Src/nmNum/nmPlot/nmGuiPlotCmdHelper.cpp @@ -161,6 +161,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) { pPlot->appendTools(pObjTool); connect(pObjTool, SIGNAL(sigPtsFinished(QVector&)), \ this, SLOT(slotObjPtsFinished(QVector&))); + m_activeTools.insert(pObjTool->getNOT(), pObjTool); return true; } else if (_isSame(sAction, "nmInsertObjPointWell")) { nmObjToolBase* pObjTool = new nmObjPointWellTool(); @@ -169,6 +170,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) { pPlot->appendTools(pObjTool); connect(pObjTool, SIGNAL(sigPtsFinished(QVector&)), \ this, SLOT(slotObjPtsFinished(QVector&))); + m_activeTools.insert(pObjTool->getNOT(), pObjTool); return true; } else if (_isSame(sAction, "nmInsertObjLine")) { nmObjToolBase* pObjTool = new nmObjLineTool(); @@ -176,6 +178,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) { pPlot->appendTools(pObjTool); connect(pObjTool, SIGNAL(sigPtsFinished(QVector&)), \ this, SLOT(slotObjPtsFinished(QVector&))); + m_activeTools.insert(pObjTool->getNOT(), pObjTool); return true; } else if (_isSame(sAction, "nmInsertObjPolygon")) { nmObjToolBase* pObjTool = new nmObjPolygonTool(); @@ -183,6 +186,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) { pPlot->appendTools(pObjTool); connect(pObjTool, SIGNAL(sigPtsFinished(QVector&)), \ this, SLOT(slotObjPtsFinished(QVector&))); + m_activeTools.insert(pObjTool->getNOT(), pObjTool); return true; } else if (_isSame(sAction, "nmInsertObjPolygonOutline")) { nmObjToolBase* pObjTool = new nmObjPolygonOutlineTool(); @@ -190,6 +194,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) { pPlot->appendTools(pObjTool); connect(pObjTool, SIGNAL(sigPtsFinished(QVector&)), \ this, SLOT(slotObjPtsFinished(QVector&))); + m_activeTools.insert(pObjTool->getNOT(), pObjTool); return true; } else if (_isSame(sAction, "nmInsertObjRect")) { nmObjToolBase* pObjTool = new nmObjRectTool(); //矩形对象工具类 @@ -197,6 +202,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) { pPlot->appendTools(pObjTool); connect(pObjTool, SIGNAL(sigPtsFinished(QVector&)), \ this, SLOT(slotObjPtsFinished(QVector&))); + m_activeTools.insert(pObjTool->getNOT(), pObjTool); return true; } else if (_isSame(sAction, "nmInsertObjRectOutline")) { nmObjToolBase* pObjTool = new nmObjRectOutlineTool(); //矩形边界工具类 @@ -204,6 +210,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) { pPlot->appendTools(pObjTool); connect(pObjTool, SIGNAL(sigPtsFinished(QVector&)), \ this, SLOT(slotObjPtsFinished(QVector&))); + m_activeTools.insert(pObjTool->getNOT(), pObjTool); return true; } else if (_isSame(sAction, "nmInsertObjRound")) { nmObjToolBase* pObjTool = new nmObjRoundTool(); //圆形对象工具类 @@ -211,6 +218,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) { pPlot->appendTools(pObjTool); connect(pObjTool, SIGNAL(sigPtsFinished(QVector&)), \ this, SLOT(slotObjPtsFinished(QVector&))); + m_activeTools.insert(pObjTool->getNOT(), pObjTool); return true; } else if (_isSame(sAction, "nmInsertObjRoundOutline")) { nmObjToolBase* pObjTool = new nmObjRoundOutlineTool(); //圆形边界工具类 @@ -218,6 +226,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) { pPlot->appendTools(pObjTool); connect(pObjTool, SIGNAL(sigPtsFinished(QVector&)), \ this, SLOT(slotObjPtsFinished(QVector&))); + m_activeTools.insert(pObjTool->getNOT(), pObjTool); return true; } else if (_isSame(sAction, "nmInsertObjLineCrack")) { nmObjToolBase* pObjTool = new nmObjLineCrackTool(); @@ -298,6 +307,21 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) { return iGuiPlotCmdHelper::runAction(sAction); } +void nmGuiPlotCmdHelper::cancelActiveTools() +{ + nmGuiPlot* pWxPlot = dynamic_cast(m_pGuiPlot); + if(pWxPlot == nullptr || pWxPlot->m_pPlot == nullptr || + pWxPlot->m_pPlotView == nullptr) { + return; + } + + foreach (nmObjToolBase* activeTool, m_activeTools.values()) { + pWxPlot->m_pPlot->removeTools(activeTool); + } + m_activeTools.clear(); + finalWithAcm(true, true); +} + void nmGuiPlotCmdHelper::slotObjPtsFinished(QVector& vec) { nmObjToolBase* pTool = dynamic_cast(sender()); if (NULL == pTool) { diff --git a/Src/nmNum/nmSubWnd/nmSubWndMain.cpp b/Src/nmNum/nmSubWnd/nmSubWndMain.cpp index df48794..1f2b236 100644 --- a/Src/nmNum/nmSubWnd/nmSubWndMain.cpp +++ b/Src/nmNum/nmSubWnd/nmSubWndMain.cpp @@ -1446,7 +1446,8 @@ QWidget* nmSubWndMain::showPointerPosDialog() QWidget* nmSubWndMain::showPropertyInterpolationDialog() { - nmWxPropertyInterpolationDlg* interpolationDlg = new nmWxPropertyInterpolationDlg(this); + nmWxPropertyInterpolationDlg* interpolationDlg = new nmWxPropertyInterpolationDlg( + m_pWxPlot, nmDataAnalyzeManager::getCurrentInstance(), this); markDialogCmdOpened(5126); interpolationDlg->setObjectName("nmPropertyInterpolationDialog"); // 关闭窗口时自动释放,并恢复菜单命令的可用状态 diff --git a/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.cpp b/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.cpp index 950be6c..ac076ff 100644 --- a/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.cpp +++ b/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.cpp @@ -1,28 +1,43 @@ #include "nmWxPropertyInterpolationDlg.h" #include +#include +#include #include #include #include +#include #include #include #include #include +#include #include #include +#include +#include #include +#include #include +#include +#include #include +#include "nmGuiPlot.h" +#include "nmObjPoint.h" +#include "tCurvePlotView.h" +#include "ZxObjText.h" +#include "ZxPlot.h" + namespace { -// 使用独立翻译上下文,便于统一维护对话框文本 +// 使用独立翻译上下文,便于统一维护对话框文本. QString interpolationText(const char* sourceText) { return QCoreApplication::translate("nmWxPropertyInterpolationDlg", sourceText); } -// 创建统一格式的非负数值输入框 +// 创建统一格式的非负数值输入框. QDoubleSpinBox* createNumberSpinBox(QWidget* parent, double value) { QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent); @@ -32,10 +47,82 @@ QDoubleSpinBox* createNumberSpinBox(QWidget* parent, double value) spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); return spinBox; } + +// 插值测点使用独立的小方块标记,避免与井图元混淆. +class PropertyInterpolationMarker : public nmObjPoint +{ +public: + PropertyInterpolationMarker(const QString& name, + ZxSubAxisX* axisX, + ZxSubAxisY* axisY) + : nmObjPoint(name, axisX, axisY) + { + setDotRadius(1.0); + } + + virtual bool drawWellPos(QPainter* painter, QPointF point) override + { + if(painter == NULL) { + return false; + } + + const QTransform transform = painter->combinedTransform(); + qreal scaleX = qAbs(transform.m11()); + qreal scaleY = qAbs(transform.m22()); + + if(scaleX < 0.0001) { + scaleX = 1.0; + } + + if(scaleY < 0.0001) { + scaleY = 1.0; + } + + const qreal halfPixelSize = 3.0; + QRectF markerRect(point.x() - halfPixelSize / scaleX, + point.y() - halfPixelSize / scaleY, + halfPixelSize * 2.0 / scaleX, + halfPixelSize * 2.0 / scaleY); + + painter->save(); + QPen pen(QColor(0, 25, 100)); + pen.setWidthF(1.0); + pen.setCosmetic(true); + painter->setPen(pen); + painter->setBrush(QColor(0, 70, 200)); + painter->drawRect(markerRect); + painter->restore(); + return true; + } +}; + +void setMarkerLabel(nmObjPoint* marker, const QString& text) +{ + if(marker == NULL || marker->getChildren().isEmpty()) { + return; + } + + ZxObjText* label = dynamic_cast(marker->at(0)); + if(label != NULL) { + label->setText(text); + label->setTextColor(QColor(30, 30, 30)); + } +} } -nmWxPropertyInterpolationDlg::nmWxPropertyInterpolationDlg(QWidget* parent) +nmWxPropertyInterpolationDlg::nmWxPropertyInterpolationDlg( + nmGuiPlot* plot, + nmDataAnalyzeManager* dataManager, + QWidget* parent) : iDlgBase(parent), + m_pPlot(plot), + m_pDataManager(dataManager), + m_pDataSetCombo(NULL), + m_pAddDataSetButton(NULL), + m_pDeleteDataSetButton(NULL), + m_nCurrentDataSetIndex(-1), + m_nNextPointId(0), + m_bUpdatingUi(true), m_pPropertyCombo(NULL), m_pNameEdit(NULL), m_pNewValueSpin(NULL), @@ -43,6 +130,8 @@ nmWxPropertyInterpolationDlg::nmWxPropertyInterpolationDlg(QWidget* parent) m_pPointTable(NULL), m_pDeletePointButton(NULL), m_pClearPointsButton(NULL), + m_pShowPointsCheck(NULL), + m_pShowLabelsCheck(NULL), m_pNuggetSpin(NULL), m_pSillSpin(NULL), m_pRangeSpin(NULL), @@ -56,25 +145,66 @@ nmWxPropertyInterpolationDlg::nmWxPropertyInterpolationDlg(QWidget* parent) setMinimumSize(520, 620); resize(560, 680); initInterpolationUI(); + initializeDataSets(); + + if(qApp != NULL) { + qApp->installEventFilter(this); + } +} + +nmWxPropertyInterpolationDlg::~nmWxPropertyInterpolationDlg() +{ + if(qApp != NULL) { + qApp->removeEventFilter(this); + } + + onMapPickToggled(false); + saveCurrentDataSet(); + syncDataSetsToManager(false); + removeAllMarkers(); } void nmWxPropertyInterpolationDlg::initInterpolationUI() { QVBoxLayout* mainLayout = new QVBoxLayout(this); - // 插值属性及名称设置 + // 数据组创建、切换及删除. + QHBoxLayout* dataSetSelectLayout = new QHBoxLayout; + dataSetSelectLayout->addWidget(new QLabel(interpolationText("Dataset:"), this)); + m_pDataSetCombo = new QComboBox(this); + dataSetSelectLayout->addWidget(m_pDataSetCombo, 1); + + QString iconDir = QCoreApplication::applicationDirPath(); + iconDir = iconDir.section('/', 0, -2) + "/Res/Icon/"; + + m_pAddDataSetButton = new QPushButton(this); + m_pAddDataSetButton->setIcon(QIcon(iconDir + "Add.png")); + m_pAddDataSetButton->setIconSize(QSize(18, 18)); + m_pAddDataSetButton->setFixedSize(28, 28); + m_pAddDataSetButton->setToolTip(interpolationText("Add dataset")); + dataSetSelectLayout->addWidget(m_pAddDataSetButton); + + m_pDeleteDataSetButton = new QPushButton(this); + m_pDeleteDataSetButton->setIcon(QIcon(iconDir + "NmDelete.png")); + m_pDeleteDataSetButton->setIconSize(QSize(18, 18)); + m_pDeleteDataSetButton->setFixedSize(28, 28); + m_pDeleteDataSetButton->setToolTip(interpolationText("Delete dataset")); + dataSetSelectLayout->addWidget(m_pDeleteDataSetButton); + mainLayout->addLayout(dataSetSelectLayout); + + // 当前数据组的名称和属性. QFormLayout* dataSetLayout = new QFormLayout; + m_pNameEdit = new QLineEdit(this); + dataSetLayout->addRow(interpolationText("Name:"), m_pNameEdit); + m_pPropertyCombo = new QComboBox(this); m_pPropertyCombo->addItem(interpolationText("Permeability k"), QString("k")); m_pPropertyCombo->addItem(interpolationText("Porosity phi"), QString("phi")); m_pPropertyCombo->addItem(interpolationText("Reservoir thickness h"), QString("h")); dataSetLayout->addRow(interpolationText("Property:"), m_pPropertyCombo); - - m_pNameEdit = new QLineEdit(interpolationText("Permeability"), this); - dataSetLayout->addRow(interpolationText("Name:"), m_pNameEdit); mainLayout->addLayout(dataSetLayout); - // 测点数据录入区域 + // 测点数据录入区域. QGroupBox* pointGroup = new QGroupBox(interpolationText("Measurement Points"), this); QVBoxLayout* pointLayout = new QVBoxLayout(pointGroup); @@ -84,12 +214,12 @@ void nmWxPropertyInterpolationDlg::initInterpolationUI() newPointLayout->addWidget(m_pNewValueSpin, 1); m_pMapPickButton = new QPushButton(interpolationText("Select on Map"), pointGroup); m_pMapPickButton->setCheckable(true); - // 地图选点功能将在后续阶段接入 - m_pMapPickButton->setEnabled(false); + m_pMapPickButton->setEnabled(!m_pPlot.isNull() && + m_pPlot->m_pPlotView != NULL && + m_pPlot->m_pPlot != NULL); newPointLayout->addWidget(m_pMapPickButton); pointLayout->addLayout(newPointLayout); - // 每个测点保存坐标及对应的属性值 m_pPointTable = new QTableWidget(0, 3, pointGroup); QStringList headers; headers << interpolationText("X") << interpolationText("Y") << interpolationText("Value"); @@ -100,7 +230,6 @@ void nmWxPropertyInterpolationDlg::initInterpolationUI() m_pPointTable->setAlternatingRowColors(true); pointLayout->addWidget(m_pPointTable, 1); - // 测点列表管理按钮 QHBoxLayout* pointButtonLayout = new QHBoxLayout; pointButtonLayout->addStretch(); m_pDeletePointButton = new QPushButton(interpolationText("Delete Selected"), pointGroup); @@ -110,9 +239,19 @@ void nmWxPropertyInterpolationDlg::initInterpolationUI() m_pClearPointsButton->setEnabled(false); pointButtonLayout->addWidget(m_pClearPointsButton); pointLayout->addLayout(pointButtonLayout); + + QHBoxLayout* displayLayout = new QHBoxLayout; + m_pShowPointsCheck = new QCheckBox(interpolationText("Show measurement points"), pointGroup); + m_pShowPointsCheck->setChecked(true); + displayLayout->addWidget(m_pShowPointsCheck); + m_pShowLabelsCheck = new QCheckBox(interpolationText("Show labels"), pointGroup); + m_pShowLabelsCheck->setChecked(true); + displayLayout->addWidget(m_pShowLabelsCheck); + displayLayout->addStretch(); + pointLayout->addLayout(displayLayout); mainLayout->addWidget(pointGroup, 1); - // Kriging插值参数设置 + // Kriging插值参数设置. QGroupBox* parameterGroup = new QGroupBox(interpolationText("Kriging Parameters"), this); QFormLayout* parameterLayout = new QFormLayout(parameterGroup); m_pNuggetSpin = createNumberSpinBox(parameterGroup, 0.01); @@ -128,10 +267,9 @@ void nmWxPropertyInterpolationDlg::initInterpolationUI() parameterLayout->addRow(interpolationText("Model:"), m_pModelCombo); mainLayout->addWidget(parameterGroup); - // 底部操作按钮 + // 底部操作按钮. QHBoxLayout* buttonLayout = new QHBoxLayout; m_pPreviewButton = new QPushButton(interpolationText("Preview"), this); - // 插值计算和预览功能将在后续阶段接入 m_pPreviewButton->setEnabled(false); buttonLayout->addWidget(m_pPreviewButton); buttonLayout->addStretch(); @@ -142,5 +280,640 @@ void nmWxPropertyInterpolationDlg::initInterpolationUI() buttonLayout->addWidget(closeButton); mainLayout->addLayout(buttonLayout); + connect(m_pDataSetCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(onDataSetChanged(int))); + connect(m_pAddDataSetButton, SIGNAL(clicked()), this, SLOT(onAddDataSet())); + connect(m_pDeleteDataSetButton, SIGNAL(clicked()), this, SLOT(onDeleteDataSet())); + connect(m_pNameEdit, SIGNAL(textChanged(QString)), + this, SLOT(onDataSetNameChanged(QString))); + connect(m_pPropertyCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(onCurrentDataSetEdited())); + connect(m_pNuggetSpin, SIGNAL(valueChanged(double)), + this, SLOT(onCurrentDataSetEdited())); + connect(m_pSillSpin, SIGNAL(valueChanged(double)), + this, SLOT(onCurrentDataSetEdited())); + connect(m_pRangeSpin, SIGNAL(valueChanged(double)), + this, SLOT(onCurrentDataSetEdited())); + connect(m_pModelCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(onCurrentDataSetEdited())); + connect(m_pMapPickButton, SIGNAL(toggled(bool)), this, SLOT(onMapPickToggled(bool))); + connect(m_pPointTable, SIGNAL(itemSelectionChanged()), + this, SLOT(onPointSelectionChanged())); + connect(m_pPointTable, SIGNAL(itemChanged(QTableWidgetItem*)), + this, SLOT(onPointItemChanged(QTableWidgetItem*))); + connect(m_pDeletePointButton, SIGNAL(clicked()), this, SLOT(onDeleteSelectedPoint())); + connect(m_pClearPointsButton, SIGNAL(clicked()), this, SLOT(onClearPoints())); + connect(m_pShowPointsCheck, SIGNAL(toggled(bool)), this, SLOT(onShowPointsToggled(bool))); + connect(m_pShowLabelsCheck, SIGNAL(toggled(bool)), this, SLOT(onShowLabelsToggled(bool))); connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); } + +void nmWxPropertyInterpolationDlg::initializeDataSets() +{ + m_bUpdatingUi = true; + + if(!m_pDataManager.isNull()) { + m_vecDataSets = m_pDataManager->getPropertyInterpolationDataSets(); + } + + bool dataChanged = false; + for(int i = 0; i < m_vecDataSets.size(); ++i) { + if(m_vecDataSets[i].name.isEmpty()) { + m_vecDataSets[i].name = nextDataSetName(); + dataChanged = true; + } + } + + m_vecMarkerNames.resize(m_vecDataSets.size()); + createMarkersForAllDataSets(); + + bool oldBlock = m_pDataSetCombo->blockSignals(true); + m_pDataSetCombo->clear(); + for(int i = 0; i < m_vecDataSets.size(); ++i) { + m_pDataSetCombo->addItem(m_vecDataSets[i].name); + } + const int initialIndex = m_vecDataSets.isEmpty() ? -1 : 0; + m_pDataSetCombo->setCurrentIndex(initialIndex); + m_pDataSetCombo->blockSignals(oldBlock); + + m_nCurrentDataSetIndex = initialIndex; + loadDataSet(initialIndex); + m_bUpdatingUi = false; + updateDataSetButtons(); + + if(dataChanged) { + syncDataSetsToManager(false); + } +} + +nmPropertyInterpolationDataSet nmWxPropertyInterpolationDlg::createDefaultDataSet() +{ + nmPropertyInterpolationDataSet dataSet; + dataSet.name = nextDataSetName(); + dataSet.property = "k"; + dataSet.showPoints = true; + dataSet.showLabels = true; + dataSet.nugget = 0.01; + dataSet.sill = 100.0; + dataSet.range = 1000.0; + dataSet.model = 0; + return dataSet; +} + +QString nmWxPropertyInterpolationDlg::nextDataSetName() +{ + for(int number = 1; ; ++number) { + const QString name = interpolationText("Dataset #%1").arg(number); + bool exists = false; + for(int i = 0; i < m_vecDataSets.size(); ++i) { + if(m_vecDataSets[i].name == name) { + exists = true; + break; + } + } + + if(!exists) { + return name; + } + } +} + +void nmWxPropertyInterpolationDlg::loadDataSet(int index) +{ + if(index < 0 || index >= m_vecDataSets.size()) { + clearCurrentDataSet(); + return; + } + + bool wasUpdating = m_bUpdatingUi; + m_bUpdatingUi = true; + const nmPropertyInterpolationDataSet& dataSet = m_vecDataSets[index]; + + m_pNameEdit->setText(dataSet.name); + int propertyIndex = m_pPropertyCombo->findData(dataSet.property); + m_pPropertyCombo->setCurrentIndex(propertyIndex < 0 ? 0 : propertyIndex); + m_pNuggetSpin->setValue(dataSet.nugget); + m_pSillSpin->setValue(dataSet.sill); + m_pRangeSpin->setValue(dataSet.range); + int modelIndex = m_pModelCombo->findData(dataSet.model); + m_pModelCombo->setCurrentIndex(modelIndex < 0 ? 0 : modelIndex); + m_pShowPointsCheck->setChecked(dataSet.showPoints); + m_pShowLabelsCheck->setChecked(dataSet.showLabels); + + bool oldTableBlock = m_pPointTable->blockSignals(true); + m_pPointTable->setRowCount(0); + const QStringList markerNames = m_vecMarkerNames.value(index); + for(int i = 0; i < dataSet.points.size(); ++i) { + appendPointRow(dataSet.points[i], markerNames.value(i)); + } + m_pPointTable->blockSignals(oldTableBlock); + + clearTableSelection(); + updatePointButtons(); + updateCurrentMarkerVisibility(); + m_bUpdatingUi = wasUpdating; + updateDataSetButtons(); +} + +void nmWxPropertyInterpolationDlg::clearCurrentDataSet() +{ + bool wasUpdating = m_bUpdatingUi; + m_bUpdatingUi = true; + + if(m_pMapPickButton->isChecked()) { + m_pMapPickButton->setChecked(false); + } + m_pNameEdit->clear(); + m_pPropertyCombo->setCurrentIndex(-1); + m_pNewValueSpin->setValue(0.0); + m_pPointTable->setRowCount(0); + m_pShowPointsCheck->setChecked(false); + m_pShowLabelsCheck->setChecked(false); + m_pNuggetSpin->setValue(0.01); + m_pSillSpin->setValue(100.0); + m_pRangeSpin->setValue(1000.0); + m_pModelCombo->setCurrentIndex(-1); + + clearTableSelection(); + m_bUpdatingUi = wasUpdating; + updateDataSetButtons(); +} + +void nmWxPropertyInterpolationDlg::saveCurrentDataSet() +{ + if(m_nCurrentDataSetIndex < 0 || + m_nCurrentDataSetIndex >= m_vecDataSets.size()) { + return; + } + + nmPropertyInterpolationDataSet& dataSet = m_vecDataSets[m_nCurrentDataSetIndex]; + dataSet.name = m_pNameEdit->text(); + dataSet.property = m_pPropertyCombo->itemData(m_pPropertyCombo->currentIndex()).toString(); + dataSet.nugget = m_pNuggetSpin->value(); + dataSet.sill = m_pSillSpin->value(); + dataSet.range = m_pRangeSpin->value(); + dataSet.model = m_pModelCombo->itemData(m_pModelCombo->currentIndex()).toInt(); + dataSet.showPoints = m_pShowPointsCheck->isChecked(); + dataSet.showLabels = m_pShowLabelsCheck->isChecked(); + +} + +void nmWxPropertyInterpolationDlg::syncDataSetsToManager(bool markModified) +{ + if(!m_pDataManager.isNull()) { + m_pDataManager->setPropertyInterpolationDataSets(m_vecDataSets); + } + + if(markModified && !m_pPlot.isNull()) { + m_pPlot->setModified(true); + } +} + +void nmWxPropertyInterpolationDlg::updateDataSetButtons() +{ + const bool hasCurrent = m_nCurrentDataSetIndex >= 0 && + m_nCurrentDataSetIndex < m_vecDataSets.size(); + const bool canPickOnMap = hasCurrent && !m_pPlot.isNull() && + m_pPlot->m_pPlotView != NULL && m_pPlot->m_pPlot != NULL; + + m_pDataSetCombo->setEnabled(hasCurrent); + m_pDeleteDataSetButton->setEnabled(hasCurrent); + m_pNameEdit->setEnabled(hasCurrent); + m_pPropertyCombo->setEnabled(hasCurrent); + m_pNewValueSpin->setEnabled(hasCurrent); + m_pMapPickButton->setEnabled(canPickOnMap); + m_pPointTable->setEnabled(hasCurrent); + m_pShowPointsCheck->setEnabled(hasCurrent); + m_pShowLabelsCheck->setEnabled(hasCurrent); + m_pNuggetSpin->setEnabled(hasCurrent); + m_pSillSpin->setEnabled(hasCurrent); + m_pRangeSpin->setEnabled(hasCurrent); + m_pModelCombo->setEnabled(hasCurrent); + updatePointButtons(); +} + +void nmWxPropertyInterpolationDlg::onDataSetChanged(int index) +{ + if(m_bUpdatingUi || index < 0 || index >= m_vecDataSets.size() || + index == m_nCurrentDataSetIndex) { + return; + } + + saveCurrentDataSet(); + syncDataSetsToManager(false); + m_nCurrentDataSetIndex = index; + loadDataSet(index); +} + +void nmWxPropertyInterpolationDlg::onAddDataSet() +{ + saveCurrentDataSet(); + m_vecDataSets.append(createDefaultDataSet()); + m_vecMarkerNames.append(QStringList()); + + int newIndex = m_vecDataSets.size() - 1; + bool oldBlock = m_pDataSetCombo->blockSignals(true); + m_pDataSetCombo->addItem(m_vecDataSets[newIndex].name); + m_pDataSetCombo->setCurrentIndex(newIndex); + m_pDataSetCombo->blockSignals(oldBlock); + + m_nCurrentDataSetIndex = newIndex; + loadDataSet(newIndex); + updateDataSetButtons(); + syncDataSetsToManager(true); +} + +void nmWxPropertyInterpolationDlg::onDeleteDataSet() +{ + if(m_nCurrentDataSetIndex < 0 || + m_nCurrentDataSetIndex >= m_vecDataSets.size()) { + return; + } + + int removedIndex = m_nCurrentDataSetIndex; + removeDataSetMarkers(removedIndex); + m_vecDataSets.remove(removedIndex); + m_vecMarkerNames.remove(removedIndex); + + const int newIndex = m_vecDataSets.isEmpty() ? -1 : + qMin(removedIndex, m_vecDataSets.size() - 1); + bool oldBlock = m_pDataSetCombo->blockSignals(true); + m_pDataSetCombo->removeItem(removedIndex); + m_pDataSetCombo->setCurrentIndex(newIndex); + m_pDataSetCombo->blockSignals(oldBlock); + + m_nCurrentDataSetIndex = newIndex; + loadDataSet(newIndex); + updateDataSetButtons(); + syncDataSetsToManager(true); +} + +void nmWxPropertyInterpolationDlg::onDataSetNameChanged(const QString& name) +{ + if(m_bUpdatingUi || m_nCurrentDataSetIndex < 0 || + m_nCurrentDataSetIndex >= m_vecDataSets.size()) { + return; + } + + m_vecDataSets[m_nCurrentDataSetIndex].name = name; + m_pDataSetCombo->setItemText(m_nCurrentDataSetIndex, name); + syncDataSetsToManager(true); +} + +void nmWxPropertyInterpolationDlg::onCurrentDataSetEdited() +{ + if(m_bUpdatingUi) { + return; + } + + saveCurrentDataSet(); + syncDataSetsToManager(true); +} + +bool nmWxPropertyInterpolationDlg::eventFilter(QObject* watched, QEvent* event) +{ + if(event != NULL && event->type() == QEvent::MouseButtonPress && m_pPointTable != NULL) { + QWidget* clickedWidget = qobject_cast(watched); + + if(clickedWidget != NULL) { + bool isTableViewport = clickedWidget == m_pPointTable->viewport() || + m_pPointTable->viewport()->isAncestorOf(clickedWidget); + + if(clickedWidget == m_pPointTable->viewport()) { + QMouseEvent* mouseEvent = static_cast(event); + + if(m_pPointTable->itemAt(mouseEvent->pos()) == NULL) { + clearTableSelection(); + } + } else if(!isTableViewport && clickedWidget != m_pDeletePointButton) { + clearTableSelection(); + } + } + } + + return iDlgBase::eventFilter(watched, event); +} + +void nmWxPropertyInterpolationDlg::onMapPickToggled(bool checked) +{ + if(m_pPlot.isNull() || m_pPlot->m_pPlotView == NULL) { + return; + } + + disconnect(m_pPlot->m_pPlotView, SIGNAL(sigLeftClick(QPointF)), + this, SLOT(onMapClicked(QPointF))); + + if(checked) { + m_pPlot->cancelActiveTools(); + connect(m_pPlot->m_pPlotView, SIGNAL(sigLeftClick(QPointF)), + this, SLOT(onMapClicked(QPointF))); + m_pPlot->m_pPlotView->setCursor(Qt::CrossCursor); + } else { + m_pPlot->m_pPlotView->restoreCursor(); + } +} + +void nmWxPropertyInterpolationDlg::onMapClicked(const QPointF& position) +{ + if(!m_pMapPickButton->isChecked() || m_pPlot.isNull() || + m_pPlot->m_pPlot == NULL || m_nCurrentDataSetIndex < 0) { + return; + } + + if(!m_pPlot->m_pPlot->getInnerRectF().contains(position)) { + return; + } + + QPointF plotPosition = position; + QPointF valuePoint = m_pPlot->m_pPlot->getValueForPos(plotPosition); + appendPoint(valuePoint); +} + +void nmWxPropertyInterpolationDlg::appendPoint(const QPointF& valuePoint) +{ + if(m_nCurrentDataSetIndex < 0 || + m_nCurrentDataSetIndex >= m_vecDataSets.size()) { + return; + } + + nmPropertyInterpolationDataSet& dataSet = m_vecDataSets[m_nCurrentDataSetIndex]; + double pointValue = m_pNewValueSpin->value(); + nmPropertyInterpolationPointData point(valuePoint.x(), valuePoint.y(), pointValue); + QString markerName = createMarker(valuePoint, pointValue, + dataSet.showPoints, dataSet.showLabels); + + dataSet.points.append(point); + m_vecMarkerNames[m_nCurrentDataSetIndex].append(markerName); + + bool oldBlock = m_pPointTable->blockSignals(true); + appendPointRow(point, markerName); + m_pPointTable->blockSignals(oldBlock); + m_pPointTable->scrollToBottom(); + updatePointButtons(); + syncDataSetsToManager(true); +} + +void nmWxPropertyInterpolationDlg::appendPointRow( + const nmPropertyInterpolationPointData& point, + const QString& markerName) +{ + int row = m_pPointTable->rowCount(); + m_pPointTable->insertRow(row); + + QTableWidgetItem* xItem = new QTableWidgetItem(QString::number(point.x, 'g', 15)); + xItem->setFlags(xItem->flags() & ~Qt::ItemIsEditable); + xItem->setData(Qt::UserRole, markerName); + m_pPointTable->setItem(row, 0, xItem); + + QTableWidgetItem* yItem = new QTableWidgetItem(QString::number(point.y, 'g', 15)); + yItem->setFlags(yItem->flags() & ~Qt::ItemIsEditable); + m_pPointTable->setItem(row, 1, yItem); + + m_pPointTable->setItem(row, 2, + new QTableWidgetItem(QString::number(point.value, 'f', 6))); +} + +QString nmWxPropertyInterpolationDlg::createMarker(const QPointF& valuePosition, + double value, + bool showPoints, + bool showLabels) +{ + if(m_pPlot.isNull() || m_pPlot->m_pPlot == NULL) { + return QString(); + } + + QString markerName; + do { + markerName = QString("__PropertyInterpolationPoint_%1").arg(++m_nNextPointId); + } while(m_pPlot->m_pPlot->getObjByName(markerName) != NULL); + + bool wasModified = m_pPlot->isModified(); + PropertyInterpolationMarker* marker = new PropertyInterpolationMarker( + markerName, + m_pPlot->m_pPlot->getMainAxisX(), + m_pPlot->m_pPlot->getMainAxisY()); + m_pPlot->bindObjSignals(marker); + + if(!m_pPlot->m_pPlot->addOneObj(marker)) { + delete marker; + m_pPlot->setModified(wasModified); + return QString(); + } + + QVector values; + values.append(valuePosition); + marker->setAllValues(values); + m_pPlot->slotObjCompleted(marker); + setMarkerLabel(marker, QString::number(value, 'g', 10)); + marker->setReadOnly(true); + marker->setLockPos(true); + marker->showSubObjs(showPoints && showLabels); + marker->setVisible(showPoints); + marker->deselect(true); + marker->update(); + m_pPlot->setModified(wasModified); + return markerName; +} + +void nmWxPropertyInterpolationDlg::createMarkersForAllDataSets() +{ + if(m_pPlot.isNull() || m_pPlot->m_pPlot == NULL) { + return; + } + + for(int dataSetIndex = 0; dataSetIndex < m_vecDataSets.size(); ++dataSetIndex) { + const nmPropertyInterpolationDataSet& dataSet = m_vecDataSets[dataSetIndex]; + QStringList markerNames; + + for(int pointIndex = 0; pointIndex < dataSet.points.size(); ++pointIndex) { + const nmPropertyInterpolationPointData& point = dataSet.points[pointIndex]; + QPointF valuePosition(point.x, point.y); + markerNames.append(createMarker(valuePosition, point.value, + dataSet.showPoints, dataSet.showLabels)); + } + + m_vecMarkerNames[dataSetIndex] = markerNames; + } +} + +void nmWxPropertyInterpolationDlg::removeDataSetMarkers(int dataSetIndex) +{ + if(dataSetIndex < 0 || dataSetIndex >= m_vecMarkerNames.size()) { + return; + } + + const QStringList markerNames = m_vecMarkerNames[dataSetIndex]; + for(int i = 0; i < markerNames.size(); ++i) { + removeMarkerByName(markerNames[i]); + } + m_vecMarkerNames[dataSetIndex].clear(); +} + +void nmWxPropertyInterpolationDlg::removeAllMarkers() +{ + for(int i = 0; i < m_vecMarkerNames.size(); ++i) { + removeDataSetMarkers(i); + } + m_vecMarkerNames.clear(); +} + +QString nmWxPropertyInterpolationDlg::markerNameAt(int row) const +{ + if(m_pPointTable == NULL || row < 0 || row >= m_pPointTable->rowCount()) { + return QString(); + } + + QTableWidgetItem* item = m_pPointTable->item(row, 0); + return item == NULL ? QString() : item->data(Qt::UserRole).toString(); +} + +nmObjPoint* nmWxPropertyInterpolationDlg::markerByName(const QString& markerName) const +{ + if(m_pPlot.isNull() || m_pPlot->m_pPlot == NULL || markerName.isEmpty()) { + return NULL; + } + + return dynamic_cast(m_pPlot->m_pPlot->getObjByName(markerName)); +} + +nmObjPoint* nmWxPropertyInterpolationDlg::markerAt(int row) const +{ + return markerByName(markerNameAt(row)); +} + +void nmWxPropertyInterpolationDlg::removeMarkerByName(const QString& markerName) +{ + if(m_pPlot.isNull() || m_pPlot->m_pPlot == NULL || markerName.isEmpty() || + m_pPlot->m_pPlot->getObjByName(markerName) == NULL) { + return; + } + + bool wasModified = m_pPlot->isModified(); + m_pPlot->m_pPlot->removeObjByName(markerName); + m_pPlot->setModified(wasModified); +} + +void nmWxPropertyInterpolationDlg::onPointSelectionChanged() +{ + updatePointButtons(); +} + +void nmWxPropertyInterpolationDlg::onPointItemChanged(QTableWidgetItem* item) +{ + if(m_bUpdatingUi || item == NULL || item->column() != 2 || + m_nCurrentDataSetIndex < 0 || + m_nCurrentDataSetIndex >= m_vecDataSets.size() || + item->row() >= m_vecDataSets[m_nCurrentDataSetIndex].points.size()) { + return; + } + + m_vecDataSets[m_nCurrentDataSetIndex].points[item->row()].value = item->text().toDouble(); + nmObjPoint* marker = markerAt(item->row()); + if(marker != NULL) { + setMarkerLabel(marker, item->text()); + marker->update(); + } + syncDataSetsToManager(true); +} + +void nmWxPropertyInterpolationDlg::onDeleteSelectedPoint() +{ + int row = m_pPointTable->currentRow(); + if(row < 0 || m_nCurrentDataSetIndex < 0 || + m_nCurrentDataSetIndex >= m_vecDataSets.size()) { + return; + } + + removeMarkerByName(markerNameAt(row)); + if(row < m_vecDataSets[m_nCurrentDataSetIndex].points.size()) { + m_vecDataSets[m_nCurrentDataSetIndex].points.remove(row); + } + if(row < m_vecMarkerNames[m_nCurrentDataSetIndex].size()) { + m_vecMarkerNames[m_nCurrentDataSetIndex].removeAt(row); + } + m_pPointTable->removeRow(row); + clearTableSelection(); + updatePointButtons(); + syncDataSetsToManager(true); +} + +void nmWxPropertyInterpolationDlg::onClearPoints() +{ + if(m_nCurrentDataSetIndex < 0 || + m_nCurrentDataSetIndex >= m_vecDataSets.size()) { + return; + } + + removeDataSetMarkers(m_nCurrentDataSetIndex); + m_vecDataSets[m_nCurrentDataSetIndex].points.clear(); + m_pPointTable->setRowCount(0); + clearTableSelection(); + updatePointButtons(); + syncDataSetsToManager(true); +} + +void nmWxPropertyInterpolationDlg::onShowPointsToggled(bool checked) +{ + if(m_bUpdatingUi || m_nCurrentDataSetIndex < 0 || + m_nCurrentDataSetIndex >= m_vecDataSets.size()) { + return; + } + + m_vecDataSets[m_nCurrentDataSetIndex].showPoints = checked; + updateCurrentMarkerVisibility(); + syncDataSetsToManager(true); +} + +void nmWxPropertyInterpolationDlg::onShowLabelsToggled(bool checked) +{ + if(m_bUpdatingUi || m_nCurrentDataSetIndex < 0 || + m_nCurrentDataSetIndex >= m_vecDataSets.size()) { + return; + } + + m_vecDataSets[m_nCurrentDataSetIndex].showLabels = checked; + updateCurrentMarkerVisibility(); + syncDataSetsToManager(true); +} + +void nmWxPropertyInterpolationDlg::clearTableSelection() +{ + m_pPointTable->clearSelection(); + m_pPointTable->setCurrentCell(-1, -1); +} + +void nmWxPropertyInterpolationDlg::updatePointButtons() +{ + const bool hasCurrent = m_nCurrentDataSetIndex >= 0 && + m_nCurrentDataSetIndex < m_vecDataSets.size(); + m_pDeletePointButton->setEnabled(hasCurrent && m_pPointTable->currentRow() >= 0); + m_pClearPointsButton->setEnabled(hasCurrent && m_pPointTable->rowCount() > 0); +} + +void nmWxPropertyInterpolationDlg::updateCurrentMarkerVisibility() +{ + // 地图上只显示当前数据组的测点,其他数据组统一隐藏. + for(int dataSetIndex = 0; + dataSetIndex < m_vecMarkerNames.size(); ++dataSetIndex) { + const bool isCurrentDataSet = dataSetIndex == m_nCurrentDataSetIndex && + dataSetIndex < m_vecDataSets.size(); + const bool showPoints = isCurrentDataSet && + m_vecDataSets[dataSetIndex].showPoints; + const bool showLabels = showPoints && + m_vecDataSets[dataSetIndex].showLabels; + const QStringList markerNames = m_vecMarkerNames[dataSetIndex]; + + for(int markerIndex = 0; markerIndex < markerNames.size(); ++markerIndex) { + nmObjPoint* marker = markerByName(markerNames[markerIndex]); + if(marker != NULL) { + marker->setVisible(showPoints); + marker->showSubObjs(showLabels); + marker->update(); + } + } + } + + if(!m_pPlot.isNull() && m_pPlot->m_pPlotView != NULL) { + m_pPlot->m_pPlotView->updateViewport(); + } +}