qt自定义控件-圆形通用指示表盘

一、前言

制作一个圆形仪表盘,此表盘通用各种参数指示,如:温度,速度,压力,等进度相关的类别。

二、环境

qt5.7 MinGW

windows8

三、正文

首先 演示一下效果图

qt自定义控件-圆形通用指示表盘_第1张图片qt自定义控件-圆形通用指示表盘_第2张图片

表盘采用的是上半圆形表盘,有彩色的渐变条框,这个用QConicalGradient实现,其余的还稍微容易一些,

偶尔水群,听过刘大师一句话非常有道理,只要心中有坐标,万物皆可paint,绘制自定义控件,搞明白坐标是基础的部分,其次 才是想怎么去画想要的图形。

下面话不多说直接把此通用圆形表盘代码带注释简单讲解:

qt自定义控件-圆形通用指示表盘_第3张图片

首先是一些必备的参数先声明好,还有一些提供外部修改参数的接口,这个根据自己需求预留,此 控件我暂时只 预留了修改范围和修改标题。

public Q_SLOTS:
    void setRange(int minValued, int maxValued);
    //修改范围
    void setCurentSpeed(int speed);
    //设置标题
    void setTitle(const QString &titled);
private:
    void drawFrame(QPainter& painter);//绘制边框与背景
    void drawNumberIndicator(QPainter& painter);//绘制指示数字
    void drawDividing(QPainter& painter);//绘制刻度
    void drawNumberSpeed(QPainter &painter);//显示数字速度
    void drawIndicator(QPainter& painter);//绘制速度指针

private:
    int m_startAngle;
    int m_endAngle;

    int m_refSize; //绘制的参考大小
    int m_radius;

    int m_minSpeed;//最小值
    int m_maxSpeed;//最大值
    int m_curSpeed;//当前值
    int m_true_curSpeed;//当前值
    double m_anglePerVel;//温度 对应的 角度

    QString title;                  //标题
    m_startAngle = 180; //顺时针角度
    m_endAngle = 0;

    m_refSize = 200;
    m_radius = m_refSize /2;

    m_minSpeed = 0;   //由于绘制刻度,需要为5倍数
    m_maxSpeed = 100; //由于绘制刻度,需要为5倍数
    m_curSpeed = m_minSpeed;
    //温度对应的角度
    m_anglePerVel = (360.0 - (m_startAngle - m_endAngle)) / (m_maxSpeed - m_minSpeed);

    setWindowFlags(Qt::FramelessWindowHint);
    setAttribute(Qt::WA_TranslucentBackground);

 

接下来就是paintevent部分了,在这里,每个控件通用的语句就是先定好中心和一个范围,如下代码上半部分所示

void SpeedPanel::paintEvent(QPaintEvent */*event*/)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);// 抗锯齿

    float scale = qMin(width(),height());
    //设置缩放比例和原点的先后顺序很重要
    painter.scale(scale/m_refSize,scale/m_refSize);
    painter.translate(m_refSize/2,m_refSize/2); //设置坐标原点

    drawFrame(painter);
    drawDividing(painter);//绘制刻度
    drawNumberIndicator(painter);//绘制指示数字
    drawIndicator(painter);//绘制速度指针
    drawNumberSpeed(painter);//显示数字速度
}

 在之后还有5个函数,分别是绘制表盘从底层到 顶层,一层一层覆盖

此部分核心 代码直接贴上,注释都有可以看懂


void SpeedPanel::drawFrame(QPainter &painter)
{
    painter.save();
    painter.setPen(Qt::NoPen);//确保没有边框线----填满,不留边界线
    //绘制灰色圆形外边框
    QLinearGradient lg1(-m_radius,-m_radius,m_radius,m_radius);//渐变区域
    lg1.setColorAt(0,Qt::gray);
    lg1.setColorAt(0.5,Qt::white);
    lg1.setColorAt(1,Qt::gray);
    painter.setBrush(lg1);//设置画笔
    painter.drawEllipse(-m_radius,-m_radius,m_refSize,m_refSize);//画圆

    //绘制圆形渐变色
    QConicalGradient conical(0, 0, 0);
    conical.setColorAt(0,Qt::red);
    conical.setColorAt(0.3,Qt::yellow);
    conical.setColorAt(0.5,Qt::green);
    conical.setColorAt(1,Qt::white);

    painter.setPen(Qt::transparent);
    painter.setBrush(conical);
    painter.drawEllipse(QPoint(0,0),95,95);

    //白色覆盖下半圆
    QPen pen;
    pen.setCapStyle(Qt::FlatCap);
    pen.setWidthF(10);
    pen.setColor(Qt::white);
    painter.setPen(pen);
    QRectF rect = QRectF(-90, -90, 90 * 2, 90 * 2);
    painter.drawArc(rect, 0 * 16, -180 * 16);

    //中间白色圆圈覆盖
    painter.setBrush(Qt::white);
    painter.drawEllipse(QPoint(0,0),81,81);

    painter.restore();

}

