C++的Qt实现自定义曲线图

发布于:2025-07-05 ⋅ 阅读:(23) ⋅ 点赞:(0)

此处主要是贴了曲线类,可以自行进行修改。里面具有部分变量得从外部传参数。
下面是.h文件:

#pragma once

#include <QtCharts>
#include <QWidget>
#include <QColor>
#include <QButtonGroup>

#include "typedefine.h"

class CustomChartView : public QChartView
{
    Q_OBJECT
public:
    explicit CustomChartView(QWidget* parent = nullptr,
                             const QString& title = "曲线图",
                             const QColor& lineColor = Qt::blue);


    // 清空数据
    void clearData();

    // 设置坐标轴范围
    void setAxisRange(double xMin, double xMax, double yMin, double yMax);

    // 设置曲线颜色
    void setLineColor(const QColor& color);

    // 设置图表标题
    void setChartTitle(const QString& title);

    // 添加新曲线(返回新创建的QLineSeries指针)
    QLineSeries* addSeries(DataSeries& series);

    // 根据名称移除曲线
    void removeSeries(const QString& seriesName);

    // 新增右键菜单事件
    void contextMenuEvent(QContextMenuEvent* event) override;

    // 获取图表图片
    QPixmap getChartPixmap(int width = 0, int height = 0) const;

    // 创建高分辨率图表
    QChart* createHighResChart() const;

    // 导出图表为图片
    bool exportToImage(const QString& filePath, int width = 0, int height = 0);

    // 从数据库中读取指定区间段的数据并存到m_mapDBData中,供生成指定时间段的图片使用
    bool LoadDataInRange(double nStart, double nEnd);

    // 设置当前表的图例名称
    void SetLengedName(const QString& strLenged);


public slots:

    // 显示导出对话框
    void SlotShowExportDialog();

    void SlotResetBestChart();

    void SlotResetBest();

    void SlotChangeAsixRange();

signals:
    void selected(CustomChartView* selectedView); // 新增选中信号
    void exportFinished(bool success, const QString& message); // 新增导出完成信号

protected:
    void mousePressEvent(QMouseEvent* event) override;
    void mouseReleaseEvent(QMouseEvent* event) override; // 新增
    void mouseMoveEvent(QMouseEvent* event) override;    // 新增
    void paintEvent(QPaintEvent* event) override;


private:
    QChart* m_chart;         // 图表对象
    QValueAxis *m_axisX;     // X轴
    QValueAxis *m_axisY;     // Y轴
    QValueAxis *m_axisY_Right;

    QColor m_borderColor = Qt::gray; // 默认边框颜色

    QList<QLineSeries*> m_seriesList; // 管理所有曲线

    QMap<QString, QLineSeries*> m_variableSeries; // 管理变量曲线

    bool m_selected = false;

    mutable QMutex m_dataMutex; // 用于保护数据访问

    QMap<QString, QQueue<double>> m_mapDBData;  // 存储从数据库中读取的所有数据

    QString m_strLenged;
    QLabel *m_pCustomLegend;

    // 坐标轴范围
    double m_nXAsixRange;
    double m_nYAsixMinRange;
    double m_nYAsixMaxRange;
    bool m_bFlagRangeChange;

private:
    static CustomChartView* m_currentSelected; // 静态成员跟踪当前选中项
};

下面是.cpp文件:

#include "customchartview.h"
#include "utils.h"
#include "xyrangeselect.h"

