汽车仪表盘项目

一、自定义控件

1.1 QPaintEvent绘图事件


QPaintEvent 是 Qt 框架中一个重要的事件类,专门用于处理绘图事件。当 Qt 视图组件需要重绘自己的一部分时,就会产生QPaintEvent事件。这通常发生在以下几种情况:

  1. 窗口第一次显示时:当窗口或控件第一次出现在屏幕上时,系统会生成一个 QPaintEvent 事件,通知窗口进行自身的绘制。
  2. 窗口大小改变时:当用户改变窗口的大小时,窗口的内容通常需要重新绘制以适应新的尺寸。
  3. 窗口部分被遮挡后又重新显示时:如果窗口被其他窗口遮挡,然后又重新露出来,被遮挡的部分通常需要重新绘制。
  4. 手动请求重绘:通过调用 QWidget 的 update() 或 repaint() 方法,可以手动触发重绘事件。
  • 新建一个项目

    在这里插入图片描述

  • 继承widget

    在这里插入图片描述

  • 随便拖入几个控件

    在这里插入图片描述

在 Qt 应用程序中,通常通过重写 QWidget 的 paintEvent(QPaintEvent *) 方法来处理绘制逻辑。

例如:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
     // 【事件处理函数】重写父类的绘图事件。每当界面需要重绘时,Qt 会自动调用此函数
    void paintEvent(QPaintEvent *event) override;

private slots:
    // 【槽函数】响应界面上“pushButton”按钮的点击事件
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QTimer *timer;// 【指针】定时器对象指针,用于周期性触发任务
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QTimer>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    timer = new QTimer();// 【创建定时器】在堆区动态创建一个 QTimer 对象
    // 【连接信号与槽】使用 Lambda 表达式作为槽函数
    // 当 timer 发出 timeout(超时)信号时,执行大括号内的匿名函数
    connect(timer,&QTimer::timeout,this,[=](){
        //update();//会向系统发送重绘请求,由系统在空闲时合并处理(
        repaint();//会向系统发送重绘请求,由系统在空闲时合并处理(推荐使用)
    });
    timer->setInterval(1000);// 【设置间隔】将定时器的时间间隔设置为 1000 毫秒(即 1 秒)
    timer->start(); // 【启动定时器】定时器开始工作,每隔 1 秒就会触发一次 timeout 信号
}

Widget::~Widget()
{
    delete ui;
}
// 【绘图事件处理函数实现】
void Widget::paintEvent(QPaintEvent *event)
{
    // 【静态变量】定义一个静态整型变量 i,初始化为 0。它的值在函数调用结束后不会销毁,下次调用会保留
    static int i = 0;
    // 【调试输出】在控制台打印 "paintEvent" 和当前 i 的值,然后让 i 自增 1
    // 通过观察 i 的变化,可以直观地看到 paintEvent 被触发了多少次
    qDebug() << "paintEvent" << i++;
}
// 【按钮点击槽函数实现】
void Widget::on_pushButton_clicked()
{
    update();  // 【请求重绘】调用 update() 函数。它不会立即重绘,而是向 Qt 事件循环发送一个重绘请求
               // 当主线程空闲时,Qt 会自动调用 paintEvent 函数来刷新界面
}

在 paintEvent 方法中,您可以创建一个 QPainter 对象并使用它来执行绘制操作。 QPainter 可以绘制各种基本图形,如线条、矩形、椭圆等,还可以绘制文本和图像。重写 paintEvent 是在 Qt 中进行自定义绘制的标准做法。


1.2 QPainter画家


1.2.1 概述

QPainter 是 Qt 库中用于在屏幕上进行绘画的类。它提供了各种绘制功能,比如画线、画图形、画文本等。

以下是一些基本的用法示例:

  1. 初始化 QPainter:首先,您需要一个 QPaintDevice ,比如一个 QWidget 或 QPixmap ,然后使用它来初始化 QPainter 对象。
QPainter painter(this); // 假设在 QWidget 的子类中

设置画笔和画刷:您可以设置画笔(用于描边)和画刷(用于填充)的颜色、样式等。

painter.setPen(Qt::blue); // 设置画笔颜色为蓝色
painter.setBrush(Qt::yellow); // 设置画刷颜色为黄色

绘制图形:使用 QPainter 的方法来绘制线条、矩形、圆形、文本等。

painter.drawLine(10, 10, 100, 100); // 画线
painter.drawRect(10, 10, 100, 100); // 画矩形
painter.drawText(10, 10, "Hello, Qt!"); // 画文本

结束绘制:完成绘制后, QPainter 对象会在其析构函数中自动结束绘制。

请注意, QPainter 的使用依赖于 Qt 的事件循环,因此通常在 QWidget 的 paintEvent 或者类似的事件处理函数中使用它。如果您在 Qt 应用程序中使用 QPainter ,请确保您遵循 Qt 的事件驱动机制。


1.2.2 QPainter画文字

代码示例(1.1的工程):

void Widget::paintEvent(QPaintEvent *event)
{
    // 【创建画家对象】实例化一个 QPainter 对象,传入 this(当前 Widget 窗口)作为参数,表示“我要在这个窗口上画画”
    QPainter painter(this);
    // 【设置画笔】将画笔颜色设置为蓝色,后续绘制的文字、线条等轮廓都会是蓝色
    painter.setPen(Qt::blue);
    // 【设置字体】设置后续绘制文字时使用的字体为 Arial,字号为 30 像素
    painter.setFont(QFont("Arial", 30));
     // 【绘制居中大字】在指定的矩形区域(rect() 即整个窗口的大小)内,按照居中对齐(Qt::AlignCenter)的方式,绘制字符串 "Qt的案例"
    painter.drawText(rect(), Qt::AlignCenter, "Qt的案例");


    //void
    //drawText(int x, int y, const QString &text)
    //void
    //drawText(const QRectF &rectangle, int flags, const QString &text, QRectF *boundingRect = nullptr)
    //void
    //drawText(const QRect &rectangle, int flags, const QString &text, QRect *boundingRect = nullptr)
    //void
    //drawText(int x, int y, int width, int height, int flags, const QString &text, QRect *boundingRect = nullptr)
    
    // 【在指定小矩形内绘制文字】在坐标 (100, 200) 处,划定一个宽 80、高 80 的矩形区域,在此区域内居中对齐绘制文字 "Qt"
    painter.drawText(100,200,80,80,Qt::AlignCenter, "Qt");
    // 【在指定坐标绘制文字】在绝对坐标 (20, 40) 的位置(即距离窗口左边 20 像素,距离顶部 40 像素的起点)直接绘制文字 "hello"
    painter.drawText(20,40,"hello");

    // 【调试输出】在控制台打印当前窗口的宽度和高度,方便开发者观察窗口尺寸的变化
    qDebug() << rect().width() << "," <<rect().height();
}

画出来的效果

在这里插入图片描述


1.2.3 QPainter画线画矩形画椭圆
画线的代码示例:
void Widget::paintEvent(QPaintEvent *event)
{
    // 【创建画家对象】实例化一个 QPainter 对象,传入 this(当前 Widget 窗口)作为参数,表示“我要在这个窗口上画画”
    QPainter painter(this);
     // 【设置画笔】将画笔颜色设置为蓝色,后续绘制的线条、文字轮廓都会是蓝色
    painter.setPen(Qt::blue);
     // 【设置字体】设置后续绘制文字时使用的字体为 Arial,字号为 30 像素
    painter.setFont(QFont("Arial", 30));
     // 【绘制居中大字】在指定的矩形区域(rect() 即整个窗口的大小)内,按照居中对齐(Qt::AlignCenter)的方式,绘制字符串 "Qt的案例"
    //painter.drawText(rect(), Qt::AlignCenter, "Qt的案例");

	//画线五种方式drawLine
    //void
    //drawLine(const QLineF &line)浮点型(小数型)
    //void
    //drawLine(const QLine &line)整型
    //void
    //drawLine(int x1, int y1, int x2, int y2)
    //void
    //drawLine(const QPoint &p1, const QPoint &p2)整型
    //void
    //drawLine(const QPointF &p1, const QPointF &p2)浮点型(小数型)

    //画线
    // 【画线方式1:直接传坐标】从点 (40, 200) 画一条直线到点 (300, 60)
    painter.drawLine(40,200,300,60);
    // 【画线方式2:使用 QLine 对象】创建一个 QLine 对象(起点(30,100),终点(200,10)),并绘制这条线
    painter.drawLine(QLine(30,100,200,10));
    // 【画线方式3:使用 QPoint 对象】传入两个 QPoint 对象(起点(10,200),终点(300,20))来绘制直线
    painter.drawLine(QPoint(10,200),QPoint(300,20));

}

效果图

在这里插入图片描述


画矩形代码示例:
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setPen(Qt::blue);
    painter.setFont(QFont("Arial", 30));
    //painter.drawText(rect(), Qt::AlignCenter, "Qt的案例");

    //画矩形drawRect,三种方式(单个图形)
    //void
    //drawRect(const QRect &rectangle)
            //x坐标,Y坐标,宽,高
    // 【画矩形方式1:传入 QRect 对象】
    // 先创建一个 QRect 矩形对象,参数依次代表:左上角 X 坐标、左上角 Y 坐标、矩形的宽度、矩形的高度
    QRect rectangle(20,30,100,80);
    // 调用 drawRect,传入上面定义好的矩形对象进行绘制
    painter.drawRect(rectangle);
    //void
    //drawRect(int x, int y, int width, int height)
    // 【画矩形方式2:直接传入坐标和宽高】
    // 直接在 drawRect 函数中传入四个参数:起点 X 坐标(130)、起点 Y 坐标(120)、宽度(300)、高度(220)
    painter.drawRect(130,120,300,220);
    //void
    //drawRect(const QRectF &rectangle)
}

效果图

在这里插入图片描述


画椭圆代码示例:
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setPen(Qt::blue);
    painter.setFont(QFont("Arial", 30));
    //painter.drawText(rect(), Qt::AlignCenter, "Qt的案例");

    //画椭圆drawEllipse
    //void
    //drawEllipse(const QRectF &rectangle)
    //void
    //drawEllipse(const QRect &rectangle)
    //void
    //drawEllipse(int x, int y, int width, int height)
    //void
    //drawEllipse(const QPointF &center, qreal rx, qreal ry)
    //void
    //drawEllipse(const QPoint &center, int rx, int ry)

    // 【画椭圆方式1:传入 QRect 对象(外接矩形法)】
    // 创建一个左上角坐标为(200, 100),宽度为200,高度为100的矩形对象
    QRect rectangle(200,100,200,100);
    // 在这个矩形内部绘制一个紧密贴合的椭圆
    painter.drawEllipse(rectangle);
    
    // 【画椭圆方式2:传入中心点和半径(中心半径法)】
    // 以当前窗口的中心点(rect().center())为圆心,水平半径为200,垂直半径为100,绘制一个椭圆
    painter.drawEllipse(rect().center(),200,100);
    // 【画圆(特殊的椭圆)】
    // 以坐标点(100, 500)为圆心,水平半径和垂直半径都为80。当两个半径相等时,绘制出来的就是一个标准的圆
    painter.drawEllipse(QPoint(100,500),80,80);

}

