Qt练手小项目之绘制动态柱状图

    首先附上效果图。Qt版本4.8.6,IDE:vs2008.柱状图可根据数据变化实时增减,支持修改刻度值范围,修改柱状图颜色和背景颜色。实现起来并不复杂,使用的都是一些非常常见的Qt类和函数,下面就来看看主要代码。

    首先,背景的绘制并没有使用QSS,这样可以实时更改背景色。背景使用渐变,很简单。

    其次是刻度线和刻度值的绘制稍微麻烦一点,因为刻度线要分别绘制单位刻度线,中位刻度线和主刻度线。需要注意的是绘制的位置和刻度线需要梯度上升。

     最后,绘制柱状图和柱状图背景都相对简单,只需要设置好柱状图所在的区域就能快速绘制。

    主要部分源码如下:

#include "MeasuringBar.h"

#include <typeinfo>
#include <cmath>

#include <QLinearGradient>
#include <QString>
#include <QDebug>

#define DEVIATION 0.0001  //浮点数比较精度
#define LINEHPERSITION 15 //刻度线水平位置参数
#define TEXTWIDTH      5  //刻度值高度参数
#define TEXTHEIGHT     4  //刻度值宽度参数
#define BARHPOSITION   35  //柱状图水平位置参数

MeasuringBar::MeasuringBar(QWidget *parent)
    : QWidget(parent)
{
    Init();
    CreateConnection();
}

MeasuringBar::~MeasuringBar()
{
    if(m_timer->isActive())
    {
        m_timer->stop();
    }
}

void MeasuringBar::Init()
{
    m_dMinValue =    0.0;   //最小值
    m_dMaxValue =    100.0; //最大值
    m_dTargetValue = 0.0;   //目标值
    m_iPercision =   0;     //精度值。小数点后几位

    m_iLongStep =    10;    //长刻度条步长
    m_iShortStep =   1;     //短刻度条步长
    m_iSpace =       20;    //间距

    m_bAnimation =     false; //是否启用动画
    m_dAnimationStep = 0.5;   //动画播放时步长

    m_BGColorStart = QColor(100, 100, 100); //背景开始渐变色
    m_BGColorEnd   = QColor(60, 60, 60);    //背景结束渐变色
    m_LineColor =    QColor(255, 255, 255); //线条颜色
    m_BarBGColor =   QColor(220, 220, 220); //柱状图背景色
    m_BarColor =     QColor(100, 184, 255); //柱状图颜色

    m_bReverse =      false;  //是否倒退
    m_dCurrentValue = 0.0;    //当前值

    m_timer = new QTimer(this);
    m_timer->setInterval(10);

    setFont(QFont("Arial", 8));
}

void MeasuringBar::CreateConnection()
{
    connect(m_timer, SIGNAL(timeout()), this, SLOT(SltUpdateValue()));
}

void MeasuringBar::SltUpdateValue()
{
    if(!m_bReverse)  //柱状图向上增长
    {
        if(m_dCurrentValue > m_dTargetValue)
        {
            m_dCurrentValue = m_dTargetValue;
            m_timer->stop();
        }
        else
        {
            m_dCurrentValue += m_dAnimationStep;
        }
    }
    else //柱状图向下减少
    {
        if(m_dCurrentValue <= m_dTargetValue)
        {
            m_dCurrentValue = m_dTargetValue;
            m_timer->stop();
        }
        else
        {
            m_dCurrentValue -= m_dAnimationStep;
        }
    }

    update();
}

//绘制背景(渐变)
void MeasuringBar::DrawBackground(QPainter *painter)
{
    painter->save();
    painter->setPen(Qt::NoPen); //Qt::NoPen:不绘制任何边界线

    //渐变
    QLinearGradient bgGradient(QPointF(0, 0), QPointF(0, height()));
    bgGradient.setColorAt(0.0, m_BGColorStart);
    bgGradient.setColorAt(1.0, m_BGColorEnd);

    painter->setBrush(bgGradient);
    painter->drawRect(rect());
    painter->restore();
}