#include <QColor>
#include <QMessageBox>
CustomChartView* CustomChartView::m_currentSelected = nullptr;
CustomChartView::CustomChartView(QWidget* parent, const QString& title, const QColor& lineColor)
    : QChartView(parent)
    , m_bFlagRangeChange(false)
{
    // 初始化图表
    m_chart = new QChart();
    m_chart->setBackgroundBrush(QBrush(Qt::white));
    m_chart->legend()->setVisible(false);   // 隐藏图例

    // 初始化坐标轴
    m_axisX = new QValueAxis();
    m_axisY = new QValueAxis();
    m_axisY_Right = new QValueAxis();
    m_axisX->setTitleText(QStringLiteral("时间"));
    m_axisY->setTitleText(QStringLiteral("参数值"));
    m_axisY_Right->setTitleText(QStringLiteral("误差值"));
    // 设置坐标轴可见性
    m_axisX->setVisible(true);
    m_axisY->setVisible(true);
    m_axisY_Right->setVisible(true);
    // 设置默认显示范围(重要!)
    m_axisX->setRange(-1, 1); // 初始显示-1到1的范围
    m_axisY->setRange(-1, 1);
    m_axisY_Right->setRange(-1, 1);

    // 设置网格线为黑色虚线
    m_axisX->setGridLinePen(QPen(Qt::black, 1, Qt::DashLine));
    m_axisY->setGridLinePen(QPen(Qt::black, 1, Qt::DashLine));

    // 设置坐标轴轴线颜色
    m_axisX->setLinePen(QPen(Qt::black, 2));  // 黑色,2像素宽
    m_axisY->setLinePen(QPen(Qt::black, 2));

    //// 设置刻度线颜色
    //m_axisX->setTickPen(QPen(Qt::black, 1));  // 黑色,1像素宽
    //m_axisY->setTickPen(QPen(Qt::black, 1));

    // 设置刻度标签颜色
    m_axisX->setLabelsColor(Qt::black);
    m_axisY->setLabelsColor(Qt::black);

    // 添加坐标轴到图表
    m_chart->addAxis(m_axisX, Qt::AlignBottom);
    m_chart->addAxis(m_axisY, Qt::AlignLeft);
    m_chart->addAxis(m_axisY_Right, Qt::AlignRight);


    // 设置图表视图
    this->setChart(m_chart);
    this->setRenderHint(QPainter::Antialiasing);
    this->setRubberBand(QChartView::RectangleRubberBand); // 支持框选缩放

    // 设置图例可见
    m_chart->legend()->setVisible(true);

    //m_pCustomLegend = new QLabel("", this);
    //m_pCustomLegend->setStyleSheet("background: lightgray; border: 1px solid gray;");
    //m_pCustomLegend->setAlignment(Qt::AlignCenter);

    m_chart->setMargins(QMargins(0, 0, 0, 0));   // 左、上、右、下边距全为0
    m_chart->setBackgroundRoundness(0);          // 消除圆角导致的空隙

    setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
}

// 清空数据
void CustomChartView::clearData()
{
    m_axisX->setRange(0, 1);
    m_axisY->setRange(0, 1);
}

// 设置坐标轴范围
void CustomChartView::setAxisRange(double xMin, double xMax, double yMin, double yMax)
{
    m_axisX->setRange(xMin, xMax);
    m_axisY->setRange(yMin, yMax);
}

// 设置曲线颜色
void CustomChartView::setLineColor(const QColor& color)
{

}

// 设置图表标题
void CustomChartView::setChartTitle(const QString& title)
{
    m_chart->setTitle(title);
}

QLineSeries* CustomChartView::addSeries(DataSeries& series)
{
    static QList<QColor> colors =
    {
        Qt::red,        // 红色
        Qt::green,      // 绿色
        Qt::blue,       // 蓝色
    };
    QLineSeries* newSeries = new QLineSeries;
    if(series.tag == DataTag::eWuCha)
    {
        series.color = Qt::red;
        newSeries->setName(QStringLiteral("误差"));
    }
    else if(series.tag == DataTag::eFangZhen)
    {
        series.color = Qt::green;
        newSeries->setName(QStringLiteral("仿真"));
    }
    else if(series.tag == DataTag::eShiWu)
    {
        series.color = Qt::blue;
        newSeries->setName(QStringLiteral("实物"));
    }
    //series.color = colors.at(m_seriesList.size() % colors.size());
    // 创建新曲线

    newSeries->replace(series.data);
    newSeries->setColor(series.color);

    // 添加到图表
    m_chart->addSeries(newSeries);
    newSeries->attachAxis(m_axisX);
    newSeries->attachAxis(m_axisY);

    m_seriesList.append(newSeries);
    return newSeries;
}

void CustomChartView::removeSeries(const QString& seriesName)
{
    for(auto it = m_seriesList.begin(); it != m_seriesList.end(); ++it)
    {
        if((*it)->name() == seriesName)
        {
            m_chart->removeSeries(*it);
            delete *it;
            m_seriesList.erase(it);
            break;
        }
        m_chart->removeSeries(*it);
        delete *it;
        m_seriesList.erase(it);
        break;
    }
}

