Qt绘制曲线统计图(paintEvent)


前言

        整理代码的时候看到了之前写的一个统计图的代码,感觉样式还可以,分享一下,适合初学者。

        本统计图是一个填充了渐变颜色的曲线统计图,X轴显示了从今天往前倒推一周的日期,Y轴则显示的是时间的统计,目前只做了统计到“分”的计算,可以用于统计一周的运动时间、学习时间等。开发环境用的Qt5.5.1+MSVC2013。


一、效果图

先展示一下效果图:

二、主要代码

        统计图的数据是用QList<QMap<QString, QString>>存储的,QMap中记录了X轴和Y轴的键值对,X轴对应的key是“date”,Y轴对应的key是“value”。产生的数据暂时用随机数模拟,代码如下:

    QDateTime curTime = QDateTime::currentDateTime();
    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    QList<QMap<QString, QString>> dataList;
    for(int i = 1; i <= 7; i++)
    {
        QMap<QString, QString> oneMap;
        oneMap.insert("date",
                      QString("%1.%2").arg(curTime.addDays(i - 7).date().month())
                      .arg(curTime.addDays(i - 7).date().day()));
        oneMap.insert("value",QString::number(qrand() % 3599, 10));
        dataList.append(oneMap);
    }

        如果你想把统计图换成其他颜色可以修改渐变色的值,一定要加上painter.setRenderHint(QPainter::Antialiasing, true)这一段代码,要不画出来的图有毛边。

    QPainter painter(this);
    painter.setPen(Qt::NoPen);
    painter.setRenderHint(QPainter::Antialiasing, true);
    QLinearGradient linearBubble(rect().topLeft(), rect().bottomLeft());
    linearBubble.setColorAt(0, QColor(45,183,255));
    linearBubble.setColorAt(0.4, QColor(45,183,255));
    linearBubble.setColorAt(1, Qt::white);
    painter.setBrush(linearBubble);

        要遍历一下传递过来的数据,取到最大的值,以最大值占用总值90%的比率计算出统计图的阈值,然后再计算出每个点相隔的距离:        

    for(int i = 0;i < 7;i++)
    {
        if(m_data[i]["value"].toInt() > m_curveThreshold)
        {
            m_curveThreshold = m_data[i]["value"].toInt()/0.9;
        }
        if(m_curveThreshold == 0)
        {
            m_curveThreshold = 1;
        }
    }

    //取点间距
    m_pointSpace = (width() - CHART_EDGE * 2) / 6;

通过计算来确定每个点的位置,两个点之间用一条三次贝塞尔曲线链接:

    //画链接两点的曲线
    QPoint start_pos(CHART_EDGE + (i - 1) * m_pointSpace, startPosY);
    QPoint end_pos(CHART_EDGE + i * m_pointSpace, endPosY);
    QPoint c1((start_pos+end_pos).x()/2,start_pos.y());
    QPoint c2((start_pos+end_pos).x()/2,end_pos.y());
    myPath.cubicTo(c1 , c2, end_pos);

        最后将每个点的连线放到QPainterPath中,再调用画笔的drawPath方法就可以了。

三、paintEvent全部代码

paintEvent中的全部代码:

void MyCountChart::paintEvent(QPaintEvent *event)
{
    m_curveThreshold = 0;
    if(m_data.count() < 7)
    {
        return;
    }

    //创建画板
    QPainter painter(this);

    //设置无画笔
    painter.setPen(Qt::NoPen);
    //反锯齿
    painter.setRenderHint(QPainter::Antialiasing, true);
    //填充画板渐变色
    QLinearGradient linearBubble(rect().topLeft(), rect().bottomLeft());
    linearBubble.setColorAt(0, QColor(45,183,255));
    linearBubble.setColorAt(0.4, QColor(45,183,255));
    linearBubble.setColorAt(1, Qt::white);
    painter.setBrush(linearBubble);

    //取阈值
    for(int i = 0;i < 7;i++)
    {
        if(m_data[i]["value"].toInt() > m_curveThreshold)
        {
            m_curveThreshold = m_data[i]["value"].toInt()/0.9;
        }
        if(m_curveThreshold == 0)
        {
            m_curveThreshold = 1;
        }
    }

    //取点间距
    m_pointSpace = (width() - CHART_EDGE * 2) / 6;

    //设置曲线图高度
    m_chartCurveWidth = height() - CHART_EDGE * 2;

    //底线端点
    QPoint startEdge(CHART_EDGE, height() - CHART_EDGE);
    QPoint endEdge(CHART_EDGE + 6 * m_pointSpace, height() - CHART_EDGE);

    //创建路径
    QPainterPath myPath(endEdge);
    myPath.lineTo(startEdge);

    //设置第一个点的位置
    int firstPosY = height() - CHART_EDGE - m_data[0]["value"].toInt() * m_chartCurveWidth / m_curveThreshold;
    QPoint firstPoint(CHART_EDGE, firstPosY);
    myPath.lineTo(firstPoint);

    //设置第一个点的日期标签位置和内容
    m_dateLabels[0]->setText(m_data[0]["date"]);
    m_dateLabels[0]->move(CHART_EDGE - 20, height() - CHART_EDGE + 10);

    //设置第一个点的时长标签位置和内容
    int firstTime_min = m_data[0]["value"].toInt()/60;
    int firstTime_sec = m_data[0]["value"].toInt()%60;
    if(firstTime_sec >= 30)
    {
        firstTime_min+=1;
    }
    else
    {
        if(firstTime_min == 0 && firstTime_sec > 0)
        {
            firstTime_min = 1;
        }
    }
    m_timeLabels[0]->setText(QString("%1%2").arg(firstTime_min).arg(Y_UNIT_MIN));
    m_timeLabels[0]->move(CHART_EDGE-23, firstPosY-18);

    for(int i = 1; i < 7; i++)
    {
        //两点的Y坐标
        int startPosY = height() - CHART_EDGE - m_data[i-1]["value"].toInt() * m_chartCurveWidth / m_curveThreshold;
        int endPosY = height() - CHART_EDGE - m_data[i]["value"].toInt() * m_chartCurveWidth / m_curveThreshold;

        //画链接两点的曲线
        QPoint start_pos(CHART_EDGE + (i - 1) * m_pointSpace, startPosY);
        QPoint end_pos(CHART_EDGE + i * m_pointSpace, endPosY);
        QPoint c1((start_pos+end_pos).x()/2,start_pos.y());
        QPoint c2((start_pos+end_pos).x()/2,end_pos.y());
        myPath.cubicTo(c1 , c2, end_pos);

        //日期
        if(i == 6)
        {
            m_dateLabels[i]->setText(QString::fromLocal8Bit("今天"));
        }
        else
        {
            m_dateLabels[i]->setText(m_data[i]["date"]);
        }
        m_dateLabels[i]->move(CHART_EDGE + i * m_pointSpace - 20, height() - CHART_EDGE + 10);

        //时长
        int time_min = m_data[i]["value"].toInt()/60;
        int time_sec = m_data[i]["value"].toInt()%60;
        if(time_sec >= 30)
        {
            time_min+=1;
        }
        else
        {
            if(time_min == 0 && time_sec > 0)
            {
                time_min = 1;
            }
        }
        m_timeLabels[i]->setText(QString("%1%2").arg(time_min).arg(Y_UNIT_MIN));
        m_timeLabels[i]->move(CHART_EDGE + i * m_pointSpace - 23, endPosY - 18);
    }

    //封闭图形
    myPath.lineTo(endEdge);
    painter.drawPath(myPath);
    return QWidget::paintEvent(event);
}

最后

主要就是通过重绘事件对界面的一个绘制,希望对您有帮助。

源码地址:https://download.csdn.net/download/qq_36129488/87810601

  • 11
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q泥T挖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值