效果图

在这里插入图片描述

1.2.4 QPaint画弧画扇形
画弧形代码示例:
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setPen(Qt::blue);
    painter.setFont(QFont("Arial", 30));
    painter.drawText(rect(), Qt::AlignCenter, "Qt的案例");

    //画弧形drawArc
    //void
    //drawArc(const QRectF &rectangle, int startAngle, int spanAngle)
    //void
    //drawArc(const QRect &rectangle, int startAngle, int spanAngle)
    //void
    //drawArc(int x, int y, int width, int height, int startAngle, int spanAngle)

    // 【画弧形方式1:传入 QRectF 对象】
    // 创建一个左上角坐标为(10.0, 20.0),宽度为80.0,高度为60.0的浮点型矩形,弧线将在这个矩形内绘制
    QRectF rectangle(10.0, 20.0, 80.0, 60.0);
     // 设置起始角度为 30 度。注意:Qt 中角度单位是 1/16 度,所以实际参数需要乘以 16
    int startAngle = 30 * 16;
    // 设置跨度角度为 120 度(即从起始位置开始,逆时针扫过 120 度的范围)
    int spanAngle = 120 * 16;
    // 调用 drawArc 绘制弧线
    painter.drawArc(rectangle, startAngle, spanAngle);

    // 【画弧形方式2:直接传入坐标、宽高和角度】
    // 在左上角(30,100),宽100、高180的矩形区域内绘制弧线
    // 起始角度为 45 度,跨度为 90 度(同样需要乘以 16)
    painter.drawArc(30,100,100,180,45*16,90*16);
}

效果图

在这里插入图片描述


画扇形代码示例
void Widget::paintEvent(QPaintEvent *event)
{
    // 【创建画家对象】在当前 Widget 窗口上实例化一个 QPainter 画家
    QPainter painter(this);
    // 【开启抗锯齿】让绘制的图形边缘更加平滑,消除锯齿感,提升视觉质量
    painter.setRenderHint(QPainter::Antialiasing,true);
    // 【创建自定义画笔】创建一个红色的画笔对象,并将线条宽度设置为 7 像素
    QPen pen(Qt::red,7);
    // 【设置画笔】将配置好的粗红色画笔应用到画家上(后续绘制的扇形轮廓将是粗红线)
    painter.setPen(pen);
    //painter.setPen(Qt::blue);

    //画弧形drawPie
    //void
    //drawPie(const QRectF &rectangle, int startAngle, int spanAngle)
    //void
    //drawPie(int x, int y, int width, int height, int startAngle, int spanAngle)
    //void
    //drawPie(const QRect &rectangle, int startAngle, int spanAngle)

    // 【画扇形方式1:传入 QRectF 对象】
    // 创建一个左上角坐标为(10.0, 20.0),宽度为80.0,高度为60.0的浮点型矩形,扇形将在这个椭圆区域内绘制
    QRectF rectangle(10.0, 20.0, 80.0, 60.0);
    // 设置起始角度为 30 度(Qt中角度单位是 1/16 度,需乘以 16)
    int startAngle = 30 * 16;
    // 设置跨度角度为 120 度(从起始位置开始,逆时针扫过 120 度的范围)
    int spanAngle = 120 * 16;
    // 调用 drawPie 绘制一个闭合的扇形
    painter.drawPie(rectangle, startAngle, spanAngle);

    // 【画扇形方式2:直接传入坐标、宽高和角度】
    // 在左上角(30,100),宽100、高180的矩形区域内绘制扇形
    // 起始角度为 45 度,跨度为 90 度(同样需要乘以 16)
    painter.drawPie(30,100,100,180,45*16,90*16);

}

效果图

在这里插入图片描述


1.3 QGradient渐变色

1.3.1 QLinearGradient线性渐变

QLinearGradient 是 Qt 框架中用于创建线性渐变的类。线性渐变是一种从一个颜色平滑过渡到另一个颜色的效果,其变化沿着两个点之间的直线进行。这种渐变在图形用户界面设计中非常常见,用于添加深度、立体感或动态效果。

基本用法

要使用 QLinearGradient ,你需要执行以下几个基本步骤:

  1. 创建 QLinearGradient 对象:指定渐变的起点和终点坐标。

  2. 设置颜色停靠点:在渐变线上定义颜色和相应的位置。

  3. 使用渐变创建 QBrush :用 QLinearGradient 对象来创建一个 QBrush ,然后用它在 QPainter中进行绘制。

    #include <QPainter>
    #include <QLinearGradient>
    void MyWidget::paintEvent(QPaintEvent *) {
    // 创建一个 QLinearGradient 对象
    QLinearGradient linearGradient(0, 0, 100, 100); // 起点(0, 0) 终点(100, 100)
    // 设置颜色停靠点
    linearGradient.setColorAt(0.0, Qt::red); // 起点颜色
    linearGradient.setColorAt(1.0, Qt::blue); // 终点颜色
    // 使用这个渐变创建 QBrush
    QBrush brush(linearGradient);
    // 使用 QBrush 进行绘图
    QPainter painter(this);
    painter.setBrush(brush);
    painter.setPen(Qt::NoPen); // 无边框
    painter.drawRect(this->rect()); // 绘制矩形覆盖整个小部件
    }
    

    在这个例子中, QLinearGradient 创建了一个从红色到蓝色的渐变,其方向是从小部件的左上角 (0, 0)到右下角 (100, 100)。

    注意事项

    • QLinearGradient 的颜色变化是沿着两个指定点之间的直线进行的。通过改变这些点的位置,你可以控制渐变的方向和长度。

    • setColorAt() 方法的第一个参数是一个介于 0.0 和 1.0 之间的浮点数,表示颜色在渐变线上的位置。0.0 通常对应于起点,1.0 对应于终点。

    • 你可以设置多个颜色停靠点来创建更复杂的渐变效果。例如,你可以在 0.0 处设置一种颜色,在 0.5处设置另一种颜色,在 1.0 处再设置一种颜色。

    • 使用 QLinearGradient 创建的 QBrush 可以用于填充任何形状,包括矩形、椭圆、多边形等。

示例代码

以下是一个创建和使用 QLinearGradient 的示例代码:

void Widget::paintEvent(QPaintEvent *event)
{
     // 【创建画家对象】在当前 Widget 窗口上实例化一个 QPainter 画家
    QPainter painter(this);
    // 【创建线性渐变对象】
    // 起点坐标为 (窗口宽度的一半, 0),即窗口顶部的水平中心点
    // 终点坐标为 (窗口宽度的一半, 窗口高度),即窗口底部的水平中心点
    // 这定义了一条从上到下的垂直渐变轴线
    QLinearGradient lineGradient(width()/2,0,width()/2,height());
    // 【设置颜色停靠点 (Color Stops)】
    // 在渐变位置 0.1 (顶部 10% 处),设置颜色为完全不透明的黑色。
    // QColor(0,0,0,255) 中的最后一个参数 255 代表 Alpha 通道(透明度),255 表示完全不透明
    lineGradient.setColorAt(0.1,QColor(0,0,0,255));
    // 在渐变位置 0.3 (顶部 30% 处),设置颜色为半透明的黑色(透明度降为 180)。
    lineGradient.setColorAt(0.3,QColor(0,0,0,180));
     // 在渐变位置 0.5 (窗口正中间),设置颜色为更透明的黑色(透明度降为 100)。
    lineGradient.setColorAt(0.5,QColor(0,0,0,100));
    // 在渐变位置 1 (窗口最底部),设置颜色为 Qt 自带的纯白色(完全不透明)
    lineGradient.setColorAt(1,Qt::white);

    // 【创建画刷】将上面配置好的线性渐变对象装入 QBrush 画刷中
    QBrush brush(lineGradient);
    // 【设置画刷】将带有渐变色的画刷交给画家,准备用来填充图形
    painter.setBrush(brush);
    // 【绘制矩形】rect() 返回当前窗口的整个大小,这里表示用渐变画刷填充整个窗口背景
    painter.drawRect(rect());
}

最后效果

在这里插入图片描述


1.3.2 QRadialGradient径向渐变

QRadialGradient 是 Qt 框架中用于创建径向渐变的类。径向渐变是一种从中心点向外部辐射的颜色渐变,通常在中心点有一种颜色,而向外围渐渐变化为另一种颜色。这种渐变非常适合用于模拟光源、阴影或创建圆形的立体感。

基本用法

要使用 QRadialGradient ,你需要执行以下几个基本步骤:

  1. 创建 QRadialGradient 对象:指定渐变的中心点、半径以及焦点(可选)。
  2. 设置颜色停靠点:在径向渐变中定义颜色和对应的位置。
  3. 使用渐变创建 QBrush :利用 QRadialGradient 对象创建一个 QBrush ,然后用它在 QPainter 中进行绘制。

以下是一个创建和使用 QRadialGradient 的示例代码:

#include <QPainter>
#include <QRadialGradient>
void MyWidget::paintEvent(QPaintEvent *) {
// 创建一个 QRadialGradient 对象
QRadialGradient radialGradient(50, 50, 50); // 中心和半径 (50, 50, 50)
// 可选:设置焦点
// radialGradient.setFocalPoint(30, 30);
// 设置颜色停靠点
radialGradient.setColorAt(0.0, Qt::yellow); // 中心颜色
radialGradient.setColorAt(1.0, Qt::black); // 外围颜色
// 使用这个渐变创建 QBrush
QBrush brush(radialGradient);
// 使用 QBrush 进行绘图
QPainter painter(this);
painter.setBrush(brush);
painter.setPen(Qt::NoPen); // 无边框
painter.drawRect(this->rect()); // 绘制矩形覆盖整个小部件
}

在这个例子中, QRadialGradient 创建了一个从中心的黄色向外围的黑色渐变。渐变的中心和半径都设置在 (50, 50, 50)。

注意事项

  • setColorAt() 方法的第一个参数是一个介于 0.0 和 1.0 之间的浮点数,表示颜色在径向渐变中的位置。0.0 通常对应于中心点,1.0 对应于边缘。
  • 通过添加多个颜色停靠点,你可以创建更复杂的径向渐变效果。
  • setFocalPoint() 方法允许你设置焦点位置,这是渐变颜色开始变化的点,可以与中心点不同。
  • 使用 QRadialGradient 创建的 QBrush 可以用于填充任何形状,如矩形、椭圆、多边形等。
  • 为了获得更好的视觉效果,可以启用 QPainter 的抗锯齿选项( QPainter::Antialiasing )。
  • 当绘制较大区域时,可以通过调整渐变的半径和中心点来控制渐变效果的扩展。
  • QRadialGradient 非常适用于创建像按钮、指示灯或其他需要有深度感和立体感的界面元素。