void CustomChartView::contextMenuEvent(QContextMenuEvent* event)
{
    QMenu menu(this);

    // 导出菜单项
    QAction* exportAction = menu.addAction(QIcon(":/icons/export.png"), QStringLiteral("导出图表"));
    connect(exportAction, &QAction::triggered, this, &CustomChartView::SlotShowExportDialog);

    menu.addSeparator();

    QAction* zoomResetAction = menu.addAction(QStringLiteral("重置缩放"));
    connect(zoomResetAction, &QAction::triggered, [this]()
    {
        m_chart->zoomReset();
    });

    QAction* resetBestAction = menu.addAction(QStringLiteral("缩放到最佳"));
    connect(resetBestAction, &QAction::triggered, this, &CustomChartView::SlotResetBestChart);

    menu.addSeparator();

    QAction* xyAsixRangeAction = menu.addAction(QStringLiteral("横纵范围"));
    connect(xyAsixRangeAction, &QAction::triggered, this, &CustomChartView::SlotChangeAsixRange);
    // 显示菜单
    menu.exec(event->globalPos());
}

QPixmap CustomChartView::getChartPixmap(int width /*= 0*/, int height /*= 0*/) const
{
    // 确定输出尺寸
    QSize outputSize;
    if(width > 0 && height > 0)
    {
        outputSize = QSize(width, height);
    }
    else
    {
        // 使用屏幕分辨率作为默认尺寸
        QScreen *screen = QApplication::primaryScreen();
        if(screen)
        {
            outputSize = screen->size() * 0.8; // 屏幕尺寸的80%
        }
        else
        {
            outputSize = QSize(1920, 1080); // 默认尺寸
        }
    }

    // 创建空位图
    QPixmap pixmap(outputSize);
    if(pixmap.isNull())
    {
        qWarning() << "无法创建位图,尺寸:" << outputSize;
        return QPixmap();
    }

    // 填充透明背景
    pixmap.fill(Qt::transparent);

    // 创建绘图器
    QPainter painter(&pixmap);
    if(!painter.isActive())
    {
        qWarning() << "无法在位图上创建绘图器";
        return QPixmap();
    }

    // 设置高质量渲染
    painter.setRenderHints(QPainter::Antialiasing |
                           QPainter::TextAntialiasing |
                           QPainter::SmoothPixmapTransform);

    // 创建临时图表对象(不添加到视图)
    QScopedPointer<QChart> tempChart(createHighResChart());
    if(!tempChart)
    {
        qWarning() << "无法创建临时图表";
        return QPixmap();
    }

    // 创建临时场景
    QGraphicsScene tempScene;
    tempScene.addItem(tempChart.data());
    tempChart->setParent(&tempScene); // 转移所有权

    // 设置图表大小
    tempChart->resize(outputSize);

    // 渲染场景到位图
    tempScene.render(&painter, QRectF(), tempScene.sceneRect());

    // 释放资源(避免双重删除)
    tempScene.removeItem(tempChart.take()); // 从场景移除但不删除

    return pixmap;
}

QT_CHARTS_NAMESPACE::QChart* CustomChartView::createHighResChart() const
{
    // 创建新图表对象
    QChart* highResChart = new QChart();

    // 复制当前图表属性(线程安全方式)
    highResChart->setTitle(m_chart->title());
    highResChart->setBackgroundBrush(m_chart->backgroundBrush());
    highResChart->setMargins(m_chart->margins());
    highResChart->setBackgroundRoundness(m_chart->backgroundRoundness());
    highResChart->setTheme(m_chart->theme());

    // 复制坐标轴
    QValueAxis* newAxisX = new QValueAxis();
    newAxisX->setRange(m_axisX->min(), m_axisX->max());
    newAxisX->setTitleText(m_axisX->titleText());
    newAxisX->setGridLineColor(m_axisX->gridLineColor());
    newAxisX->setLabelsColor(m_axisX->labelsColor());

    QValueAxis* newAxisY = new QValueAxis();
    newAxisY->setRange(m_axisY->min(), m_axisY->max());
    newAxisY->setTitleText(m_axisY->titleText());
    newAxisY->setGridLineColor(m_axisY->gridLineColor());
    newAxisY->setLabelsColor(m_axisY->labelsColor());

    highResChart->addAxis(newAxisX, Qt::AlignBottom);
    highResChart->addAxis(newAxisY, Qt::AlignLeft);

    // 复制所有曲线(使用互斥锁保护原始数据)
    QMutexLocker locker(&m_dataMutex);

    for(QLineSeries* series : m_seriesList)
    {
        QLineSeries* newSeries = new QLineSeries();
        newSeries->setName(series->name());

        // 复制点数据(避免直接访问原始系列)-----后续此处数据可从数据库中取
        QVector<QPointF> points = series->pointsVector();
        newSeries->replace(points);

        newSeries->setColor(series->color());
        newSeries->setPen(series->pen());

        highResChart->addSeries(newSeries);
        newSeries->attachAxis(newAxisX);
        newSeries->attachAxis(newAxisY);
    }

    return highResChart;
}