//绘制刻度及刻度值
void MeasuringBar::DrawRuler(QPainter *painter) 
{
    painter->save();
    painter->setPen(m_LineColor);

    //绘制纵向标尺线
    double initX = m_iSpace + LINEHPERSITION;
    double initY = m_iSpace;

    QPointF topPoint(initX, initY);
    QPointF bottomPoint(initX, height()-m_iSpace);

    painter->drawLine(topPoint, bottomPoint);

    //绘制刻度
    double length =    height() - 2 * m_iSpace; //刻度线距离顶部和底部m_iSpace距离,所以它的长度就是这么多
    double increment = length / (m_dMaxValue - m_dMinValue); //计算每一格移动多少
    int longLineLength =  10; //长刻度线长
    int shortLineLength = 7; //短刻度线长

    //根据范围绘制刻度值
    for(int i = static_cast<int>(m_dMaxValue); i >= static_cast<int>(m_dMinValue); i -= m_iShortStep)
    {
        if(i % m_iLongStep == 0)  //画最长刻度线
        {
            QPointF leftPoint(initX, initY);
            QPointF rightPoint(initX + longLineLength, initY);

            painter->drawLine(leftPoint, rightPoint); //绘制刻度线

            QString strValue = QString("%1").arg(static_cast<double>(i), 0, 'f', m_iPercision); //刻度值
            double fontWidth = static_cast<double>(painter->fontMetrics().width(strValue));
            //qDebug() << typeid(painter->fontMetrics().width(strValue)).name() << "-------";
            double fontHeight = static_cast<double>(painter->fontMetrics().height()); //没有参数的
            QPointF textPoint(initX - fontWidth - TEXTWIDTH, initY + fontHeight / TEXTHEIGHT);  //刻度值位置

            painter->drawText(textPoint, strValue); //绘制刻度值
        }
        else
        {
            if(i % (m_iLongStep / 2) == 0) //刻度中位线长
            {
                shortLineLength = 7;
            }
            else                           //单位刻度线长
            {
                shortLineLength = 4;
            }

            QPointF leftPoint(initX, initY);
            QPointF righPoint(initX + shortLineLength, initY); //刻度线位置

            painter->drawLine(leftPoint, righPoint); //画刻度线
        }

        initY += increment * m_iShortStep; //刻度线向上移动
    }

    painter->restore();
}

//绘制柱状图背景
void MeasuringBar::DrawBarBackground(QPainter *painter)
{
    painter->save();
    painter->setPen(Qt::NoPen);

    double initX = static_cast<double>(m_iSpace + BARHPOSITION);
    QPointF topLeftPoint(initX, m_iSpace);
    QPointF bottomRightPoint(width() - m_iSpace, height() - m_iSpace);

    m_barRect = QRectF(topLeftPoint, bottomRightPoint); //柱状图所在区域

    painter->setBrush(m_BarBGColor);
    painter->drawRect(m_barRect);
    painter->restore();
}

//绘制柱状图
void MeasuringBar::DrawBar(QPainter *painter)
{
    painter->save();
    painter->setPen(Qt::NoPen);

    double barHeight = m_barRect.height();
    double increment = barHeight / (m_dMaxValue - m_dMinValue); //增量
    double initY = (m_dCurrentValue - m_dMinValue) * increment;

    QPointF topLeftPoint(m_barRect.topLeft().x(), m_barRect.bottomLeft().y() - initY);
    //qDebug() << "----------------" << m_barRect.bottomLeft().y();
    QPointF bottomRightPoint(m_barRect.bottomRight());
    QRectF  currentRect(topLeftPoint, bottomRightPoint);

    painter->setBrush(m_BarColor);
    painter->drawRect(currentRect);
    painter->restore();
}

//绘画事件
void MeasuringBar::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); //反走样

    DrawBackground(&painter);
    DrawRuler(&painter);
    DrawBarBackground(&painter);
    DrawBar(&painter);
}

//设置刻度范围
void MeasuringBar::SetRange(double minValue, double maxValue)
{
    if(m_dMinValue > m_dMaxValue) //最小值大于最大值,不合理
    {
        return;
    }

    m_dMinValue = minValue;
    m_dMaxValue = maxValue;

    if(m_dTargetValue < m_dMinValue || m_dTargetValue > m_dMaxValue)
    {
        SetTargetValue(m_dTargetValue);
    }

    update();
}

void MeasuringBar::SetRange(int minValue,int maxValue)
{
    SetRange(static_cast<double>(minValue), static_cast<double>(maxValue));
}

//设置最大最小值
void MeasuringBar::SetMaxValue(double maxValue)
{
    SetRange(m_dMinValue, maxValue);
}

void MeasuringBar::SetMinVlaue(double minValue)
{
    SetRange(minValue, m_dMaxValue);
}

//设置目标值
void MeasuringBar::SetTargetValue(double targetValue)
{
    if(targetValue < m_dMinValue || targetValue > m_dMaxValue)
    {
        return;
    }

    if(targetValue > m_dTargetValue)
    {
        m_bReverse = false;
    }
    else if(targetValue < m_dTargetValue)
    {
        m_bReverse = true;
    }

    m_dTargetValue = targetValue;

    emit TargetValueChanged(targetValue);

    if(!m_bAnimation)
    {
        m_dCurrentValue = m_dTargetValue;
        update();
    }
    else
    {
        m_timer->start();
    }
}