//绘制刻度
void SpeedPanel::drawDividing(QPainter &painter)
{
    painter.save();
    painter.rotate(m_startAngle);//将坐标系顺时针旋转150°,到达起始位置

    QPen pen(Qt::black);
    painter.setPen(pen);

    int step = (m_maxSpeed - m_minSpeed) / 5;
    double angleStep = (360.0 - (m_startAngle - m_endAngle)) / step;

    for (int i = m_minSpeed; i <= m_maxSpeed; i += 5)
    {
        if (i >= 100){ //绘制红色
            pen.setColor(Qt::red);
            painter.setPen(pen);
        }

        if (i % 25 == 0){//粗线
            pen.setWidth(2);
            painter.setPen(pen);
            painter.drawLine(88,0,75,0);

        }else if (i % 10 == 0){//中等
            pen.setWidth(1);
            painter.setPen(pen);
            painter.drawLine(88,0,80,0);

        }else if (i % 5 == 0){ //短线
            pen.setWidth(0);
            painter.setPen(pen);
            painter.drawLine(83,0,80,0);
        }
        painter.rotate(angleStep);
    }

    painter.restore();
}

//绘制数字指示
void SpeedPanel::drawNumberIndicator(QPainter &painter)
{
    painter.save();

    painter.setPen(Qt::black);

    double x,y;
    double angle, angleArc;
    double w,h;
    QFontMetricsF fm(this->font());

    for (int i = m_minSpeed; i <= m_maxSpeed; i+= 25)//每隔20Km设置一个数字
    {
        angle = 360 - (m_startAngle + (i - m_minSpeed) * m_anglePerVel); //角度
        angleArc = angle * 3.14 / 180; //转换为弧度

        x = 65 * cos(angleArc);
        y = -65 * sin(angleArc); // 负号的意义在于 Y轴正方向向下

        QString speed = QString::number(i);

        if (i % 25 == 0)
        {
            w = fm.width(speed);
            h = fm.height();
            painter.drawText(QPointF(x - w / 2,y + h/4),speed);
        }
    }

    painter.restore();
}

void SpeedPanel::drawIndicator(QPainter &painter)
{
    //绘制指针
    painter.save();
    double curAngle = m_startAngle + m_curSpeed * m_anglePerVel;
    painter.rotate(curAngle); //旋转坐标系

    QRadialGradient haloGradient(0, 0, 60, 0, 0);  //辐射渐变
    haloGradient.setColorAt(0, QColor(200,60,60));
    haloGradient.setColorAt(1, QColor(0,0,0)); //灰
    painter.setPen(Qt::red); //定义线条文本颜色  设置线条的颜色//Qt::transparent
    painter.setBrush(haloGradient);//刷子定义形状如何填满 填充后的颜色
    static const QPointF points[3] = {
        QPointF(0.0, 3),
        QPointF(0.0, -3),
        QPointF(70.0, 0),
    };
    painter.drawPolygon(points,3); //绘制三角形指针
    painter.restore();


    //绘制旋转中心
    painter.save();
    QRadialGradient rg(0,0,10,0,0);
    rg.setColorAt(0.0,Qt::darkGray);
    rg.setColorAt(0.5,Qt::white);
    rg.setColorAt(1.0,Qt::darkGray);
    painter.setPen(Qt::NoPen);
    painter.setBrush(rg);
    painter.drawEllipse(QPoint(0,0),10,10);

    painter.restore();
}

void SpeedPanel::drawNumberSpeed(QPainter &painter)
{
    painter.save();
    painter.setPen(Qt::black);
    QString speed = QString("%1").arg(m_true_curSpeed);
    QFontMetricsF fm(this->font());
    qreal w = fm.size(Qt::TextSingleLine,speed).width();
    painter.drawText(-w/2,40,speed);

    QFontMetricsF fm1(this->font());
    qreal w2 = fm1.size(Qt::TextSingleLine,title).width();
    painter.drawText(-w2/2,70,title);

    painter.restore();
}

之后就是预留的接口部分,我留了一个设置范围的接口,这块一开始也不小心反了错误,在外部程序调用此空间后,范围计算的

角度使用初始化时的范围,并不是外部修改的范围, 然后导致程序显示不正常,在修改范围后重新计算一下没个单位对应的角度即可。

void SpeedPanel::setRange(int minValued, int maxValued)
{
    m_minSpeed=minValued;                //最小值
    m_maxSpeed=maxValued;                //最大值
    m_anglePerVel = (360.0 - (m_startAngle - m_endAngle)) / (m_maxSpeed - m_minSpeed);
    update();
}

 

四、结语

我心坐标,坐标我心。

你可能感兴趣的:(qt,MyCustomWidget)