bool CustomChartView::exportToImage(const QString& filePath, int width /*= 0*/, int height /*= 0*/)
{
    QPixmap pixmap = getChartPixmap(width, height);

    if(pixmap.isNull())
    {
        emit exportFinished(false, QStringLiteral("无法创建图表图片"));
        return false;
    }

    // 根据文件扩展名确定格式
    QString format = "PNG";
    if(filePath.endsWith(".jpg", Qt::CaseInsensitive) ||
            filePath.endsWith(".jpeg", Qt::CaseInsensitive))
    {
        format = "JPG";
    }
    else if(filePath.endsWith(".bmp", Qt::CaseInsensitive))
    {
        format = "BMP";
    }

    // 保存图片
    bool success = pixmap.save(filePath, format.toUtf8().constData(), 95);

    if(success)
    {
        emit exportFinished(true, QString("图表已成功导出到: %1").arg(filePath));
    }
    else
    {
        emit exportFinished(false, QString("导出失败: %1").arg(filePath));
    }

    return success;
}

bool CustomChartView::LoadDataInRange(double nStart, double nEnd)
{
    m_mapDBData.clear();
    int nStartRow = nStart / 0.2;   // 数据库起始行
    int nEndRow = nEnd / 0.2;    // 数据库结束行

    return true;
}

void CustomChartView::SetLengedName(const QString& strLenged)
{
    m_strLenged = strLenged;
}

void CustomChartView::SlotShowExportDialog()
{
    // 创建文件对话框
    QString fileName = QFileDialog::getSaveFileName(
                           this,
                           "导出图表",
                           QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) + "/chart.png",
                           "图片文件 (*.png *.jpg *.jpeg *.bmp)"
                       );

    if(fileName.isEmpty())
    {
        return; // 用户取消
    }

    // 分辨率选择对话框(添加时间段功能)
    QDialog dialog(this);
    dialog.setWindowTitle(QStringLiteral("选择分辨率")); // 修改标题

    QVBoxLayout mainLayout(&dialog); // 改用垂直布局作为主布局

    //// ===== 新增部分:时间范围选择 =====
    //QGroupBox timeGroup(QStringLiteral("选择时间段"));
    //QFormLayout timeLayout(&timeGroup);

    //QLineEdit startTimeEdit;
    //QLineEdit endTimeEdit;

    //timeLayout.addRow(QStringLiteral("开始时间:"), &startTimeEdit);
    //timeLayout.addRow(QStringLiteral("结束时间:"), &endTimeEdit);
    //// ===== 时间范围选择结束 =====

    QGroupBox resGroup(QStringLiteral("选择分辨率"));
    QFormLayout resLayout(&resGroup);

    QSpinBox widthSpin;
    widthSpin.setRange(400, 10000);
    widthSpin.setValue(1920);
    widthSpin.setSuffix(" px");

    QSpinBox heightSpin;
    heightSpin.setRange(300, 10000);
    heightSpin.setValue(1080);
    heightSpin.setSuffix(" px");

    QCheckBox keepRatioCheck(QStringLiteral("保持宽高比"));
    keepRatioCheck.setChecked(true);

    // 当前视图宽高比
    double currentAspect = static_cast<double>(width()) / height();

    // 连接宽高比保持
    QObject::connect(&widthSpin, QOverload<int>::of(&QSpinBox::valueChanged),
                     [&](int value)
    {
        if(keepRatioCheck.isChecked())
        {
            heightSpin.blockSignals(true);
            heightSpin.setValue(static_cast<int>(value / currentAspect));
            heightSpin.blockSignals(false);
        }
    });

    QObject::connect(&heightSpin, QOverload<int>::of(&QSpinBox::valueChanged),
                     [&](int value)
    {
        if(keepRatioCheck.isChecked())
        {
            widthSpin.blockSignals(true);
            widthSpin.setValue(static_cast<int>(value * currentAspect));
            widthSpin.blockSignals(false);
        }
    });

    resLayout.addRow(QStringLiteral("宽度:"), &widthSpin);
    resLayout.addRow(QStringLiteral("高度:"), &heightSpin);
    resLayout.addRow(&keepRatioCheck);

    // ===== 添加到主布局 =====
    //mainLayout.addWidget(&timeGroup);
    mainLayout.addWidget(&resGroup);

    // ===== 底部按钮区域 =====
    QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
    mainLayout.addWidget(&buttons);

    QObject::connect(&buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
    QObject::connect(&buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);

    if(dialog.exec() != QDialog::Accepted)
    {
        return;
    }

    //// ===== 获取用户输入的时间值 =====
    //QString strStartTime = startTimeEdit.text().trimmed();
    //QString strEndTime = endTimeEdit.text().trimmed();
    //bool bFlatStart = Utils::instance()->isInputNumber(strStartTime);
    //bool bFlatEnd = Utils::instance()->isInputNumber(strEndTime);
    //if(!bFlatStart || !bFlatEnd)
    //{
    //    QMessageBox::critical(nullptr, QStringLiteral("提示"), QStringLiteral("请输入正确数字!"));
    //    return;
    //}
    //else if(strStartTime.toDouble() > strEndTime.toDouble())
    //{
    //    QMessageBox::critical(nullptr, QStringLiteral("提示"), QStringLiteral("请输入正确范围!"));
    //    return;
    //}
    //bool bResFlag = LoadDataInRange(strStartTime.toDouble(), strEndTime.toDouble());
    //if(!bResFlag)
    //{
    //    QMessageBox::critical(nullptr, QStringLiteral("提示"), QStringLiteral("数据库读取失败!"));
    //    return;
    //}
    int selectedWidth = widthSpin.value();
    int selectedHeight = heightSpin.value();

    // 导出图片
    exportToImage(fileName, widthSpin.value(), heightSpin.value());
}

