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

一、前言

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

二、环境

qt5.7 MinGW

windows8

三、正文

首先 演示一下效果图

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

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

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

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

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编写一个旋转仪表盘控件。 首先,我们需要在Qt中创建一个新的自定义控件类。可以通过Qt Creator中的“添加新文件”功能来创建一个QWidget派生类。在这个类中,我们需要实现paintEvent()函数来绘制仪表盘。 在paintEvent()函数中,我们可以使用QPainter来绘制仪表盘的各个部分,包括刻度线、指针、文字等。具体实现可以参考以下代码: ```C++ void RotatingDial::paintEvent(QPaintEvent *event) { // 设置背景色 QPalette pal(palette()); pal.setColor(QPalette::Background, Qt::white); setAutoFillBackground(true); setPalette(pal); // 绘制刻度线 QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.translate(width() / 2, height() / 2); painter.setPen(QPen(Qt::black, 2)); for (int i = 0; i < 60; i++) { painter.drawLine(0, -100, 0, -90); painter.rotate(6); } // 绘制指针 painter.save(); painter.rotate(m_angle); painter.setBrush(Qt::red); painter.setPen(Qt::NoPen); painter.drawConvexPolygon(m_pointer, 3); painter.restore(); // 绘制文字 QFont font("Arial", 12, QFont::Bold); painter.setFont(font); painter.drawText(-30, 50, "Speed"); } ``` 在上面的代码中,我们首先设置了控件的背景色为白色。然后使用QPainter绘制了60条刻度线,并旋转6度。接着,我们绘制了一个红色的指针,并根据m_angle属性来旋转指针的角度。最后,我们使用QPainter绘制了文字“Speed”。 在我们的自定义控件中,我们需要一个属性来控制指针的角度。我们可以使用Q_PROPERTY宏来定义这个属性,例如: ```C++ class RotatingDial : public QWidget { Q_OBJECT Q_PROPERTY(int angle READ angle WRITE setAngle) public: RotatingDial(QWidget *parent = nullptr); int angle() const; void setAngle(int angle); private: int m_angle; QPolygon m_pointer; }; ``` 在上面的代码中,我们使用Q_PROPERTY宏定义了angle属性,并提供了getter和setter函数。我们还定义了一个私有变量m_angle来存储当前的角度,以及一个QPolygon对象m_pointer来存储指针的形状。 在setAngle()函数中,我们设置m_angle属性,并根据新的角度计算指针的位置: ```C++ void RotatingDial::setAngle(int angle) { if (angle != m_angle) { m_angle = angle; m_pointer.setPoint(0, QPoint(0, -90)); m_pointer.setPoint(1, QPoint(5, 0)); m_pointer.setPoint(2, QPoint(-5, 0)); m_pointer.translate(0, 100); update(); } } ``` 在上面的代码中,我们首先判断新的角度是否与当前的角度相同。如果不同,我们就更新m_angle属性,并重新计算指针的位置。最后,我们调用update()函数来触发paintEvent()函数的调用,从而完成控件的重绘。 最后,我们可以在MainWindow类中使用我们的自定义控件,例如: ```C++ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { RotatingDial *dial = new RotatingDial(this); dial->setAngle(30); QHBoxLayout *layout = new QHBoxLayout; layout->addWidget(dial); setCentralWidget(new QWidget); centralWidget()->setLayout(layout); } ``` 在上面的代码中,我们创建了一个RotatingDial对象,并设置了初始角度为30度。然后,将其添加到QHBoxLayout布局中,并设置为主窗口的中央控件。 至此,我们已经完成了一个简单的旋转仪表盘自定义控件。你可以根据需要对其进行扩展和优化,使其更加适合你的应用场景。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大桶矿泉水

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

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

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

打赏作者

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

抵扣说明:

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

余额充值