void MeasuringBar::SetTargetValue(int targetValue)
{
    SetTargetValue(static_cast<double>(targetValue));
}

//设置精确度
void MeasuringBar::SetPercision(int percision)
{
    if(percision <= 3 && m_iPercision != percision) //最大精确度为3
    {
        m_iPercision = percision;
        update();
    }
}

//设置长短刻度条步长
void MeasuringBar::SetLongStep(int longStep)
{
    if(m_iShortStep > m_iLongStep) //短刻度步长不可超过长刻度步长
    {
        return;
    }

    if(m_iLongStep != longStep)
    {
        m_iLongStep = longStep;
        update();
    }
}

void MeasuringBar::SetShortStep(int shortStep)
{
    if(m_iShortStep > m_iLongStep) //短刻度步长不能超过长刻度步长
    {
        return;
    }

    if(m_iShortStep != shortStep)
    {
        m_iShortStep = shortStep;
        update();
    }
}

//设置间距
void MeasuringBar::SetSpace(int space)
{
    if(m_iSpace != space)
    {
        m_iSpace = space;
    }
}

//设置是否使用动画显示
void MeasuringBar::SetUseAnimation(bool animation)
{
    if(m_bAnimation != animation)
    {
        m_bAnimation = animation;
        update();
    }
}

void MeasuringBar::SetAnimationStep(double animationStep)
{
    if(fabs(m_dAnimationStep - animationStep) > DEVIATION) //m_dAnimationStep != animationStep
    {
        m_dAnimationStep = animationStep;
        update();
    }
}

//设置背景颜色
void MeasuringBar::SetBackgroundColorStart(const QColor &BgColorStart)
{
    if(m_BGColorStart != BgColorStart)
    {
        m_BGColorStart = BgColorStart;
        update();
    }
}

void MeasuringBar::SetBackgroundColorEnd(const QColor &BgColorEnd)
{
    if(m_BGColorEnd != BgColorEnd)
    {
        m_BGColorEnd = BgColorEnd;
        update();
    }
}

//设置线条颜色
void MeasuringBar::SetLineColor(const QColor &lineColor)
{
    if(m_LineColor != lineColor)
    {
        m_LineColor = lineColor;
        update();
    }
}

//设置柱状图背景颜色
void MeasuringBar::SetBarBgColor(const QColor &barBgColor)
{
    if(m_BarBGColor != barBgColor)
    {
        m_BarBGColor = barBgColor;
        update();
    }
}

//设置柱状图颜色
void MeasuringBar::SetBarColor(const QColor &barColor)
{
    if(m_BarColor != barColor)
    {
        m_BarColor = barColor;
        update();
    }
}

如需整个工程请见:https://download.csdn.net/download/dzhongjie/11099810

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您介绍一下在 Qt绘制并添加三角的方法。 首先,我们可以使用 `QPainter` 类来绘制。具体步骤如下: 1. 创建一个继承自 `QWidget` 的自定义控件。 2. 在 `paintEvent` 方法中创建一个 `QPainter` 对象。 3. 根据数据绘制。 4. 如果需要添加三角,可以使用 `QPolygonF` 类创建一个三角形的路径,然后使用 `QPainter` 的 `drawPolygon` 方法绘制出来。 下面是一个简单的代码示例,可以绘制一个简单的柱,并在顶部添加一个三角形: ```cpp void MyWidget::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 绘制坐标轴 painter.drawLine(50, 250, 350, 250); painter.drawLine(50, 250, 50, 50); // 绘制 QVector<double> data = {20, 50, 80, 30, 70}; int spacing = 50; int width = 30; int x = 75; int y = 250; painter.setBrush(QBrush(Qt::blue)); for (int i = 0; i < data.size(); i++) { int height = data[i] * 2; painter.drawRect(x, y - height, width, height); x += spacing; } // 绘制顶部三角形 QPointF points[3] = { QPointF(150, 30), QPointF(165, 15), QPointF(180, 30) }; QPolygonF triangle(points, 3); painter.setBrush(QBrush(Qt::red)); painter.drawPolygon(triangle); } ``` 这里我们绘制了一个数据为 {20, 50, 80, 30, 70} 的柱,每个柱子之间的间距为 50,每个柱子的宽度为 30。在顶部添加了一个红色的三角形。您可以根据实际需求调整柱和三角形的位置和大小。 希望这个例子能够帮助到您!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值