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

786 lines
23 KiB
C++

#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(), 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<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();
}