示例代码

以下是一个创建和使用 QLinearGradient 的示例代码:

void Widget::paintEvent(QPaintEvent *event)
{
    // 【创建画家对象】在当前 Widget 窗口上实例化一个 QPainter 画家
    QPainter painter(this);
    
    // 【创建径向渐变对象】
    // 参数分别为:圆心X坐标(400)、圆心Y坐标(400)、渐变半径(200)
    // 这意味着渐变的中心点在 (400, 400),颜色将从该点向外扩散,直到距离圆心200像素的圆周处结束
    QRadialGradient radialGradient(400,400,200);
    // 【设置颜色停靠点 (Color Stops)】
    // 在渐变位置 0.1 (距离圆心 10% 半径的位置),设置颜色为纯白色
    radialGradient.setColorAt(0.1,Qt::white);
    / 在渐变位置 1 (距离圆心 100% 半径的最外圈边缘),设置颜色为纯黑色
    // Qt 会自动计算 0.1 到 1 之间的颜色,形成从白到黑的平滑过渡
    radialGradient.setColorAt(1,Qt::black);
     // 【设置画刷并绘制】
    // 将配置好的径向渐变对象装入 QBrush 画刷中,并直接交给画家设置
    painter.setBrush(QBrush(radialGradient));
     // 【绘制矩形】
    // 在坐标 (200, 300) 处,绘制一个宽 400、高 200 的矩形
    // 矩形内部会被刚才定义的径向渐变色填充
    painter.drawRect(200,300,400,200);
   
}

效果图

在这里插入图片描述


1.3.3 QConicalGradient圆锥形渐变

QConicalGradient 是 Qt 框架中用于创建圆锥形渐变的类。圆锥渐变是一种渐变效果,其中颜色沿着圆锥的轮廓变化,类似于旋转颜色轮。这种渐变以其中心点为基点,颜色沿圆周分布,可以创建出富有动感的视觉效果。

基本用法

要使用 QConicalGradient ,你通常需要做以下几个步骤:

  1. 创建 QConicalGradient 对象:指定渐变的中心点和起始角度。
  2. 设置颜色停靠点:为渐变添加不同的颜色和对应的位置(角度)。
  3. 使用渐变创建 QBrush :使用这个渐变对象来创建一个 QBrush ,然后应用到 QPainter 中进行绘图。
#include <QPainter>
#include <QConicalGradient>
void MyWidget::paintEvent(QPaintEvent *) {
// 创建一个 QConicalGradient 对象
QConicalGradient conicalGradient(100, 100, 0); // 中心点 (100, 100),起始角度 0
// 添加颜色停靠点
conicalGradient.setColorAt(0.0, Qt::red);
conicalGradient.setColorAt(0.5, Qt::blue);
conicalGradient.setColorAt(1.0, Qt::red);
// 使用这个渐变创建 QBrush
QBrush brush(conicalGradient);
// 使用 QBrush 进行绘图
QPainter painter(this);
painter.setBrush(brush);
painter.setPen(Qt::NoPen); // 无边框
painter.drawRect(this->rect()); // 绘制矩形覆盖整个小部件
}

在这个例子中, QConicalGradient 被用来创建一个从红色到蓝色再回到红色的渐变。渐变的中心设置在点 (100, 100),并且从 0 度开始旋转。

注意事项

  • QConicalGradient 的颜色是沿着圆周分布的,其中 0.0 和 1.0 在圆周上是相同的位置。
  • 你可以通过添加多个颜色停靠点来创建更复杂的渐变效果。
  • QConicalGradient在使用时,角度是按照顺时针方向测量的,起始点(0度)通常在三点钟方向。
  • 为了达到最佳的渲染效果,可以启用 QPainter 的抗锯齿渲染提示( QPainter::Antialiasing )。
  • QConicalGradient 非常适合用于创建旋转或动态效果的图形,例如加载指示器、进度条或任何需要圆周颜色变化的场景。

示例代码

以下是一个创建和使用 QLinearGradient 的示例代码:

void Widget::paintEvent(QPaintEvent *event)
{
    // 【创建画家对象】在当前 Widget 窗口上实例化一个 QPainter 画家
    QPainter painter(this);
    / 【创建锥形渐变对象】
    // 参数分别为:中心点X坐标(400)、中心点Y坐标(400)、起始角度(0)
    // 锥形渐变会围绕这个中心点 (400, 400) 进行顺时针的颜色扫描
    QConicalGradient conicalGradient(400,400,0);
    / 【设置颜色停靠点 (Color Stops)// 在渐变位置 0.0 (即 0 度,通常是圆心的 3 点钟方向),设置颜色为红色
    conicalGradient.setColorAt(0.0,Qt::red);
    // 在渐变位置 0.5 (即转过半圈 180 度,圆心的 9 点钟方向),设置颜色为蓝色
    conicalGradient.setColorAt(0.5,Qt::blue);
    // 在渐变位置 1 (即转完一整圈 360 度,回到 3 点钟方向),再次设置为红色
    // 这样首尾颜色一致,可以形成完美的闭环过渡
    conicalGradient.setColorAt(1,Qt::red);
    // 【设置画刷并绘制】
    // 将配置好的锥形渐变对象装入 QBrush 画刷中,并直接交给画家设置
    painter.setBrush(QBrush(conicalGradient));
    // 【绘制矩形】
    // 在坐标 (200, 300) 处,绘制一个宽 400、高 200 的矩形
    // 矩形内部会被刚才定义的锥形渐变色填充
    painter.drawRect(200,300,400,200);

}

效果图

在这里插入图片描述


1.3.4 在矩形里边画弧形

示例代码

void Widget::paintEvent(QPaintEvent *event)
{
     // 【创建画家对象】在当前 Widget 窗口上实例化一个 QPainter 画家
    QPainter painter(this);
    // 【开启抗锯齿】让绘制的扇形边缘更加平滑,消除锯齿感,提升视觉质量
    painter.setRenderHint(QPainter::Antialiasing);
     // 【创建径向渐变对象】
    // 圆心坐标设为 (窗口宽度一半, 窗口高度一半),即窗口的正中心
    // 渐变半径设为 200 像素
    // 渐变效果将从窗口中心向外辐射
    QRadialGradient radialGradient(width()/2,height()/2,200);
    // 【设置颜色停靠点 (Color Stops)】
    // 在渐变位置 0.1 (距离圆心 10% 半径的位置),设置颜色为红色
    radialGradient.setColorAt(0.1,Qt::red);
    // 在渐变位置 1 (距离圆心 100% 半径的最外圈边缘),设置颜色为黄色
    // Qt 会自动计算 0.1 到 1 之间的颜色,形成从红到黄的平滑放射状过渡
    radialGradient.setColorAt(1,Qt::yellow);
    // 【设置画刷】将配置好的径向渐变对象装入 QBrush 画刷中,并交给画家
    painter.setBrush(QBrush(radialGradient));
      // 【设置画笔】使用 Qt::NoPen 表示不绘制任何边框或轮廓线
    // 这样绘制出来的扇形只有填充色,没有边界线
    painter.setPen(Qt::NoPen);
    // 【绘制扇形】
    // 第一个参数 rect():使用整个窗口的矩形区域作为扇形的外接椭圆边界
    // 第二个参数 30*16:起始角度为 30 度(Qt中角度单位是 1/16 度,需乘以 16)
    // 第三个参数 120*16:跨度角度为 120 度(从起始角度开始,逆时针扫过 120 度的范围)
    // 整体效果:在窗口中心创建一个从红到黄的放射状渐变,并只绘制其中 30° 到 150° (30+120) 这个角度范围内的扇形
    painter.drawPie(rect(),30*16,120*16);
}

核心知识点总结

  1. 径向渐变与扇形的结合
    • QRadialGradient 定义了一个以窗口中心为圆心的放射状颜色场。
    • drawPie 则在这个颜色场中“切割”出一个特定角度的扇形区域。
    • 最终结果是,这个扇形内部的颜色不再是单一的,而是呈现出从中心向外的红到黄的放射状渐变效果。
  2. setPen(Qt::NoPen) 的作用
    通过设置 Qt::NoPen,可以绘制出一个没有边框的纯净扇形。如果不清除画笔,扇形的弧边和两条半径线会以默认的黑色(或之前设置的画笔颜色)绘制出来,破坏渐变的整体视觉效果。
  3. rect() 作为外接矩形
    使用 rect() 作为 drawPie 的第一个参数,意味着扇形的外轮廓将是一个与窗口大小相同的椭圆(如果窗口不是正方形的话)。
  4. 抗锯齿 (Antialiasing)
    对于带有曲线的图形(如这里的扇形弧边),开启抗锯齿能让边缘更加柔和自然,显著提升图形的视觉品质。

效果图

在这里插入图片描述


二、实现一个仿雷达扫描器

2.1 translate坐标转移

在 Qt 框架中, painter.translate(rect().center()) 这行代码的作用是移动 QPainter 的坐标系统原点到当前绘制区域(即由 rect() 返回的矩形)的中心。

解释一下各个部分:

  1. painter : 这是一个 QPainter 对象实例,用于在 Qt 窗口或者图像上进行绘制。
  2. translate() : 这是 QPainter 类中的一个方法,用于改变坐标系统的原点。它接受一个 QPoint 或QPointF 作为参数,这个点指定了新的原点位置。
  3. rect() : 这通常是指一个控件(如 QWidget)的矩形区域,返回一个 QRect 或 QRectF 对象,表示该控件的大小和位置。
  4. rect().center() : 这个方法返回当前矩形(即控件的区域)的中心点,是一个 QPoint 或QPointF 对象。

总之, painter.translate(rect().center()) 这行代码将 QPainter 的绘图原点移动到控件的中心。

这在进行中心对称绘制或者需要以控件中心为基准进行绘图时特别有用。


void 
translate(const QPointF &offset)
void 
translate(const QPoint &offset)
void 
translate(qreal dx, qreal dy)


2.2 画雷达案例

代码示例:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    // 【重写绘图事件】当窗口需要刷新(如调用 update())时,Qt 会自动调用此函数进行绘制
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
    QTimer *timer;// 【定时器指针】用于周期性地触发信号,驱动雷达旋转动画
    int startAngle = 0; // 【起始角度变量】记录当前雷达扫描线的角度,初始值为 0
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QPainter>
#include <QTimer>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
     // 【实例化定时器】创建 QTimer 对象,并指定 this(当前窗口)为父对象,这样窗口销毁时定时器会自动释放
    timer = new QTimer(this);
    // 当 timer 发出 timeout() 信号(即定时时间到)时,执行大括号内的代码
    connect(timer,&QTimer::timeout,[=](){
        startAngle += 30;// 【更新角度】每次定时器触发,将扫描起始角度增加 30 度
        if(startAngle >= 360){ // 【角度归零判断】如果角度大于等于 360 度(转完一圈)
            startAngle = 0;// 将角度重置为 0,保证角度值始终在 0-359 之间
        }
        // 【请求重绘】调用 update() 会通知 Qt 系统:界面数据已更新,请尽快调用 paintEvent 重新绘制窗口
        update();
    });

    timer->setInterval(100);// 【设置定时器间隔】设置每 100 毫秒(即 0.1 秒)触发一次 timeout 信号
    timer->start();// 【启动定时器】开始计时

}

