You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmSubWxs/nmWxPressFlowChartWidget.cpp

782 lines
23 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmWxPressFlowChartWidget.h"
#include "nmDataAnalyzeManager.h"
#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QFont>
#include <QFontMetrics>
#include <cmath>
nmWxPressFlowChartWidget::nmWxPressFlowChartWidget(QWidget *parent)
: QWidget(parent)
, m_lineColor(Qt::blue)
, m_chartType(CHART_TYPE_LINE) // 默认为连续线条
, m_minX(0), m_maxX(1)
, m_minY(0), m_maxY(1)
, m_marginLeft(45)
, m_marginRight(10)
, m_marginTop(2)
, m_marginBottom(40)
, m_showXAxisLabels(true) // 默认显示X轴标签
, m_isDraggingBoundary(false)
, m_draggingBoundaryIndex(-1)
, m_draggingBoundaryTime(0.0)
, m_snapToRateChanges(true)
, m_externalDragActive(false)
, m_externalDragBoundaryIndex(-1)
, m_externalDragBoundaryTime(0.0)
{
m_currentWell = nmDataAnalyzeManager::getCurrentInstance()->getCurWellData();
setMinimumSize(300, 200);
setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);
}
void nmWxPressFlowChartWidget::setData(const QVector<QPointF>& data)
{
m_data = data;
calculateDataRange();
update();
}
void nmWxPressFlowChartWidget::setAxisLabels(const QString& xLabel, const QString& yLabel)
{
m_xLabel = xLabel;
m_yLabel = yLabel;
update();
}
void nmWxPressFlowChartWidget::setAxisUnits(const QString& xUnit, const QString& yUnit)
{
m_xUnit = xUnit;
m_yUnit = yUnit;
update();
}
void nmWxPressFlowChartWidget::setLineColor(const QColor& color)
{
m_lineColor = color;
update();
}
void nmWxPressFlowChartWidget::setChartType(ChartType type)
{
m_chartType = type;
update();
}
void nmWxPressFlowChartWidget::setShowXAxisLabels(bool show)
{
m_showXAxisLabels = show;
// 如果不显示X轴标签减少底部边距
if (!show) {
m_marginBottom = 2;
} else {
m_marginBottom = 40;
}
update();
}
void nmWxPressFlowChartWidget::clearData()
{
m_data.clear();
update();
}
void nmWxPressFlowChartWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
// 计算绘图区域
m_plotArea = QRect(m_marginLeft, m_marginTop,
width() - m_marginLeft - m_marginRight,
height() - m_marginTop - m_marginBottom);
drawBackground(painter);
drawGrid(painter);
drawAxes(painter);
drawSelectedFlowSegments(painter); // 绘制选中的流动段
drawData(painter);
drawLabels(painter);
}
void nmWxPressFlowChartWidget::drawBackground(QPainter& painter)
{
painter.fillRect(rect(), Qt::white);
painter.fillRect(m_plotArea, Qt::white);
}
double nmWxPressFlowChartWidget::calculateTickInterval(double range, bool isYAxisForPressure)
{
double roughInterval = range / 4.0; // 目标是5个主刻度
if (isYAxisForPressure) {
// 压力图Y轴优先使用整数或0.5的倍数
// 计算数量级
double magnitude = pow(10.0, floor(log10(roughInterval)));
double normalized = roughInterval / magnitude;
// 选择合适的系数允许0.5的倍数
double factor;
if (normalized <= 1.0) {
factor = 1.0;
} else if (normalized <= 2.0) {
factor = 2.0;
} else if (normalized <= 2.5) {
factor = 2.5; // 允许2.5倍数
} else if (normalized <= 5.0) {
factor = 5.0;
} else {
factor = 10.0;
}
return factor * magnitude;
} else {
// X轴和流量图Y轴使用10的倍数
// 计算数量级
double magnitude = pow(10.0, floor(log10(roughInterval)));
double normalized = roughInterval / magnitude;
// 选择合适的系数1, 2, 5, 10
double factor;
if (normalized <= 1.0) {
factor = 1.0;
} else if (normalized <= 2.0) {
factor = 2.0;
} else if (normalized <= 5.0) {
factor = 5.0;
} else {
factor = 10.0;
}
return factor * magnitude;
}
}
void nmWxPressFlowChartWidget::drawGrid(QPainter& painter)
{
const QColor majorGridColor(206, 206, 206);
const QColor minorGridColor(236, 236, 236);
const double xRange = m_maxX - m_minX;
const double yRange = m_maxY - m_minY;
if (xRange <= 0.0 || yRange <= 0.0) return;
const double sx = m_plotArea.width() / xRange;
const double sy = m_plotArea.height() / yRange;
// X主刻度次刻度 = 主/2
const double xMain = calculateTickInterval(xRange, false);
const double xMinor = xMain / 2.0;
// Y沿用你原逻辑压力图原主/2 作为新主;次刻度 = 新主/2
const bool isPressureChart = (m_lineColor == QColor(50, 200, 50));
const double yOrigMain = calculateTickInterval(yRange, isPressureChart);
const double yMain = yOrigMain / 2.0;
const double yMinor = yMain / 2.0;
const AxisTicks xt = makeTicks(m_minX, m_maxX, xMain, xMinor);
const AxisTicks yt = makeTicks(m_minY, m_maxY, yMain, yMinor);
painter.save();
painter.setRenderHint(QPainter::Antialiasing, true);
int i;
// 垂直网格:先画次刻度
painter.setPen(QPen(minorGridColor, 1));
for (i = 0; i < xt.minors.size(); ++i) {
double vx = xt.minors[i];
double x = m_plotArea.left() + (vx - m_minX) * sx + 0.5;
painter.drawLine(QPointF(x, m_plotArea.top()), QPointF(x, m_plotArea.bottom()));
}
painter.setPen(QPen(majorGridColor, 1));
for (i = 0; i < xt.majors.size(); ++i) {
double vx = xt.majors[i];
double x = m_plotArea.left() + (vx - m_minX) * sx + 0.5;
painter.drawLine(QPointF(x, m_plotArea.top()), QPointF(x, m_plotArea.bottom()));
}
// 水平网格:先画次刻度
painter.setPen(QPen(minorGridColor, 1));
for (i = 0; i < yt.minors.size(); ++i) {
double vy = yt.minors[i];
double y = m_plotArea.bottom() - (vy - m_minY) * sy + 0.5;
painter.drawLine(QPointF(m_plotArea.left(), y), QPointF(m_plotArea.right(), y));
}
painter.setPen(QPen(majorGridColor, 1));
for (i = 0; i < yt.majors.size(); ++i) {
double vy = yt.majors[i];
double y = m_plotArea.bottom() - (vy - m_minY) * sy + 0.5;
painter.drawLine(QPointF(m_plotArea.left(), y), QPointF(m_plotArea.right(), y));
}
painter.restore();
}
void nmWxPressFlowChartWidget::drawAxes(QPainter& painter)
{
// 边框/轴线0.5像素对齐,锐利)
painter.save();
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(Qt::black, 1));
const double L = m_plotArea.left() + 0.5;
const double R = m_plotArea.right() + 0.5;
const double T = m_plotArea.top() + 0.5;
const double B = m_plotArea.bottom() + 0.5;
painter.drawLine(QPointF(L, B), QPointF(R, B)); // X轴底边
painter.drawLine(QPointF(L, B), QPointF(L, T)); // Y轴左边
painter.drawLine(QPointF(L, T), QPointF(R, T)); // 顶边
painter.drawLine(QPointF(R, T), QPointF(R, B)); // 右边
painter.restore();
const double xRange = m_maxX - m_minX;
const double yRange = m_maxY - m_minY;
if (xRange <= 0.0 || yRange <= 0.0) return;
const double sx = m_plotArea.width() / xRange;
const double sy = m_plotArea.height() / yRange;
const QColor majorGridColor(206, 206, 206);
const bool isPressureChart = (m_lineColor == QColor(50, 200, 50));
const double xMain = calculateTickInterval(xRange, false);
const AxisTicks xt = makeTicks(m_minX, m_maxX, xMain, 0.0);
const double yOrigMain = calculateTickInterval(yRange, isPressureChart);
const double yMain = yOrigMain / 2.0; // 新主刻度
const AxisTicks yt = makeTicks(m_minY, m_maxY, yMain, 0.0);
QFont font("Arial", 8);
painter.setFont(font);
// X轴主刻度与标签
painter.save();
painter.setPen(QPen(majorGridColor, 1));
int i;
for (i = 0; i < xt.majors.size(); ++i) {
double vx = xt.majors[i];
double x = m_plotArea.left() + (vx - m_minX) * sx + 0.5;
// 刻度线
painter.drawLine(QPointF(x, m_plotArea.bottom() + 0.5),
QPointF(x, m_plotArea.bottom() + 8.5));
// 标签
painter.setPen(Qt::black);
const bool preferInt = (xMain >= 1.0);
const QString label = formatTickLabel(vx, preferInt, false);
const QRect tr = painter.fontMetrics().boundingRect(label);
painter.drawText((int)(x - tr.width()/2),
m_plotArea.bottom() + 20,
label);
painter.setPen(QPen(majorGridColor, 1));
}
painter.restore();
// Y轴主刻度与标签
painter.save();
painter.setPen(QPen(majorGridColor, 1));
for (i = 0; i < yt.majors.size(); ++i) {
double vy = yt.majors[i];
double y = m_plotArea.bottom() - (vy - m_minY) * sy + 0.5;
// 刻度线(仅主刻度)
painter.drawLine(QPointF(m_plotArea.left() - 8.5, y),
QPointF(m_plotArea.left() + 0.5, y));
// 标签
painter.setPen(Qt::black);
const QString label = formatTickLabel(vy, false, isPressureChart);
const QRect tr = painter.fontMetrics().boundingRect(label);
painter.drawText(m_plotArea.left() - tr.width() - 10,
(int)(y + tr.height()/2),
label);
painter.setPen(QPen(majorGridColor, 1));
}
painter.restore();
}
void nmWxPressFlowChartWidget::drawData(QPainter& painter)
{
if (m_data.size() < 1) return;
// 根据图表类型选择绘制方法
if (m_chartType == CHART_TYPE_STEP) {
drawStepData(painter);
} else {
drawLineData(painter);
}
}
void nmWxPressFlowChartWidget::drawLineData(QPainter& painter)
{
if (m_data.size() < 2) return;
// 绘制连接线
painter.setPen(QPen(m_lineColor, 2));
for (int i = 0; i < m_data.size() - 1; ++i) {
QPointF p1 = dataToScreen(m_data[i]);
QPointF p2 = dataToScreen(m_data[i + 1]);
painter.drawLine(p1, p2);
}
// 绘制+号标记
QPen finePen(m_lineColor.darker(110), 1); // 1像素宽度
painter.setPen(finePen);
int markerSize = 2;
for (int i = 0; i < m_data.size(); ++i) {
QPointF p = dataToScreen(m_data[i]);
// 使用浮点坐标确保精确绘制
painter.drawLine(QPointF(p.x() - markerSize, p.y()),
QPointF(p.x() + markerSize, p.y()));
painter.drawLine(QPointF(p.x(), p.y() - markerSize),
QPointF(p.x(), p.y() + markerSize));
}
}
void nmWxPressFlowChartWidget::drawStepData(QPainter& painter)
{
if (m_data.size() < 2) return;
// 设置线条样式
painter.setPen(QPen(m_lineColor, 2));
painter.setRenderHint(QPainter::Antialiasing, true);
// 只绘制阶梯状线条,不绘制任何标记点
for (int i = 0; i < m_data.size() - 1; ++i) {
QPointF p1 = dataToScreen(m_data[i]);
QPointF p2 = dataToScreen(m_data[i + 1]);
painter.drawLine(p1, p2);
}
}
void nmWxPressFlowChartWidget::drawLabels(QPainter& painter)
{
QFont titleFont("Arial", 12, QFont::Bold);
QFont labelFont("Arial", 10);
// 绘制标题
if (!m_title.isEmpty()) {
painter.save();
painter.setFont(titleFont);
painter.setPen(Qt::black);
QRect titleRect = painter.fontMetrics().boundingRect(m_title);
int titleX = (width() - titleRect.width()) / 2;
painter.drawText(titleX, 20, m_title);
painter.restore();
}
// X轴标签
if (!m_xLabel.isEmpty()) {
painter.save();
painter.setFont(labelFont);
painter.setPen(Qt::black);
QString xLabelText = m_xLabel;
if (!m_xUnit.isEmpty()) {
xLabelText += " (" + m_xUnit + ")";
}
QRect xLabelRect = painter.fontMetrics().boundingRect(xLabelText);
int xLabelX = (width() - xLabelRect.width()) / 2;
painter.drawText(xLabelX, height() - 10, xLabelText);
painter.restore();
}
// Y轴标签旋转90度
if (!m_yLabel.isEmpty()) {
painter.save();
painter.setFont(labelFont);
painter.setPen(Qt::black);
QString yLabelText = m_yLabel;
if (!m_yUnit.isEmpty()) {
yLabelText += " (" + m_yUnit + ")";
}
painter.translate(15, height() / 2);
painter.rotate(-90);
QRect yLabelRect = painter.fontMetrics().boundingRect(yLabelText);
painter.drawText(-yLabelRect.width()/2, 0, yLabelText);
painter.restore();
}
}
void nmWxPressFlowChartWidget::drawSelectedFlowSegments(QPainter& painter)
{
const QVector<FlowSegmentData>& segments = m_perforationData.getFlowSegments();
// 1. 首先绘制选中流动段的半透明背景
for(int i = 0; i < segments.size(); ++i) {
const FlowSegmentData& segment = segments[i];
if(segment.isSelected) {
double startTime = segment.segmentStart.getValue().toDouble();
double endTime = segment.segmentEnd.getValue().toDouble();
// 如果是外部拖动状态,并且是被拖动的边界,使用拖动中的时间
if(m_externalDragActive && i == m_externalDragBoundaryIndex) {
startTime = m_externalDragBoundaryTime;
}
// 检查下一个段是否受到拖动影响
if(m_externalDragActive && i + 1 < segments.size() && (i + 1) == m_externalDragBoundaryIndex) {
endTime = m_externalDragBoundaryTime;
}
// 转换为屏幕坐标
int leftX = m_plotArea.left() + (startTime - m_minX) / (m_maxX - m_minX) * m_plotArea.width();
int rightX = m_plotArea.left() + (endTime - m_minX) / (m_maxX - m_minX) * m_plotArea.width();
// 绘制半透明矩形背景
QColor fillColor(255, 180, 180, 140);
painter.fillRect(QRect(leftX, m_plotArea.top(),
rightX - leftX, m_plotArea.height()), fillColor);
}
}
// 2. 绘制所有流动段的边界线(蓝色)
painter.setPen(QPen(QColor(0, 0, 255), 2)); // 蓝色2像素宽
for(int i = 0; i < segments.size(); ++i) {
const FlowSegmentData& segment = segments[i];
double startTime = segment.segmentStart.getValue().toDouble();
double endTime = segment.segmentEnd.getValue().toDouble();
// 如果是外部拖动状态,并且是被拖动的边界,使用拖动中的时间
if(m_externalDragActive && i == m_externalDragBoundaryIndex) {
startTime = m_externalDragBoundaryTime;
}
// 检查下一个段是否受到拖动影响
if(m_externalDragActive && i + 1 < segments.size() && (i + 1) == m_externalDragBoundaryIndex) {
endTime = m_externalDragBoundaryTime;
}
// 检查是否处于合并状态
// 转换为屏幕坐标
int leftX = m_plotArea.left() + (startTime - m_minX) / (m_maxX - m_minX) * m_plotArea.width();
// 绘制起点边界线
painter.drawLine(leftX, m_plotArea.top(), leftX, m_plotArea.bottom());
// 只有最后一个段画终点
if (i == segments.size() - 1) {
int rightX = m_plotArea.left() + (endTime - m_minX) / (m_maxX - m_minX) * m_plotArea.width();
painter.drawLine(rightX, m_plotArea.top(), rightX, m_plotArea.bottom());
}
}
// 3. 如果正在进行外部拖动,绘制拖动中的边界线
if(m_externalDragActive && m_externalDragBoundaryIndex >= 0) {
int dragX = m_plotArea.left() + (m_externalDragBoundaryTime - m_minX) / (m_maxX - m_minX) * m_plotArea.width();
painter.drawLine(dragX, m_plotArea.top(), dragX, m_plotArea.bottom());
}
}
void nmWxPressFlowChartWidget::calculateDataRange()
{
if (m_data.isEmpty()) {
m_minX = 0; m_maxX = 1;
m_minY = 0; m_maxY = 1;
return;
}
m_minX = m_maxX = m_data[0].x();
m_minY = m_maxY = m_data[0].y();
foreach (const QPointF& point, m_data) {
m_minX = qMin(m_minX, point.x());
m_maxX = qMax(m_maxX, point.x());
m_minY = qMin(m_minY, point.y());
m_maxY = qMax(m_maxY, point.y());
}
// 添加一些边距
double xMargin = (m_maxX - m_minX) * 0.02;
double yMargin = (m_maxY - m_minY) * 0.1;
m_minX -= xMargin;
m_maxX += xMargin;
m_minY -= yMargin;
m_maxY += yMargin;
// 确保范围不为零
if (m_maxX - m_minX < 1e-6) {
m_minX -= 0.5;
m_maxX += 0.5;
}
if (m_maxY - m_minY < 1e-6) {
m_minY -= 0.5;
m_maxY += 0.5;
}
}
QPointF nmWxPressFlowChartWidget::dataToScreen(const QPointF& dataPoint)
{
double x = m_plotArea.left() +
(dataPoint.x() - m_minX) / (m_maxX - m_minX) * m_plotArea.width();
double y = m_plotArea.bottom() -
(dataPoint.y() - m_minY) / (m_maxY - m_minY) * m_plotArea.height();
return QPointF(x, y);
}
QPointF nmWxPressFlowChartWidget::screenToData(const QPointF& screenPoint)
{
double x = m_minX + (screenPoint.x() - m_plotArea.left()) / m_plotArea.width() * (m_maxX - m_minX);
double y = m_minY + (m_plotArea.bottom() - screenPoint.y()) / m_plotArea.height() * (m_maxY - m_minY);
return QPointF(x, y);
}
void nmWxPressFlowChartWidget::mouseMoveEvent(QMouseEvent *event)
{
if(m_isDraggingBoundary && m_draggingBoundaryIndex >= 0) {
QPointF dataPoint = screenToData(event->pos());
double newTime = dataPoint.x();
// 限制拖动范围
const QVector<FlowSegmentData>& segments = m_perforationData.getFlowSegments();
double minTime = (m_draggingBoundaryIndex > 1) ?
segments[m_draggingBoundaryIndex - 1].segmentStart.getValue().toDouble() + 0.001 : m_minX;
double maxTime = (m_draggingBoundaryIndex < segments.size() - 1) ?
segments[m_draggingBoundaryIndex + 1].segmentStart.getValue().toDouble() - 0.001 : m_maxX;
newTime = qMax(minTime, qMin(maxTime, newTime));
// 保存原始时间,用于合并检查
double originalNewTime = newTime;
newTime = snapToNearestFlowStep(newTime);
if(qAbs(newTime - m_draggingBoundaryTime) > 1e-6) {
m_draggingBoundaryTime = newTime;
// 实时更新本地数据副本
updateLocalBoundaryPosition(m_draggingBoundaryIndex, newTime);
// 在snap模式下检查是否应该立即合并
if(m_snapToRateChanges && shouldMergeSegments(originalNewTime, m_draggingBoundaryIndex)) {
// 立即触发合并
emit segmentsMerged(m_draggingBoundaryIndex);
m_isDraggingBoundary = false;
m_draggingBoundaryIndex = -1;
m_draggingBoundaryTime = 0.0;
setCursor(Qt::ArrowCursor);
return;
}
// 发送信号同步到另一个图表
emit boundaryMoved(m_draggingBoundaryIndex, newTime);
update();
}
} else if(m_plotArea.contains(event->pos())) {
int boundaryIndex = findBoundaryAtPosition(event->pos(), 8.0);
if(boundaryIndex >= 0 && isBoundaryDraggable(boundaryIndex)) {
setCursor(Qt::SizeHorCursor);
} else {
setCursor(Qt::ArrowCursor);
}
} else {
setCursor(Qt::ArrowCursor);
}
QWidget::mouseMoveEvent(event);
}
void nmWxPressFlowChartWidget::mouseReleaseEvent(QMouseEvent *event)
{
if(m_isDraggingBoundary && event->button() == Qt::LeftButton) {
m_isDraggingBoundary = false;
setCursor(Qt::ArrowCursor);
// 检查是否需要合并段(使用当前数据)
if(shouldMergeSegments(m_draggingBoundaryTime, m_draggingBoundaryIndex)) {
emit segmentsMerged(m_draggingBoundaryIndex);
} else {
// 拖动结束,发送最终更新信号
emit boundaryMovedFinal(m_draggingBoundaryIndex, m_draggingBoundaryTime);
}
m_draggingBoundaryIndex = -1;
m_draggingBoundaryTime = 0.0;
}
QWidget::mouseReleaseEvent(event);
}
void nmWxPressFlowChartWidget::updateLocalBoundaryPosition(int boundaryIndex, double newTime)
{
QVector<FlowSegmentData> segments = m_perforationData.getFlowSegments();
if(boundaryIndex <= 0 || boundaryIndex >= segments.size()) {
return;
}
// 更新当前段的起始时间
segments[boundaryIndex].segmentStart.setValue(QString::number(newTime, 'f', 5));
segments[boundaryIndex].startTime.setValue(QString::number(newTime, 'f', 5));
// 更新前一个段的结束时间
segments[boundaryIndex - 1].segmentEnd.setValue(QString::number(newTime, 'f', 5));
// 更新本地数据副本
m_perforationData.setFlowSegments(segments);
}
int nmWxPressFlowChartWidget::findBoundaryAtPosition(const QPoint& pos, double tolerance) const
{
if(!m_plotArea.contains(pos)) {
return -1;
}
const QVector<FlowSegmentData>& segments = m_perforationData.getFlowSegments();
// 检查每个流动段的边界线
for(int i = 0; i < segments.size(); ++i) {
const FlowSegmentData& segment = segments[i];
// 检查段的起始边界(第一个段的起始边界不可拖动)
if(i > 0) {
double startTime = segment.segmentStart.getValue().toDouble();
int boundaryX = m_plotArea.left() + (startTime - m_minX) / (m_maxX - m_minX) * m_plotArea.width();
if(qAbs(pos.x() - boundaryX) <= tolerance) {
return i; // 返回段索引,表示该段的左边界
}
}
}
return -1;
}
// 检查分界线是否可拖动
bool nmWxPressFlowChartWidget::isBoundaryDraggable(int boundaryIndex) const
{
const QVector<FlowSegmentData>& segments = m_perforationData.getFlowSegments();
// 第一个段的起始边界和最后一个段的结束边界不可拖动
return boundaryIndex > 0 && boundaryIndex < segments.size();
}
// 将时间吸附到最近的流量阶梯边界
double nmWxPressFlowChartWidget::snapToNearestFlowStep(double time) const
{
if(!m_snapToRateChanges) {
return time; // 未勾选对齐,直接返回
}
return m_currentWell->findSmartFlowStepBoundary(time);
}
// 检查是否应该合并段
bool nmWxPressFlowChartWidget::shouldMergeSegments(double newBoundaryTime, int boundaryIndex) const
{
const QVector<FlowSegmentData>& segments = m_perforationData.getFlowSegments();
if(boundaryIndex <= 0 || boundaryIndex >= segments.size()) {
return false;
}
double mergeThreshold = 1.0; // 1小时的合并阈值
// 检查与左侧分界线的距离
if(boundaryIndex > 1) {
double leftBoundaryTime = segments[boundaryIndex - 1].segmentStart.getValue().toDouble();
double distance = qAbs(newBoundaryTime - leftBoundaryTime);
if(distance < mergeThreshold) {
return true;
}
}
// 检查与右侧分界线的距离
if(boundaryIndex < segments.size() - 1) {
double rightBoundaryTime = segments[boundaryIndex + 1].segmentStart.getValue().toDouble();
double distance = qAbs(newBoundaryTime - rightBoundaryTime);
if(distance < mergeThreshold) {
return true;
}
}
return false;
}
void nmWxPressFlowChartWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && m_plotArea.contains(event->pos())) {
// 首先检查是否点击在分界线附近
int boundaryIndex = findBoundaryAtPosition(event->pos());
if(boundaryIndex >= 0 && isBoundaryDraggable(boundaryIndex)) {
// 开始拖动分界线
m_isDraggingBoundary = true;
m_draggingBoundaryIndex = boundaryIndex;
// 获取当前分界线时间
const QVector<FlowSegmentData>& segments = m_perforationData.getFlowSegments();
m_draggingBoundaryTime = segments[boundaryIndex].segmentStart.getValue().toDouble();
m_lastMousePos = event->pos();
setCursor(Qt::SizeHorCursor);
update();
return;
}
// 如果不是拖动分界线,执行原有逻辑
QPointF dataPoint = screenToData(event->pos());
double clickTime = dataPoint.x();
emit chartPointClicked(clickTime);
//int segmentIndex = nmDataAnalyzeManager::getCurrentInstance()->findFlowSegmentByTime(clickTime);
int segmentIndex = m_perforationData.findFlowSegmentByTime(clickTime);
if(segmentIndex >= 0) {
emit flowSegmentClicked(segmentIndex);
}
}
QWidget::mousePressEvent(event);
}
void nmWxPressFlowChartWidget::wheelEvent(QWheelEvent *event)
{
// 实现缩放功能
Q_UNUSED(event);
}
void nmWxPressFlowChartWidget::setFlowSegmentData(const nmDataPerforation& perforationData)
{
m_perforationData = perforationData;
update();
}
void nmWxPressFlowChartWidget::setSnapToRateChanges(bool snap) {
m_snapToRateChanges = snap;
}
void nmWxPressFlowChartWidget::setExternalDragState(bool isDragging, int boundaryIndex, double boundaryTime)
{
m_externalDragActive = isDragging;
m_externalDragBoundaryIndex = boundaryIndex;
m_externalDragBoundaryTime = boundaryTime;
// 如果是外部拖动,也要同步更新本地数据副本
if(isDragging && boundaryIndex >= 0) {
updateLocalBoundaryPosition(boundaryIndex, boundaryTime);
}
// 立即重绘以显示外部拖动状态
update();
}
void nmWxPressFlowChartWidget::clearExternalDragState()
{
m_externalDragActive = false;
m_externalDragBoundaryIndex = -1;
m_externalDragBoundaryTime = 0.0;
update();
}