void CustomChartView::SlotResetBestChart()
{
    m_bFlagRangeChange = false;
    SlotResetBest();
}

void CustomChartView::SlotResetBest()
{
    if(!m_bFlagRangeChange)
    {
        const auto seriesList = m_chart->series();
        if(seriesList.isEmpty())
        {
            return;
        }

        qreal minX = std::numeric_limits<qreal>::max();
        qreal maxX = std::numeric_limits<qreal>::lowest();
        qreal minY = minX;
        qreal maxY = maxX;

        for(QAbstractSeries* series : seriesList)
        {
            if(auto xySeries = qobject_cast<QXYSeries*>(series))
            {
                for(const QPointF& point : xySeries->points())
                {
                    minX = qMin(minX, point.x());
                    maxX = qMax(maxX, point.x());
                    minY = qMin(minY, point.y());
                    maxY = qMax(maxY, point.y());
                }
            }
        }
        //minX = maxX - m_nXAsixRange;
        //minY = maxY - m_nYAsixRange;

        const qreal margin = 0.05;
        const qreal xRange = maxX - minX;
        const qreal yRange = maxY - minY;

        // X轴正常处理
        minX -= xRange * margin;
        maxX += xRange * margin;

        // Y轴特殊处理恒定值
        if(qFuzzyIsNull(yRange))
        {
            const qreal baseY = minY;
            const qreal offset = (qFuzzyIsNull(baseY) ? 1.0 : qAbs(baseY * 0.1));
            minY = baseY - offset;
            maxY = baseY + offset;

            // 防止相等导致坐标轴崩溃
            if(qFuzzyCompare(minY, maxY))
            {
                maxY += 0.001;
            }
        }
        else
        {
            minY -= yRange * margin;
            maxY += yRange * margin;
        }
        m_chart->axisX()->setRange(minX, maxX);
        m_chart->axisY()->setRange(minY, maxY);


        // 设置误差值坐标轴的范围
        // 使用 std::find_if 查找目标序列
        auto it = std::find_if(seriesList.begin(), seriesList.end(),
                               [](QAbstractSeries * series)
        {
            // 检查序列名称是否匹配 "误差"
            return series->name() == QStringLiteral("误差");
        }
                              );
        if(it != seriesList.end())
        {
            QAbstractSeries* errorSeries = *it;
            if(auto lineSeries = qobject_cast<QLineSeries*>(errorSeries))
            {
                // 获取序列所有数据点
                const auto points = lineSeries->pointsVector();

                // 处理空序列情况
                if(points.isEmpty())
                {
                    qWarning() << QStringLiteral("'误差'曲线数据为空");
                    return;
                }

                // 获取Y值范围
                const double tolerance = 1e-6;  // 浮点精度容差

                // 修改点:使用pair接收minmax_element结果(兼容C++11/14)
                auto result = std::minmax_element(
                                  points.begin(), points.end(),
                                  [](const QPointF & a, const QPointF & b)
                {
                    return a.y() < b.y();
                });
                // 修改点:显式定义迭代器
                auto minIt = result.first;   // 指向最小值的迭代器
                auto maxIt = result.second;  // 指向最大值的迭代器

                double minY = minIt->y();
                double maxY = maxIt->y();

                // 处理极值相等的情况
                if(qFuzzyCompare(minY + 1, maxY + 1))       // 避免浮点精度问题
                {
                    qDebug() << QStringLiteral("警告:最大值与最小值相等 (%1)").arg(minY);

                    // 自动扩展显示范围
                    const double margin = qAbs(minY) * 0.1;
                    minY -= (margin < tolerance ? 1.0 : margin);
                    maxY += (margin < tolerance ? 1.0 : margin);
                }

                //// 输出结果
                //qDebug() << QStringLiteral("'误差'曲线Y轴范围: [%1, %2]")
                //         .arg(minY).arg(maxY);

                // 设置坐标轴范围
                m_axisY_Right->setRange(minY, maxY);
            }
        }
        else
        {
            qWarning() << QStringLiteral("未找到名为'误差'的曲线");
        }
    }
    else
    {
        const auto seriesList = m_chart->series();
        if(seriesList.isEmpty())
        {
            return;
        }

        qreal minX = std::numeric_limits<qreal>::max();
        qreal maxX = std::numeric_limits<qreal>::lowest();
        qreal minY = minX;
        qreal maxY = maxX;

        for(QAbstractSeries* series : seriesList)
        {
            if(auto xySeries = qobject_cast<QXYSeries*>(series))
            {
                for(const QPointF& point : xySeries->points())
                {
                    minX = qMin(minX, point.x());
                    maxX = qMax(maxX, point.x());
                    minY = qMin(minY, point.y());
                    maxY = qMax(maxY, point.y());
                }
            }
        }
        minX = maxX - m_nXAsixRange;
        //minY = maxY - m_nYAsixRange;

        const qreal margin = 0.05;
        const qreal xRange = maxX - minX;
        const qreal yRange = maxY - minY;

        // X轴正常处理
        minX -= xRange * margin;
        maxX += xRange * margin;

        // Y轴特殊处理恒定值
        if(qFuzzyIsNull(yRange))
        {
            const qreal baseY = minY;
            const qreal offset = (qFuzzyIsNull(baseY) ? 1.0 : qAbs(baseY * 0.1));
            minY = baseY - offset;
            maxY = baseY + offset;

            // 防止相等导致坐标轴崩溃
            if(qFuzzyCompare(minY, maxY))
            {
                maxY += 0.001;
            }
        }
        else
        {
            minY -= yRange * margin;
            maxY += yRange * margin;
        }
        m_chart->axisX()->setRange(minX, maxX);
        m_chart->axisY()->setRange(m_nYAsixMinRange, m_nYAsixMaxRange);
    }
}