Widget::~Widget()
{
    delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    //kangjuchi
    painter.setRenderHint(QPainter::Antialiasing,true);
    //把背景色刷成黑色
    QBrush brush(Qt::black);// 【创建黑色画刷】
    painter.setBrush(brush); // 【设置画刷】将画家的填充工具设置为黑色画刷
    painter.drawRect(rect());// 【绘制背景矩形】rect() 返回当前窗口的整个大小,用黑色填满整个窗口作为雷达的底色

    //平移坐标轴到窗口的正中央
    painter.translate(rect().center());

    //最小圆的半径
    int rEve = height()/2/7; // 假设窗口高度的一半被分为 7 份,rEve 就是每一份(每一圈)的半径长度
    int dataTmp =rEve * 7;// 【计算最大半径】dataTmp 等于最外圈圆的半径(即窗口高度的一半)

    //设置画笔,绿色,像素3
    // 【绘制雷达网格线(同心圆和十字线)】
    QPen pen(Qt::green,3);
    painter.setPen(pen);
    painter.setBrush(Qt::NoBrush);//不要画刷,否则只能看到最外面的圈
    for(int i = 0;i <= 7;i++){
        painter.drawEllipse(QPoint(0,0),rEve*i,rEve*i);//依次画出七个圆形
    }
    // 【绘制十字坐标线】
    painter.drawLine(-rEve*7,0,rEve*7,0);// 画一条水平线,从最左侧 (-最大半径) 到最右侧 (+最大半径)
    painter.drawLine(0,-rEve*7,0,rEve*7);// 画一条垂直线,从最上方 (-最大半径) 到最下方 (+最大半径)

    //设置锥形渐变
    QConicalGradient conicalGradient(0,0,startAngle);//如果需要顺时针,只需要在"startAngle"前添加"-":-startAngle
    conicalGradient.setColorAt(0,QColor(0,255,0,200)); // 0 位置(扫描波的最前沿):高亮半透明绿色
    conicalGradient.setColorAt(0.1,QColor(0,255,0,100));// 转过 10% 圆周:较淡的半透明绿色
    conicalGradient.setColorAt(0.2,QColor(0,255,0,0)); // 转过 20% 圆周:完全透明绿色 (Alpha=0)
    conicalGradient.setColorAt(1,QColor(0,255,0,0));// 转过一整圈 (360度):保持完全透明,形成闭环

    //直接用渐变色指定画刷
    painter.setBrush(conicalGradient);
    painter.setPen(Qt::NoPen);//去除扇形区域边框
    //画出扇形,现在是逆时针方向旋转,如果需要顺时针,只需要在"startAngle"前添加"-":-startAngle
    //启动角度是startAngle,由定时器来修改
    painter.drawPie(QRect(-dataTmp,-dataTmp,dataTmp*2,dataTmp*2),startAngle*16,70*16);
   // 左上角坐标为 (-最大半径, -最大半径),宽高为最大半径的 2 倍,正好覆盖整个雷达区域
}


效果图

在这里插入图片描述


三、仪表表盘

3.1 初步完成

3.3.1 简易仪表盘制作001

代码示例:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    // 【重写绘图事件】当窗口需要刷新(如调用 update())时,Qt 会自动调用此函数进行绘制
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QPainter>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    //把底色弄成黑色;
    painter.setBrush(Qt::black);
    painter.drawRect(rect());

    //坐标系,平移到中心
    painter.translate(rect().center());
    QRadialGradient radialGradient(0, 0, height()/2); // 中心和半径 (0, 0, eight()/2)
    // 设置颜色停靠点
    radialGradient.setColorAt(0.0, QColor(255,0,0,50)); // 0.0 (圆心处):设置颜色为低透明度的红色 (Alpha=50)
    radialGradient.setColorAt(1.0, QColor(255,0,0,250)); // 1.0 (最外圈边缘):设置颜色为高透明度的红色 (Alpha=250)

    // 使用这个渐变创建 QBrush
    QBrush brush(radialGradient);
    painter.setBrush(brush);
    //画大圆
    painter.drawEllipse(QPoint(0,0),height()/2,height()/2);
    painter.setBrush(Qt::NoBrush);//不要画刷,否则只能看到最外面的圈,看不见小圈
    //画小圆
    painter.setPen(QPen(Qt::white,3));
    painter.drawEllipse(QPoint(0,0),60,60);

    //画刻度
    //1.算出一个刻度需要的旋转的角度
    double angle = 270*1.0/50;//270*1.0的作用是扩大变量类型,把int型阔成double,保留小数
    //2.设置第一个刻度的位置
    painter.rotate(135);
    for(int i=0;i <= 50;i++){
        if(i % 10 == 0){//画长的刻度线
            painter.drawLine(height()/2 - 20,0,height()/2-3,0);// 画一条较长的线。起点 X 为 (半径-20),终点 X 为 (半径-3)
        }else{//画短的刻度线
            painter.drawLine(height()/2 - 8,0,height()/2-3,0);// 画一条较短的线。起点 X 为 (半径-8),终点 X 为 (半径-3)
        }
        //画完后旋转
        // 【旋转坐标系】画完一根刻度线后,将坐标系顺时针旋转一个刻度的角度 (angle)
        // 下一根刻度线就会在旋转后的新坐标系 (Y=0) 上绘制,从而形成一圈放射状的刻度
        painter.rotate(angle);
    }

}

效果图

在这里插入图片描述


3.3.2 简易仪表盘制作002

代码示例:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QPainter>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    //把底色弄成黑色;
    painter.setBrush(Qt::black);
    painter.drawRect(rect());

    //坐标系,平移到中心
    painter.translate(rect().center());
    QRadialGradient radialGradient(0, 0, height()/2); // 中心和半径 (0, 0, eight()/2)
    // 设置颜色停靠点
    radialGradient.setColorAt(0.0, QColor(255,0,0,50)); // 0.0 (圆心处):设置颜色为低透明度的红色 (Alpha=50)
    radialGradient.setColorAt(1.0, QColor(255,0,0,250)); // 1.0 (最外圈边缘):设置颜色为高透明度的红色 (Alpha=250)

    // 使用这个渐变创建 QBrush
    QBrush brush(radialGradient);
    painter.setBrush(brush);
    //画大圆
    painter.drawEllipse(QPoint(0,0),height()/2,height()/2);
    painter.setBrush(Qt::NoBrush);//不要画刷,否则只能看到最外面的圈,看不见小圈
    //画小圆
    painter.setPen(QPen(Qt::white,3));
    painter.drawEllipse(QPoint(0,0),60,60);

    //画刻度
    //1.算出一个刻度需要的旋转的角度
    double angle = 270*1.0/50; //270*1.0的作用是扩大变量类型,把int型阔成double,保留小数
    //2.设置第一个刻度的位置
    painter.setFont(QFont("华文宋体",15));//设置字体样式和大小
    painter.save();//保存原点,x在三点钟方向
    painter.rotate(135); // 【旋转起始位】顺时针旋转135度,定位到刻度0的起始位置(左下角)
    for(int i=0;i <= 50;i++){// 【循环绘制刻度】从0到50
        if(i % 10 == 0){ // 【整十刻度】(0, 10, 20...
            //画字
            if(135 + angle * i < 270){//【判断是否需要翻转文字】如果角度小于270(即还没到底部)
                painter.rotate(180);// 旋转180度,让文字倒过来,方便阅读
                painter.drawText(-(height()/2 - 20-4),8,QString::number(i));// 绘制数字
                painter.rotate(-180);// 恢复旋转,以便继续画刻度线
            }else{
                 painter.drawText(height()/2 - 20-30,8,QString::number(i));// 底部文字直接绘制
            }

            //画长的刻度线
            painter.drawLine(height()/2 - 20,0,height()/2-3,0);
        }else{//画短的刻度线
            painter.drawLine(height()/2 - 8,0,height()/2-3,0);
        }
        //画完后旋转
        // 【旋转坐标系】画完一根刻度线后,将坐标系顺时针旋转一个刻度的角度 (angle)
        // 下一根刻度线就会在旋转后的新坐标系 (Y=0) 上绘制,从而形成一圈放射状的刻度
        painter.rotate(angle);
    }
    //画指针,线
    //坐标轴先回到原点
    painter.restore(); // 【恢复状态 1】恢复到“刻度循环开始前”的状态(即旋转了135度的状态)
    painter.save();    // 【保存状态 2】为绘制指针保存当前状态
    painter.rotate(135 + (angle * 20));
    painter.drawLine(60,0,height()/2 - 20 -10 -28,0);// 【定位指针】
                                   // 此时坐标系已在135度基础上,再加转 (5.4 * 20) = 108度
                                   // 总旋转角度 = 135 + 108 = 243度(指向刻度20的位置)

    //画扇形
    painter.restore();// 【恢复状态 1】注意:这里又恢复到了“旋转了135度”的状态!
                      // 这意味着接下来的绘图操作是在旋转后的坐标系中进行的
    QRect rectangle(-height()/2+58,-height()/2+58,height()-116,height()-116);
    painter.setPen(Qt::NoPen);// 【去除边框】扇形不需要轮廓线
    painter.setBrush(QColor(235,152,50,150));// 【设置扇形颜色】半透明橙色
    painter.drawPie(rectangle,(360-135)*16,-angle*20*16);// 起始角度:(360-135)*16。注意:Qt中角度单位是1/16度。
                                          // 这里的计算逻辑试图修正坐标系旋转带来的影响,或者定义绝对角度。
                                          // 跨度角度:-angle*20*16。负值表示逆时针绘制(或者配合起始角度定义范围)。
                                          // 这里的逻辑是为了让扇形覆盖从 0 到 20 的区域。

}

效果图

在这里插入图片描述


3.3.3 简易仪表盘完结

代码示例:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
    QTimer *timer; // 【定时器指针】用于周期性触发 update() 产生动画
    int currentValue;// 【当前值变量】存储仪表盘当前的读数(0-50)
    int mark = 0;// 【方向标记】0 表示递增(正向),1 表示递减(反向)
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QPainter>
#include <QDebug>
#include <QTimer>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    timer = new QTimer(this);// 【创建定时器】父对象设为 this,由 Qt 自动管理内存
    currentValue = 0;// 【初始化数值】起始值为 0
        
    // 【连接超时信号】使用 Lambda 表达式处理定时逻辑
    connect(timer, &QTimer::timeout, [=](){
        if(mark == 0){              // 【判断状态:递增中】
            currentValue++;         // 数值加 1
            if(currentValue >= 51){ // 如果超过最大值 50
                mark = 1;           // 切换为递减状态
            }
        }
        if(mark == 1){              // 【判断状态:递减中】
            currentValue--;         // 数值减 1
            if(currentValue == 0){  // 如果回到最小值 0
                mark = 0;           // 切换为递增状态
            }
        }
        update();                   // 【请求重绘】触发 paintEvent,刷新画面
    });

    timer->start(50);               // 【启动定时器】每 50 毫秒触发一次(约 20 FPS)
}

