#ifndef NMWXPRESSFLOWCHARTWIDGET_H #define NMWXPRESSFLOWCHARTWIDGET_H #include #include #include #include #include #include #include "nmDataPerforation.h" #include "nmDataWellBase.h" enum ChartType { CHART_TYPE_LINE, // 连续线条(用于压力) CHART_TYPE_STEP // 阶梯图(用于流量) }; namespace { struct AxisTicks { QVector majors; QVector minors; double mainInterval; double minorInterval; AxisTicks() : mainInterval(0.0), minorInterval(0.0) {} }; static inline bool approxEqual(double a, double b, double eps) { return qAbs(a - b) <= eps; } // 生成 [mn, mx] 的主/次刻度 AxisTicks makeTicks(double mn, double mx, double mainInterval, double minorInterval) { AxisTicks t; if (mx <= mn || mainInterval <= 0.0) return t; t.mainInterval = mainInterval; t.minorInterval = (minorInterval > 0.0 ? minorInterval : 0.0); const double startMajor = floor(mn / mainInterval) * mainInterval; double v; for (v = startMajor; v <= mx + mainInterval * 0.5; v += mainInterval) { if (v >= mn - 1e-9 && v <= mx + 1e-9) t.majors.append(v); } if (t.minorInterval > 0.0) { const double startMinor = floor(mn / t.minorInterval) * t.minorInterval; for (v = startMinor; v <= mx + t.minorInterval * 0.5; v += t.minorInterval) { if (v < mn - 1e-9 || v > mx + 1e-9) continue; bool isMajor = false; for (int i = 0; i < t.majors.size(); ++i) { if (approxEqual(t.majors[i], v, 1e-9)) { isMajor = true; break; } } if (!isMajor) t.minors.append(v); } } return t; } // 轴标签格式化 QString formatTickLabel(double v, bool preferInt, bool isPressureY) { if (preferInt) { const double iv = qRound(v); if (qAbs(v - iv) < 1e-6) return QString::number((int)iv); return QString::number(v, 'f', 1); } if (isPressureY) { double twice = qRound(v * 2.0) / 2.0; // 允许 .5 if (qAbs(v - twice) < 1e-6) { double iv = qRound(twice); if (qAbs(twice - iv) < 1e-6) return QString::number((int)iv); return QString::number(twice, 'f', 1); } return QString::number(v, 'f', 1); } const double iv = qRound(v); if (qAbs(v - iv) < 1e-6) return QString::number((int)iv); return QString::number(v, 'f', 1); } } class nmWxPressFlowChartWidget : public QWidget { Q_OBJECT public: explicit nmWxPressFlowChartWidget(QWidget *parent = 0); // 设置数据 void setData(const QVector& data); void setTitle(const QString& title); void setAxisLabels(const QString& xLabel, const QString& yLabel); void setAxisUnits(const QString& xUnit, const QString& yUnit); void setShowXAxisLabels(bool show); void setLineColor(const QColor& color); void setChartType(ChartType type); double calculateTickInterval(double range, bool isYAxisForPressure); // 清空数据 void clearData(); void setFlowSegmentData(const nmDataPerforation& perforationData); // 设置外部拖动状态(当另一个图表正在拖动时调用) void setExternalDragState(bool isDragging, int boundaryIndex, double boundaryTime); // 清除外部拖动状态 void clearExternalDragState(); public slots: void setSnapToRateChanges(bool snap); signals: void flowSegmentClicked(int segmentIndex); void chartPointClicked(double timePoint); void boundaryMoved(int boundaryIndex, double newTime); void segmentsMerged(int segmentIndex); void boundaryMovedFinal(int segmentIndex, double newTime); protected: void paintEvent(QPaintEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void wheelEvent(QWheelEvent *event); void mouseReleaseEvent(QMouseEvent *event) override; private: void drawBackground(QPainter& painter); void drawGrid(QPainter& painter); void drawAxes(QPainter& painter); void drawData(QPainter& painter); void drawLineData(QPainter& painter); // 绘制连续线条 void drawStepData(QPainter& painter); // 绘制阶梯图 void drawLabels(QPainter& painter); void drawSelectedFlowSegments(QPainter& painter); int findBoundaryAtPosition(const QPoint& pos, double tolerance = 5.0) const; bool isBoundaryDraggable(int boundaryIndex) const; double snapToNearestFlowStep(double time) const; bool shouldMergeSegments(double newBoundaryTime, int boundaryIndex) const; void updateLocalBoundaryPosition(int boundaryIndex, double newTime); void calculateDataRange(); QPointF dataToScreen(const QPointF& dataPoint); QPointF screenToData(const QPointF& screenPoint); private: QVector m_data; QString m_title; QString m_xLabel; QString m_yLabel; QString m_xUnit; QString m_yUnit; QColor m_lineColor; ChartType m_chartType; // 数据范围 double m_minX, m_maxX; double m_minY, m_maxY; // 绘图区域 QRect m_plotArea; int m_marginLeft; int m_marginRight; int m_marginTop; int m_marginBottom; // 交互 QPoint m_lastMousePos; bool m_showXAxisLabels; // 流动段数据副本 nmDataPerforation m_perforationData; // 当前井数据 nmDataWellBase* m_currentWell; // 拖动相关的成员变量 bool m_isDraggingBoundary; // 是否正在拖动分界线 int m_draggingBoundaryIndex; // 正在拖动的分界线索引 (-1表示不在拖动) double m_draggingBoundaryTime; // 拖动过程中的临时时间位置 bool m_snapToRateChanges; // 是否对齐到流量变化点 // 新增成员变量:跟踪外部拖动状态 bool m_externalDragActive; int m_externalDragBoundaryIndex; double m_externalDragBoundaryTime; }; #endif // NMWXPRESSFLOWCHARTWIDGET_H