void CustomChartView::SlotChangeAsixRange()
{
    XYRangeSelect dlg;
    if(dlg.exec() == QDialog::Accepted)
    {
        m_nXAsixRange = dlg.GetXAsixRange();
        m_nYAsixMinRange = dlg.GetYAsixMinRange();
        m_nYAsixMaxRange = dlg.GetYAsixMaxRange();
        m_bFlagRangeChange = true;
    }
    //QDialog dialog(this);
    //dialog.setWindowTitle(QStringLiteral("横纵轴范围")); // 匹配图片标题

    //// 创建垂直布局作为主布局
    //QVBoxLayout mainLayout(&dialog);
    //mainLayout.setContentsMargins(15, 15, 15, 15); // 添加适当边距

    //// 创建时间范围选择分组框 - 匹配图片中的样式
    //QGroupBox AsixGroup(QStringLiteral("选择时间范围"));
    //AsixGroup.setStyleSheet(
    //    "QGroupBox {"
    //    "   border: 1px solid #DDD;"
    //    "   border-radius: 5px;"
    //    "   margin-top: 1.5em;"
    //    "   background: white;"
    //    "   font-weight: bold;"
    //    "}"
    //    "QGroupBox::title {"
    //    "   subcontrol-origin: margin;"
    //    "   left: 10px;"
    //    "   padding: 0 3px;"
    //    "}"
    //);

    //// 使用表单布局作为分组框的内部布局
    //QFormLayout *AsixLayout = new QFormLayout(&AsixGroup);
    //AsixLayout->setSpacing(10); // 行间距
    //AsixLayout->setContentsMargins(15, 20, 15, 15); // 内部边距

    //// 创建输入框和单位标签
    //QLineEdit XAsixEdit;
    //XAsixEdit.setStyleSheet("background: white; padding: 4px;");

    //// 水平布局容器:输入框 + 单位标签
    //QHBoxLayout *hLayoutX = new QHBoxLayout;
    //hLayoutX->addWidget(&XAsixEdit);

    //// 添加单位标签(匹配图片中的空白位置)
    //QLabel *unitLabelX = new QLabel(" S", &AsixGroup);
    //unitLabelX->setStyleSheet("QLabel { color: #666; }");
    //hLayoutX->addWidget(unitLabelX);
    //hLayoutX->setContentsMargins(0, 0, 0, 0); // 取消内边距

    //QLineEdit YAsixEdit;
    //YAsixEdit.setStyleSheet("background: white; padding: 4px;");

    //// 垂直轴单位布局
    //QHBoxLayout *hLayoutY = new QHBoxLayout;
    //hLayoutY->addWidget(&YAsixEdit);

    //QLabel *unitLabelY = new QLabel("", &AsixGroup);
    //unitLabelY->setStyleSheet("QLabel { color: #666; }");
    //hLayoutY->addWidget(unitLabelY);
    //hLayoutY->setContentsMargins(0, 0, 0, 0);

    //// 添加带单位的行到表单布局
    //AsixLayout->addRow(QStringLiteral("横轴范围:"), hLayoutX); // 匹配图片标签文本
    //AsixLayout->addRow(QStringLiteral("纵轴范围:"), hLayoutY); // 匹配图片标签文本

    //// 添加分组框到主布局
    //mainLayout.addWidget(&AsixGroup);

    //// 添加标准按钮
    //QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
    //                           Qt::Horizontal, &dialog);
    //buttonBox.setStyleSheet("QPushButton { padding: 5px 15px; min-width: 80px; }");
    //mainLayout.addWidget(&buttonBox);

    //// 连接按钮信号
    //QObject::connect(&buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
    //QObject::connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);

    //// 显示对话框
    //if(dialog.exec() != QDialog::Accepted)
    //{
    //    return;
    //}
    //m_nXAsixRange = XAsixEdit.text().toInt();
    //m_nYAsixRange = YAsixEdit.text().toInt();
}