Widget::~Widget()
{
    delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    //把底色弄成黑色;
    painter.setBrush(Qt::black);
    painter.drawRect(rect());

    //坐标系,平移到中心
    painter.translate(rect().center());
    QRadialGradient radialGradient(0, 0, height()/2); // 中心和半径 (0, 0, eight()/2)
    // 设置颜色停靠点
    radialGradient.setColorAt(0.0, QColor(255,0,0,50)); // 0.0 (圆心处):设置颜色为低透明度的红色 (Alpha=50)
    radialGradient.setColorAt(1.0, QColor(255,0,0,250)); // 1.0 (最外圈边缘):设置颜色为高透明度的红色 (Alpha=250)

    // 使用这个渐变创建 QBrush
    QBrush brush(radialGradient);
    painter.setBrush(brush);
    //画大圆
    painter.drawEllipse(QPoint(0,0),height()/2,height()/2);
    painter.setBrush(Qt::NoBrush);//不要画刷,否则只能看到最外面的圈,看不见小圈
    //画小圆
    painter.setPen(QPen(Qt::white,3));
    painter.drawEllipse(QPoint(0,0),60,60);

    //画当前值
    painter.setFont(QFont("华文宋体",25));//设置字体样式和大小
    // 【绘制文本】在中心 120x120 的矩形区域内居中对齐显示当前值
    painter.drawText(QRect(-60,-60,120,120),Qt::AlignCenter,QString::number(currentValue));

    //画刻度
    //1.算出一个刻度需要的旋转的角度
    double angle = 270*1.0/50; //270*1.0的作用是扩大变量类型,把int型阔成double,保留小数
    //2.设置第一个刻度的位置
    painter.setFont(QFont("华文宋体",15));//设置字体样式和大小
    painter.save();//保存原点,此时此刻在原点,x在三点钟方向
    painter.rotate(135);
    for(int i=0;i <= 50;i++){
        if(i % 10 == 0){
            //画字
            if(135 + angle * i < 270){
                painter.rotate(180);
                painter.drawText(-(height()/2 - 20-4),8,QString::number(i));
                painter.rotate(-180);
            }else{
                painter.drawText(height()/2 - 20-30,8,QString::number(i));
            }

            //画长的刻度线
            painter.drawLine(height()/2 - 20,0,height()/2-3,0);
        }else{//画短的刻度线
            painter.drawLine(height()/2 - 8,0,height()/2-3,0);
        }
        //画完后旋转
        // 【旋转坐标系】画完一根刻度线后,将坐标系顺时针旋转一个刻度的角度 (angle)
        // 下一根刻度线就会在旋转后的新坐标系 (Y=0) 上绘制,从而形成一圈放射状的刻度
        painter.rotate(angle);
    }
    //画指针,线
    //坐标轴先回到原点
    painter.restore();
    painter.save();
    painter.rotate(135 + angle * currentValue);
    painter.drawLine(60,0,height()/2 - 20 -10 -28,0);

    //画扇形
    painter.restore();
    QRect rectangle(-height()/2+58,-height()/2+58,height()-116,height()-116);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(235,152,50,150));
    painter.drawPie(rectangle,(360-135)*16,-angle*currentValue*16);

}

效果图

在这里插入图片描述


3.2 简易仪表盘美化

代码示例:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
    QTimer *timer;
    int currentValue;
    int mark = 0;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QPainter>
#include <QDebug>
#include <QTimer>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    timer = new QTimer(this);
    currentValue = 0;
    connect(timer,&QTimer::timeout,[=](){
        if(mark == 0){
            currentValue++;
            if(currentValue >= 60){
                mark = 1;
            }
        }
        if(mark == 1){
            currentValue--;
            if(currentValue == 0){
                mark = 0;
            }
        }

        update();
    });
    timer->start(50);
}

Widget::~Widget()
{
    delete ui;
}

去掉径向渐变,修改刻度

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    //把底色弄成黑色;
    painter.setBrush(Qt::black);
    painter.drawRect(rect());

    //坐标系,平移到中心
    painter.translate(rect().center());

    //画大圆
    painter.drawEllipse(QPoint(0,0),height()/2,height()/2);
    painter.setBrush(Qt::NoBrush);//不要画刷,否则只能看到最外面的圈,看不见小圈
    //画小圆
    painter.setPen(QPen(Qt::white,3));
    painter.drawEllipse(QPoint(0,0),60,60);

    //画当前值
    painter.setFont(QFont("华文宋体",25));//设置字体样式和大小
    painter.drawText(QRect(-60,-60,120,120),Qt::AlignCenter,QString::number(currentValue));

    //画刻度
    //1.算出一个刻度需要的旋转的角度
    double angle = 240*1.0/60; //240*1.0的作用是扩大变量类型,把int型阔成double,保留小数
    //2.设置第一个刻度的位置
    painter.setFont(QFont("华文宋体",15));//设置字体样式和大小
    painter.save();//保存原点,此时此刻在原点,x在三点钟方向
    painter.rotate(150);
    for(int i=0;i <= 60;i++){
        if(i % 5 == 0){
            //画字
            if(150 + angle * i < 270){
                painter.rotate(180);
                painter.drawText(-(height()/2 - 20-10),8,QString::number(i*4));
                painter.rotate(-180);
            }else{
                painter.drawText(height()/2 - 20-50,8,QString::number(i*4));
            }

            //画长的刻度线
            painter.drawLine(height()/2 - 20,0,height()/2-3,0);
        }else{//画短的刻度线
            painter.drawLine(height()/2 - 8,0,height()/2-3,0);
        }
        //画完后旋转
        // 【旋转坐标系】画完一根刻度线后,将坐标系顺时针旋转一个刻度的角度 (angle)
        // 下一根刻度线就会在旋转后的新坐标系 (Y=0) 上绘制,从而形成一圈放射状的刻度
        painter.rotate(angle);
    }
    //画指针,线
    //坐标轴先回到原点
    painter.restore();
    painter.save();
    painter.rotate(150 + angle * currentValue);
    painter.drawLine(60,0,height()/2 - 20 -10 -28,0);

    //画扇形
    painter.restore();
    QRect rectangle(-height()/2,-height()/2,height(),height());
    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(235,152,50,150));
    painter.drawPie(rectangle,(360-150)*16,-angle*currentValue*16);

}

最后的效果

在这里插入图片描述


3.3 正弦纠正刻度文字方向

3.3.1 优化数字显示后代码整理

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void paintEvent(QPaintEvent *event) override;// 【重写绘图事件】所有自定义绘制都在此进行

private:
    Ui::Widget *ui;
    QTimer *timer;       // 【定时器指针】用于周期性触发 update() 产生动画
    int currentValue;    // 【当前值变量】存储仪表盘当前的读数(0-60)
    int mark = 0;        // 【方向标记】0 表示递增(正向),1 表示递减(反向)
    int startAngle;      // 【起始角度】仪表盘刻度的起始角度(150度)
    double angle;        // 【单格角度】每个刻度代表的角度值
    void initCanvas(QPainter& painter); // 【初始化画布】设置抗锯齿、背景色、平移坐标系
    void drawMiddleCircle(QPainter& painter,int radius); // 【绘制中心圆】绘制仪表盘中心的小白圆
    void drawCurrentSpeed(QPainter& painter); // 【绘制当前速度】在中心显示当前的数值
    void drawScale(QPainter& painter,int radius); // 【绘制刻度线】绘制放射状的长短刻度线
    void drawScaleText(QPainter& painter,int radius); // 【绘制刻度文字】计算坐标并绘制刻度对应的数字
    void drawPointLine(QPainter& painter,int lenth); // 【绘制指针】根据当前值旋转坐标系并画线
    void drawSpeedPie(QPainter& painter,int radius); // 【绘制速度扇形】绘制跟随指针的橙色覆盖区域
    void startSpeed();   // 【启动速度动画】初始化定时器并连接信号槽,驱动数值变化
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QPainter>
#include <QDebug>
#include <QTimer>
#include <QtMath>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    startAngle = 150;// 【设置起始角度】仪表盘从 150 度(左下角)开始绘制
    startSpeed(); // 【启动动画】调用函数初始化定时器并开始计时
}

Widget::~Widget()
{
    delete ui;
}

// --- 初始化画布 ---
void Widget::initCanvas(QPainter& painter)
{
    painter.setRenderHint(QPainter::Antialiasing, true); // 【开启抗锯齿】使圆形和线条边缘平滑
    painter.setBrush(Qt::black);       // 【设置背景刷】黑色画刷
    painter.drawRect(rect());          // 【绘制背景】填充整个窗口为黑色

    // 【平移坐标系】将原点 (0,0) 移动到窗口的水平居中、垂直偏下 0.6 的位置
    // 这样仪表盘看起来更像汽车真实的驾驶台位置
    QPoint cent(rect().width()/2, rect().height()*0.6);
    painter.translate(cent);
}

// --- 绘制中心小圆 ---
void Widget::drawMiddleCircle(QPainter &painter, int radius)
{
    //画小圆
    painter.setPen(QPen(Qt::white,3));// 【设置画笔】白色,线宽 3 像素
    painter.drawEllipse(QPoint(0,0),radius,radius);// 【绘制圆】在中心绘制指定半径的白色圆环
}

// --- 绘制中心当前速度数值 ---
void Widget::drawCurrentSpeed(QPainter &painter)
{
    //画当前速度
    painter.setFont(QFont("华文宋体",20));// 【设置字体】华文宋体,20号字
    // 【绘制文本】在中心 120x120 的矩形区域内居中对齐显示当前值
    painter.drawText(QRect(-60,-60,120,120),Qt::AlignCenter,QString::number(currentValue));
}

