#include "nmWxPressFlowChartWidget.h" #include "nmDataAnalyzeManager.h" #include #include #include #include #include #include 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& 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(), QColor(240, 240, 240)); // 绘制绘图区域背景 painter.fillRect(m_plotArea, QColor(248, 248, 248)); } 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& 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& 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 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& 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& 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& 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& 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(); }