void CustomChartView::mousePressEvent(QMouseEvent* event)
{
    if(event->button() == Qt::LeftButton)
    {
        // 取消前一个选中项的高亮
        if(m_currentSelected && m_currentSelected != this)
        {
            m_currentSelected->m_selected = false;
            m_currentSelected->update();
        }

        // 设置当前项为选中状态
        m_selected = true;
        m_currentSelected = this;
        update();
        emit selected(this);
    }
    QChartView::mousePressEvent(event);
}

void CustomChartView::mouseReleaseEvent(QMouseEvent* event)
{
    if(event->button() == Qt::LeftButton)
    {
        // 只传递左键释放事件
        QChartView::mouseReleaseEvent(event);
    }
    else
    {
        // 拦截非左键释放事件
        event->accept();
    }
}

void CustomChartView::mouseMoveEvent(QMouseEvent* event)
{
    if(event->buttons() & Qt::LeftButton)
    {
        // 只传递左键拖动事件
        QChartView::mouseMoveEvent(event);
    }
    else
    {
        // 拦截非左键移动事件
        event->accept();
    }
}

void CustomChartView::paintEvent(QPaintEvent* event)
{
    QChartView::paintEvent(event);

    QPainter painter(viewport());
    // 绘制默认边框
    painter.setPen(QPen(QColor(200, 200, 200), 2));
    painter.drawRect(rect());

    // 选中高亮
    if(m_selected)
    {
        painter.setPen(QPen(QColor(255, 165, 0), 4));
        painter.drawRect(rect().adjusted(2, 2, -2, -2));
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到