// --- 绘制刻度线 ---
void Widget::drawScale(QPainter &painter,int radius)
{
    //画刻度
    //1.算出一个刻度需要的旋转的角度
    angle = 240*1.0/60; //240*1.0的作用是扩大变量类型,把int型阔成double,保留小数// 【计算单格角度】总跨度 240 度 / 60 格 = 4 度
    painter.save();//保存原点,此时此刻在原点,x在三点钟方向
    painter.setPen(QPen(Qt::white,5));// 【设置刻度画笔】白色,线宽 5 像素
    //2.设置第一个刻度的位置
    painter.rotate(startAngle);// 【初始旋转】转到 150 度,这是刻度 0 的起始位置
    for(int i=0;i <= 60;i++){// 【循环绘制 61 个刻度点】
        if(i % 5 == 0){
            //画长的刻度线,每 5 格画一个长刻度
            painter.drawLine(radius - 20,0,radius-3,0);
        }else{//画短的刻度线
            painter.drawLine(radius - 8,0,radius-3,0);
        }
        //画完后旋转
        // 【旋转坐标系】画完一根刻度线后,将坐标系顺时针旋转一个刻度的角度 (angle)
        // 下一根刻度线就会在旋转后的新坐标系 (Y=0) 上绘制,从而形成一圈放射状的刻度
        painter.rotate(angle);
    }
    //画完刻度之后恢复坐标系
    painter.restore();
}

// --- 绘制刻度文字 ---
void Widget::drawScaleText(QPainter &painter, int radius)
{
    //写刻度文字
    painter.setFont(QFont("华文宋体",18));// 【设置字体】刻度数字用 18 号字
    int r = radius - 46;// 【计算文字半径】文字距离圆心的距离(比刻度线更靠内)
    for(int i = 0;i <= 60;i++){ // 【循环绘制文字】
        if(i % 5 == 0){
            //保存坐标系
            painter.save();
            //算出平移点,弧度=角度*3.1415/180
            // 【计算平移坐标】使用三角函数计算文字在圆周上的 X, Y 坐标
            // qDegreesToRadians 将角度转为弧度,210-angle*i 是计算文字对应的绝对角度
            int delX = qCos(qDegreesToRadians(210-angle*i)) * r;//QT中sin认的是弧度
            int delY = qSin(qDegreesToRadians(210-angle*i)) * r;
            //平移坐标系,将原点移到文字应该出现的位置
            painter.translate(QPoint(delX,-delY));
            //旋转坐标系,让文字保持正向或按特定角度倾斜
            painter.rotate(-120+angle*i);//angle=4,30*4=120的时候,实参为0,120
            //写上文字,在平移后的原点处绘制数值 (i*4 是因为最大值 60 对应 240km/h)
            painter.drawText(-25,-25,50,30,Qt::AlignCenter,QString::number(i*4));
            //恢复坐标系,准备画下一个字
            painter.restore();
        }
    }
}

// --- 绘制指针 ---
void Widget::drawPointLine(QPainter& painter,int lenth)
{
    //画指针,线
    //坐标轴先回到原点
    painter.save();
    painter.rotate(startAngle + angle * currentValue);// 【旋转到目标位置】基准 150 度 + 当前值对应的角度
    painter.drawLine(60,0,lenth,0);// 【绘制指针线段】从内圆边缘 (60) 画到指定长度
}

// --- 绘制速度扇形 ---
void Widget::drawSpeedPie(QPainter &painter, int radius)
{
    //画扇形
    painter.restore();// 【恢复状态】这里恢复的是 drawPointLine 中 save 的状态
                      // 这样正好抵消了指针绘制时的旋转,让扇形绘制基于正常坐标系
    QRect rectangle(-radius,-radius,radius*2,radius*2);// 【计算扇形外接矩形】注意:传入的 radius 是负数,所以这里 -radius 变成了正数边长
    painter.setPen(Qt::NoPen);// 【无边框】
    painter.setBrush(QColor(235,152,50,150));// 【橙色半透明画刷】Alpha=150
    // 【绘制扇形】
    // 起始角度:(360-150)*16,对应 Qt 坐标系中的 150 度位置
    // 跨度角度:-angle*currentValue*16,负值表示逆时针扫描,大小随 currentValue 变化
    painter.drawPie(rectangle,(360-startAngle)*16,-angle*currentValue*16);
}

// --- 启动速度动画逻辑 ---
void Widget::startSpeed()
{
    timer = new QTimer(this);// 【创建定时器】
    currentValue = 0; // 【初始化数值】起始值为 0
    // 【连接超时信号】使用 Lambda 表达式处理定时逻辑
    connect(timer, &QTimer::timeout, [=](){
        if(mark == 0){              // 【判断状态:递增中】
            currentValue++;         // 数值加 1
            if(currentValue >= 60){ // 如果超过最大值 60
                mark = 1;           // 切换为递减状态
            }
        }
        if(mark == 1){              // 【判断状态:递减中】
            currentValue--;         // 数值减 1
            if(currentValue == 0){  // 如果回到最小值 0
                mark = 0;           // 切换为递增状态
            }
        }
        update();                   // 【请求重绘】触发 paintEvent,刷新画面
    });
    timer->start(50);               // 【启动定时器】每 50 毫秒触发一次
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    //初始化画布
    initCanvas(painter);
    //画小圆
    drawMiddleCircle(painter,60);
    //画当前速度
    drawCurrentSpeed(painter);
    //画刻度
    drawScale(painter,height()/2);
    //写刻度文字
    drawScaleText(painter,height()/2);
    //画指针,线
    drawPointLine(painter,height()/2-58);
    //画扇形
    drawSpeedPie(painter,height()/2);
}

效果图和运算法则图

在这里插入图片描述

在这里插入图片描述


3.4 指针样式美化

3.4.1 先了解如何画指针(drawPolygon)
  1. 新建一个项目

    在这里插入图片描述

  2. 代码示例:

    在Qt中,使用 QPainter 来绘制一个类似指南针的指针,你通常会遵循以下步骤:

    • **创建一个QWidget或QMainWindow的子类:**这是你绘图的画布。
    • 重写 paintEvent 方法:这个方法是Qt中绘制自定义图形的关键地方。
    • **使用QPainter:**在 paintEvent 中创建一个 QPainter 对象,并用它来绘制你的指针
    • 绘制指针:可以通过绘制一个线条或者一个具有特定形状的多边形来创建指针,比如一个三角形。
    • 旋转指针:如果你想让指针能够像指南针那样旋转,你可以使用 QPainter 的旋转功能。
    #include "widget.h"
    #include "ui_widget.h"
    
    #include <QPainter>
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    void Widget::paintEvent(QPaintEvent *event)
    {
        QPainter painter(this);// 【核心】创建画家对象,指定在当前窗口 (this) 上进行绘制
        painter.setRenderHint(QPainter::Antialiasing,true); // 开启抗锯齿渲染,使绘制的图形边缘更平滑,减少锯齿感
        painter.setBrush(Qt::red);// 设置画刷颜色为红色(用于填充多边形内部)
        // 将坐标系原点平移到窗口的中心点
        // rect().center() 返回窗口矩形的中心坐标
        painter.translate(rect().center());
        // 定义多边形的顶点数组
        // static const 表示该数组是静态常量,只初始化一次且不可修改,提高效率
        // 注意:此时坐标系原点已在窗口中心,所以 (0,0) 就是窗口正中心
        static const QPointF points[3] = {
             QPointF(-10.0, 0),    // 顶点1:中心左侧 10 像素
             QPointF(0, -200.0),   // 顶点2:中心上方 200 像素(箭头尖端)
             QPointF(10.0, 0),     // 顶点3:中心右侧 10 像素
        };
        // 绘制多边形
        // 参数1:顶点数组
        // 参数2:顶点数量(这里写了 3,对应上面的 3 个点)
        painter.drawPolygon(points, 3);
    }
    
  3. 效果图

    在这里插入图片描述


3.4.2 指针美化(在画指针的函数加入代码就可以)

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
    QTimer *timer;
    int currentValue;
    int mark = 0;
    int startAngle;
    double angle;
    void initCanvas(QPainter& painter);
    void drawMiddleCircle(QPainter& painter,int radius);
    void drawCurrentSpeed(QPainter& painter);
    void drawScale(QPainter& painter,int radius);
    void drawScaleText(QPainter& painter,int radius);
    void drawPointLine(QPainter& painter,int lenth);
    void drawSpeedPie(QPainter& painter,int radius);
    void startSpeed();
};
#endif // WIDGET_H

widget.cpp

void Widget::drawSpeedPie(QPainter &painter, int radius)
{
    //画扇形
    painter.restore();
    QRect rectangle(-radius,-radius,radius*2,radius*2);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(255,0,0,100)); // 255,0,0 是纯红,100 是透明度(0-255),实现半透明叠加效果
    painter.drawPie(rectangle,(360-startAngle)*16,-angle*currentValue*16);
}

void Widget::drawPointLine(QPainter& painter,int lenth)
{
    //画指针,线
    //坐标轴先回到原点
    painter.save();
        
    painter.setBrush(Qt::white);
    painter.setPen(Qt::NoPen);
    // 【定义顶点数组】定义构成指针形状的4个坐标点
    // static const 确保数组只在第一次调用时初始化,提高性能
    static const QPointF points[4] = {
         QPointF(0, 0.0),      // 点1:指针的尾部中心(靠近圆心)
         QPointF(200.0, -1.1), // 点2:指针头部的上边缘(X轴方向延伸200像素,Y轴微偏上)
         QPointF(200.0, 1.1),  // 点3:指针头部的下边缘(X轴方向延伸200像素,Y轴微偏下)
         QPointF(0, 15.0)      // 点4:指针尾部的下边缘(这里构成了一个非对称的尾巴形状)
     };

     // 【绘制多边形】根据上面定义的4个点画出指针形状
    // 此时坐标系已经旋转,所以指针会指向正确的方向
    painter.rotate(startAngle + angle * currentValue);
    painter.drawPolygon(points, 4);
    //painter.drawLine(60,0,lenth,0);

}

在这里插入图片描述


3.5 汽车仪表盘高速标注红色

代码示例:

去画刻度哪里添加代码就可以,随便还可去修改一下刻度的字体和大小

widget.cpp

void Widget::drawScale(QPainter &painter,int radius)
{
    //画刻度
    //1.算出一个刻度需要的旋转的角度
    angle = 240*1.0/60; //240*1.0的作用是扩大变量类型,把int型阔成double,保留小数
    painter.save();//保存原点,此时此刻在原点,x在三点钟方向
    painter.setPen(QPen(Qt::white,5));
    //2.设置第一个刻度的位置
    painter.rotate(startAngle);
    for(int i=0;i <= 60;i++){
        if(i >= 40){       // 【颜色判断逻辑】如果当前刻度索引大于等于40(即后三分之一区域),变为红色警示区
            painter.setPen(QPen(Qt::red,5));// 切换画笔为红色
        }
        if(i % 5 == 0){
            //画长的刻度线
            painter.drawLine(radius - 20,0,radius-3,0);
        }else{//画短的刻度线
            painter.drawLine(radius - 8,0,radius-3,0);
        }
        //画完后旋转
        // 【旋转坐标系】画完一根刻度线后,将坐标系顺时针旋转一个刻度的角度 (angle)
        // 下一根刻度线就会在旋转后的新坐标系 (Y=0) 上绘制,从而形成一圈放射状的刻度
        painter.rotate(angle);
    }
    //画完刻度之后恢复坐标系
    painter.restore();
     // 【重置画笔】为了防止影响后续其他函数的绘图,显式地将画笔改回白色(虽然 restore 通常能恢复画笔,但这是一种防御性编程习惯)
    painter.setPen(QPen(Qt::white,5));
}
void Widget::drawScaleText(QPainter &painter, int radius)
{
    //写刻度文字
    QFont font("Arial",15); // 【设置字体】创建 Arial 字体对象,大小15
    font.setBold(true); // 设置为粗体,增加可读性
    painter.setFont(font); // 将字体应用到画家
    int r = radius - 49;//定义一个半径
    for(int i = 0;i <= 60;i++){
        if(i % 5 == 0){
            //保存坐标系
            painter.save();
            //算出平移点,弧度=角度*3.1415/180
            int delX = qCos(qDegreesToRadians(210-angle*i)) * r;//QT中sin认的是弧度
            int delY = qSin(qDegreesToRadians(210-angle*i)) * r;
            //平移坐标系
            painter.translate(QPoint(delX,-delY));
            //旋转坐标系
            painter.rotate(-120+angle*i);//angle=4,30*4=120的时候,实参为0,120
            //写上文字
            painter.drawText(-25,-25,50,30,Qt::AlignCenter,QString::number(i*4));
            //恢复坐标系
            painter.restore();
        }
    }
}

在这里插入图片描述


3.6 汽车仪表盘速度显示优化

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
    QTimer *timer;
    int currentValue;
    int mark = 0;
    int startAngle;
    double angle;
    void initCanvas(QPainter& painter);
    void drawMiddleCircle(QPainter& painter,int radius);
    void drawCurrentSpeed(QPainter& painter);
    void drawScale(QPainter& painter,int radius);
    void drawScaleText(QPainter& painter,int radius);
    void drawPointLine(QPainter& painter,int lenth);
    void drawSpeedPie(QPainter& painter,int radius);
    void startSpeed();
    void drawEllipseInnerBlack(QPainter& painter,int radius);
};
#endif // WIDGET_H

widget.cpp

void Widget::drawEllipseInnerBlack(QPainter &painter, int radius)
{
    painter.setBrush(Qt::black);// 【设置画刷】将画刷设置为纯黑色(Qt::black)
                               // 画刷(Brush)决定了封闭图形内部的填充颜色
    painter.drawEllipse(QPoint(0,0),radius,radius); // 【绘制圆形】以当前坐标系原点 (0,0) 为中心,绘制指定半径的圆
                                                   // 这里的 (0,0) 是经过 initCanvas 平移后的窗口中心偏下位置
                                                  // radius 参数决定了这个黑色遮罩圆的大小
}
void Widget::drawCurrentSpeed(QPainter &painter)
{
    //画当前速
    painter.setPen(Qt::white);// 【设置画笔颜色】将画笔(Pen)设置为白色
                              // 画笔决定了文字、线条的颜色
    QFont font("Arial",30); // 【设置数字字体】创建字体对象,使用 Arial 字体,大小设为 30
    font.setBold(true); // 【字体加粗】让数字显示得更醒目
    painter.setFont(font);// 【应用字体】将设置好的字体交给画家
    painter.drawText(QRect(-60,-60,120,70),Qt::AlignCenter,QString::number(currentValue));

    QFont font2("Arial",15); // 【设置单位字体】重新定义一个字体对象,大小设为 15(比数字小)
    font2.setBold(true); // 【单位加粗】
    painter.setFont(font2);// 【应用单位字体】
    painter.drawText(QRect(-60,-60,120,160),Qt::AlignCenter,"Km/h"); // 【绘制单位文本】
                                             // QRect(-60, -60, 120, 160): 定义一个更大的矩形区域(高度增加到160)
                                            // 这样文字会显示在数字的下方,依然保持整体居中对齐

}

在这里插入图片描述


3.7 汽车仪表盘内环发光圈

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
    QTimer *timer;
    int currentValue;
    int mark = 0;
    int startAngle;
    double angle;
    void initCanvas(QPainter& painter);
    void drawMiddleCircle(QPainter& painter,int radius);
    void drawCurrentSpeed(QPainter& painter);
    void drawScale(QPainter& painter,int radius);
    void drawScaleText(QPainter& painter,int radius);
    void drawPointLine(QPainter& painter,int lenth);
    void drawSpeedPie(QPainter& painter,int radius);
    void startSpeed();
    void drawEllipseInnerBlack(QPainter& painter,int radius);
    void drawEllipseInnerShine(QPainter& painter,int radius);//画渐变内圈圆
};
#endif // WIDGET_H

widget.cpp

void Widget::drawEllipseInnerShine(QPainter &painter, int radius)
{
     // 【创建径向渐变对象】
    // 参数分别为:圆心X坐标(0,0)、圆心Y坐标(0,0)、渐变的半径长度
    // 径向渐变(QRadialGradient)会从圆心开始,向外辐射产生颜色过渡
    QRadialGradient radialGradient(0,0,radius);
    
    // 【设置渐变的中心颜色(起始色)】
    // 参数1:0.0 代表渐变的起点(即圆心位置)
    // 参数2:QColor(255,0,0,200) 代表不透明度较高的亮红色(R=255, G=0, B=0, Alpha=200)
    radialGradient.setColorAt(0.0, QColor(255,0,0,200));
    
    // 【设置渐变的边缘颜色(结束色)】
    // 参数1:1.0 代表渐变的终点(即半径的最外圈)
    // 参数2:QColor(0,0,0,100) 代表不透明度较低的暗红色,实现了从中心亮红到边缘暗红的平滑过渡
    radialGradient.setColorAt(1.0, QColor(0,0,0,100));
    
    // 【将渐变对象设置为画刷】
    // 画刷(Brush)决定了封闭图形内部的填充方式,这里不再使用纯色填充,而是使用刚才定义的渐变
    painter.setBrush(radialGradient);
    
    // 【绘制圆形】
    // 以当前坐标系原点 (0,0) 为中心,绘制指定半径的圆,并用上面的径向渐变进行填充
    painter.drawEllipse(QPoint(0,0),radius,radius);
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    //初始化画布
    initCanvas(painter);
    //画小圆
    drawMiddleCircle(painter,60);
    //画刻度
    drawScale(painter,height()/2);
    //写刻度文字
    drawScaleText(painter,height()/2);
    //画指针,线
    drawPointLine(painter,height()/2-58);
    //画扇形
    drawSpeedPie(painter,height()/2);
    //画渐变内圈圆
    drawEllipseInnerShine(painter,110);
    //画黑色内圈
    drawEllipseInnerBlack(painter,80);
    //画当前速度
    drawCurrentSpeed(painter);
}

在这里插入图片描述


3.8 汽车仪表盘外环发光圈

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
    QTimer *timer;
    int currentValue;
    int mark = 0;
    int startAngle;
    double angle;
    void initCanvas(QPainter& painter);
    void drawMiddleCircle(QPainter& painter,int radius);
    void drawCurrentSpeed(QPainter& painter);
    void drawScale(QPainter& painter,int radius);
    void drawScaleText(QPainter& painter,int radius);
    void drawPointLine(QPainter& painter,int lenth);
    void drawSpeedPie(QPainter& painter,int radius);
    void startSpeed();
    void drawEllipseInnerBlack(QPainter& painter,int radius);
    void drawEllipseInnerShine(QPainter& painter,int radius);
    void drawEllipseOutterShine(QPainter& painter,int radius);
};
#endif // WIDGET_H

widget.cpp

void Widget::drawEllipseOutterShine(QPainter &painter, int radius)
{
    // 【定义外接矩形】定义一个以(0,0)为中心,宽高均为 radius*2 的矩形
    // 这个矩形是用来框住接下来要绘制的扇形区域的
    QRect rectangle(-radius, -radius, radius * 2, radius * 2);

    // 【取消边框】设置画笔为 Qt::NoPen,确保绘制出来的扇形没有轮廓线
    painter.setPen(Qt::NoPen);

    // 【创建径向渐变对象】圆心在(0,0),渐变半径为 radius
    QRadialGradient radialGradient(0, 0, radius);

    // 【设置边缘最外圈颜色】1.0 代表渐变的最外圈(半径的100%处)
    // 设置为不透明度较高的亮红色 (Alpha=200),作为光晕的最外层边界
    radialGradient.setColorAt(1.0, QColor(255, 0, 0, 200));

    // 【设置向内一点的边缘颜色】0.97 代表半径的97%处
    // 设置为不透明度稍低的红色 (Alpha=120),与最外圈形成自然的色彩过渡
    radialGradient.setColorAt(0.97, QColor(255, 0, 0, 120));

    // 【设置内部透明起始点】0.9 代表半径的90%处
    // 设置为完全透明 (Alpha=0)。这意味着从半径的90%到100%这最后10%的区域,
    // 颜色会从透明平滑过渡到亮红色,从而制造出“只有最外圈发光”的视觉效果
    radialGradient.setColorAt(0.9, QColor(0, 0, 0, 0));

    // 【设置圆心颜色】0 代表圆心
    // 同样设置为完全透明,确保仪表盘的中心区域不会被这个渐变遮挡
    radialGradient.setColorAt(0, QColor(0, 0, 0, 0));

    // 【将渐变对象设置为画刷】用这个精心设计的径向渐变来填充图形
    painter.setBrush(radialGradient);

    // 【绘制扇形光晕】
    // 参数1:外接矩形
    // 参数2:(360-150)*16 是起始角度。Qt的drawPie以3点钟方向为0度逆时针计算,
    //        所以 360-150 = 210度,正好对应仪表盘刻度起始的150度位置(左下方)
    // 参数3:-angle*61*16 是扇形的跨度。负数代表顺时针绘制,61代表覆盖全部60个刻度,
    //        乘以16是因为Qt的角度单位是1/16度
    painter.drawPie(rectangle, (360 - 150) * 16, -angle * 61 * 16);
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    //初始化画布
    initCanvas(painter);
    //画小圆
    drawMiddleCircle(painter,60);
    //画刻度
    drawScale(painter,height()/2);
    //写刻度文字
    drawScaleText(painter,height()/2);
    //画指针,线
    drawPointLine(painter,height()/2-58);
    //画扇形
    drawSpeedPie(painter,height()/2+25);
    //画渐变内圈圆
    drawEllipseInnerShine(painter,110);
    //画黑色内圈
    drawEllipseInnerBlack(painter,80);
    //画当前速度
    drawCurrentSpeed(painter);
    //画外环发光圈
    drawEllipseOutterShine(painter,height()/2+25);
}

在这里插入图片描述


3.9 画一个汽车logo

  1. 添加一个logo的资源文件

    在这里插入图片描述

  2. 名为res

    在这里插入图片描述

  3. 添加到工程

    在这里插入图片描述

代码示例:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
    QTimer *timer;
    int currentValue;
    int mark = 0;
    int startAngle;
    double angle;
    void initCanvas(QPainter& painter);//初始化画布
    void drawMiddleCircle(QPainter& painter,int radius); //画小圆
    void drawCurrentSpeed(QPainter& painter);//画当前速度
    void drawScale(QPainter& painter,int radius);//画刻度
    void drawScaleText(QPainter& painter,int radius);//画刻度文字
    void drawPointLine(QPainter& painter,int lenth);//画指针,线
    void drawSpeedPie(QPainter& painter,int radius); //画扇形
    void startSpeed();//启动动画
    void drawEllipseInnerBlack(QPainter& painter,int radius);//画黑色内圈
    void drawEllipseInnerShine(QPainter& painter,int radius);//画渐变内圈圆
    void drawEllipseOutterShine(QPainter& painter,int radius);//画外环发光圈
    void drawLogo(QPainter& painter,int radius);//画logo
};
#endif // WIDGET_H

widget.cpp

#include <QPainter>
#include <QDebug>
#include <QTimer>
#include <QtMath>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 【固定窗口大小】强制将窗口尺寸锁定为 800x600 像素
    // setFixedSize 会让用户无法通过拖拽边框来改变窗口大小,保证仪表盘比例不失调
    setFixedSize(800,600);
    // 【初始化起始角度】将仪表盘的起始绘制角度设为 150 度
    // 结合之前的代码逻辑,这通常是为了让刻度盘从左下角开始画起(类似汽车仪表盘布局)
    startAngle = 150;
    // 【启动动画/逻辑】调用自定义函数 startSpeed()
    // 根据上下文,这个函数内部通常会创建一个 QTimer 定时器,开始模拟速度变化并触发重绘
    startSpeed();
}

Widget::~Widget()
{
    delete ui;
}
void Widget::drawLogo(QPainter &painter, int radius)
{
    // 【定义 Logo 的绘制区域(外接矩形)】
    // QRect(x, y, width, height) 定义了图片将要被拉伸或放置的矩形范围。
    QRect rectangle(-65,radius*0.38,130,50);
    // 【绘制图片】
    // painter.drawPixmap(rect, pixmap): 将指定的图片绘制到 rect 定义的矩形区域内。
    // QPixmap(":/icon.png"): 从 Qt 资源系统加载名为 "icon.png" 的图片。
    painter.drawPixmap(rectangle,QPixmap(":/icon.png"));
}
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    int rad = height()/2;

    //初始化画布
    initCanvas(painter);
    //画小圆
    drawMiddleCircle(painter,60);
    //画刻度
    drawScale(painter,rad);
    //画刻度文字
    drawScaleText(painter,rad);
    //画指针,线
    drawPointLine(painter,rad-58);
    //画扇形
    drawSpeedPie(painter,rad+25);
    //画渐变内圈圆
    drawEllipseInnerShine(painter,110);
    //画黑色内圈
    drawEllipseInnerBlack(painter,80);
    //画当前速度
    drawCurrentSpeed(painter);
    //画外环发光圈
    drawEllipseOutterShine(painter,rad+25);
    //画一个汽车logo
    drawLogo(painter,rad);
}

在这里插入图片描述


四、汽车仪表盘代码总结

代码示例

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
    QTimer *timer;
    int currentValue;
    int mark = 0;
    int startAngle;
    double angle;
    void initCanvas(QPainter& painter);//初始化画布
    void drawMiddleCircle(QPainter& painter,int radius); //画小圆
    void drawCurrentSpeed(QPainter& painter);//画当前速度
    void drawScale(QPainter& painter,int radius);//画刻度
    void drawScaleText(QPainter& painter,int radius);//画刻度文字
    void drawPointLine(QPainter& painter,int lenth);//画指针,线
    void drawSpeedPie(QPainter& painter,int radius); //画扇形
    void startSpeed();
    void drawEllipseInnerBlack(QPainter& painter,int radius);//画黑色内圈
    void drawEllipseInnerShine(QPainter& painter,int radius);//画渐变内圈圆
    void drawEllipseOutterShine(QPainter& painter,int radius);//画外环发光圈
    void drawLogo(QPainter& painter,int radius);//画logo
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QPainter>
#include <QDebug>
#include <QTimer>
#include <QtMath>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    setFixedSize(800,600);
    startAngle = 150;
    startSpeed();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::initCanvas(QPainter& painter)
{
    painter.setRenderHint(QPainter::Antialiasing,true);
    //把底色弄成黑色;
    painter.setBrush(Qt::black);
    painter.drawRect(rect());

    //坐标系,平移到中心
    QPoint cent(rect().width()/2,rect().height()*0.6);
    painter.translate(cent);
}

void Widget::drawMiddleCircle(QPainter &painter, int radius)
{
    //画小圆
    painter.setPen(QPen(Qt::white,3));
    painter.drawEllipse(QPoint(0,0),radius,radius);
}

void Widget::drawCurrentSpeed(QPainter &painter)
{
    //画当前速
    painter.setPen(Qt::white);
    QFont font("Arial",30);
    font.setBold(true);
    painter.setFont(font);
    painter.drawText(QRect(-60,-60,120,70),Qt::AlignCenter,QString::number(currentValue*4));

    QFont font2("Arial",15);
    font2.setBold(true);
    painter.setFont(font2);
    painter.drawText(QRect(-60,-60,120,160),Qt::AlignCenter,"Km/h");

}

void Widget::drawScale(QPainter &painter,int radius)
{
    //画刻度
    //1.算出一个刻度需要的旋转的角度
    angle = 240*1.0/60; //240*1.0的作用是扩大变量类型,把int型阔成double,保留小数
    painter.save();//保存原点,此时此刻在原点,x在三点钟方向
    painter.setPen(QPen(Qt::white,5));
    //2.设置第一个刻度的位置
    painter.rotate(startAngle);
    for(int i=0;i <= 60;i++){
        if(i >= 40){
            painter.setPen(QPen(Qt::red,5));
        }
        if(i % 5 == 0){
            //画长的刻度线
            painter.drawLine(radius - 20,0,radius-3,0);
        }else{//画短的刻度线
            painter.drawLine(radius - 8,0,radius-3,0);
        }
        //画完后旋转
        // 【旋转坐标系】画完一根刻度线后,将坐标系顺时针旋转一个刻度的角度 (angle)
        // 下一根刻度线就会在旋转后的新坐标系 (Y=0) 上绘制,从而形成一圈放射状的刻度
        painter.rotate(angle);
    }
    //画完刻度之后恢复坐标系
    painter.restore();
    painter.setPen(QPen(Qt::white,5));
}

void Widget::drawScaleText(QPainter &painter, int radius)
{
    //写刻度文字
    QFont font("Arial",15);
    font.setBold(true);
    painter.setFont(font);//设置字体样式和大小
    int r = radius - 49;//定义一个半径
    for(int i = 0;i <= 60;i++){
        if(i % 5 == 0){
            //保存坐标系
            painter.save();
            //算出平移点,弧度=角度*3.1415/180
            int delX = qCos(qDegreesToRadians(210-angle*i)) * r;//QT中sin认的是弧度
            int delY = qSin(qDegreesToRadians(210-angle*i)) * r;
            //平移坐标系
            painter.translate(QPoint(delX,-delY));
            //旋转坐标系
            painter.rotate(-120+angle*i);//angle=4,30*4=120的时候,实参为0,120
            //写上文字
            painter.drawText(-25,-25,50,30,Qt::AlignCenter,QString::number(i*4));
            //恢复坐标系
            painter.restore();
        }
    }
}

void Widget::drawPointLine(QPainter& painter,int lenth)
{
    //画指针,线
    //坐标轴先回到原点
    painter.save();

    painter.setBrush(Qt::white);
    painter.setPen(Qt::NoPen);
    static const QPointF points[4] = {
        QPointF(0,0.0),
        QPointF(200.0,-1.1),
        QPointF(200.0,1.1),
        QPointF(0,15.0),
    };

    painter.rotate(startAngle + angle * currentValue);
    painter.drawPolygon(points, 4);
    painter.drawLine(60,0,lenth,0);

}

void Widget::drawSpeedPie(QPainter &painter, int radius)
{
    //画扇形
    painter.restore();
    QRect rectangle(-radius,-radius,radius*2,radius*2);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(200,0,0,80));
    painter.drawPie(rectangle,(360-startAngle)*16,-angle*currentValue*16);
}

void Widget::startSpeed()
{
    timer = new QTimer(this);
    currentValue = 0;
    connect(timer,&QTimer::timeout,[=](){
        if(mark == 0){
            currentValue++;
            if(currentValue >= 61){
                mark = 1;
            }
        }
        if(mark == 1){
            currentValue--;
            if(currentValue == 0){
                mark = 0;
            }
        }

        update();
    });
    timer->start(50);
}

void Widget::drawEllipseInnerBlack(QPainter &painter, int radius)
{
    painter.setBrush(Qt::black);
    painter.drawEllipse(QPoint(0,0),radius,radius);
}

void Widget::drawEllipseInnerShine(QPainter &painter, int radius)
{
    QRadialGradient radialGradient(0,0,radius);
    radialGradient.setColorAt(0.0, QColor(255,0,0,200));
    radialGradient.setColorAt(1.0, QColor(0,0,0,100));
    painter.setBrush(radialGradient);
    painter.drawEllipse(QPoint(0,0),radius,radius);
}

void Widget::drawEllipseOutterShine(QPainter &painter, int radius)
{
    QRect rectangle(-radius,-radius,radius*2,radius*2);
    painter.setPen(Qt::NoPen);

    QRadialGradient radialGradient(0,0,radius);
    radialGradient.setColorAt(1.0, QColor(255,0,0,200));
    radialGradient.setColorAt(0.97, QColor(255,0,0,120));
    radialGradient.setColorAt(0.9, QColor(0,0,0,0));
    radialGradient.setColorAt(0, QColor(0,0,0,0));
    painter.setBrush(radialGradient);

    painter.drawPie(rectangle,(360-150)*16,-angle*61*16);
}

void Widget::drawLogo(QPainter &painter, int radius)
{
    QRect rectangle(-65,radius*0.38,130,50);

    painter.drawPixmap(rectangle,QPixmap(":/icon.png"));
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    int rad = height()/2;

    //初始化画布
    initCanvas(painter);
    //画小圆
    drawMiddleCircle(painter,60);
    //画刻度
    drawScale(painter,rad);
    //画刻度文字
    drawScaleText(painter,rad);
    //画指针,线
    drawPointLine(painter,rad-58);
    //画扇形
    drawSpeedPie(painter,rad+25);
    //画渐变内圈圆
    drawEllipseInnerShine(painter,110);
    //画黑色内圈
    drawEllipseInnerBlack(painter,80);
    //画当前速度
    drawCurrentSpeed(painter);
    //画外环发光圈
    drawEllipseOutterShine(painter,rad+25);
    //画一个汽车logo
    drawLogo(painter,rad);
}




Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