P6 自定义控件
6.1 QPaintEvent绘图事件
QPaintEvent 是 Qt 框架中一个重要的事件类,专门用于处理绘图事件。当 Qt 视图组件需要重绘自己的一部分时,就会产生 QPaintEvent 事件。这通常发生在以下几种情况:
1. 窗口第一次显示时:当窗口或控件第一次出现在屏幕上时,系统会生成一个 QPaintEvent 事件, 通知窗口进行自身的绘制。
2. 窗口大小改变时:当用户改变窗口的大小时,窗口的内容通常需要重新绘制以适应新的尺寸。
3. 窗口部分被遮挡后又重新显示时:如果窗口被其他窗口遮挡,然后又重新露出来,被遮挡的部分通 常需要重新绘制。
4. 手动请求重绘:通过调用 QWidget 的 update() 或 repaint() 方法,可以手动触发重绘事件。 在 Qt 应用程序中,通常通过重写 QWidget 的 paintEvent(QPaintEvent *) 方法来处理绘制逻辑。
例如:
class MyWidget : public QWidget {
protected:
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
// 绘制逻辑
}
};
在 paintEvent 方法中,您可以创建一个 QPainter 对象并使用它来执行绘制操作。 QPainter 可以绘 制各种基本图形,如线条、矩形、椭圆等,还可以绘制文本和图像。重写 paintEvent 是在 Qt 中进行 自定义绘制的标准做法。
6.2 QPainter画家
6.2.1 概述
QPainter 是 Qt 库中用于在屏幕上进行绘画的类。它提供了各种绘制功能,比如画线、画图形、画文本 等。
以下是一些基本的用法示例:
1. 初始化 QPainter:首先,您需要一个 QPaintDevice ,比如一个 QWidget 或 QPixmap ,然后使用它来初始化 QPainter 对象。
设置画笔和画刷:您可以设置画笔(用于描边)和画刷(用于填充)的颜色、样式等。
绘制图形:使用 QPainter 的方法来绘制线条、矩形、圆形、文本等。
结束绘制:完成绘制后, QPainter 对象会在其析构函数中自动结束绘制。 请注意, QPainter 的使用依赖于 Qt 的事件循环,因此通常在 QWidget 的 paintEvent 或者类似的事件处理函数中使用它。如果您在 Qt 应用程序中使用 QPainter,请确保您遵循 Qt 的事件驱动机制。
2 QPainter画家
6.2.1 概述
QPainter 是 Qt 库中用于在屏幕上进行绘画的类。它提供了各种绘制功能,比如画线、画图形、画文本等。
以下是一些基本的用法示例:
1. 初始化 QPainter:首先,您需要一个 QPaintDevice ,比如一个 QWidget 或 QPixmap ,然后使用它来初始化 QPainter 对象。
QPainter painter(this); // 假设在 QWidget 的子类中
设置画笔和画刷:您可以设置画笔(用于描边)和画刷(用于填充)的颜色、样式等。
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 的事件驱动机制。
6.2.2 渐变色
6.2.2.1 线性渐变
QLinearGradient 是 Qt 框架中用于创建线性渐变的类。线性渐变是一种从一个颜色平滑过渡到另一个颜色的效果,其变化沿着两个点之间的直线进行。这种渐变在图形用户界面设计中非常常见,用于添加深度、立体感或动态效果。
基本用法
要使用 QLinearGradient ,你需要执行以下几个基本步骤:
1. 创建 QLinearGradient 对象:指定渐变的起点和终点坐标。
2. 设置颜色停靠点:在渐变线上定义颜色和相应的位置。
3. 使用渐变创建 QBrush :用 QLinearGradient 对象来创建一个 QBrush ,然后用它在 QPainter
中进行绘制。
示例代码
以下是一个创建和使用 QLinearGradient 的示例代码:
#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 可以用于填充任何形状,包括矩形、椭圆、多边形等。
为了获取更好的视觉效果,可以启用 QPainter 的抗锯齿选项( QPainter::Antialiasing )。
请注意,当窗口小部件的大小发生变化时,渐变的效果可能也会随之改变,除非你相应地调整渐变
的起点和终点坐标或使用其他方法来适应大小变化。
6.2.2.2 径向渐变
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 非常适用于创建像按钮、指示灯或其他需要有深度感和立体感的界面元素。
6.2.2.3 圆锥形渐变
QConicalGradient 是 Qt 框架中用于创建圆锥形渐变的类。圆锥渐变是一种渐变效果,其中颜色沿着圆锥的轮廓变化,类似于旋转颜色轮。这种渐变以其中心点为基点,颜色沿圆周分布,可以创建出富有动感的视觉效果。
基本用法
要使用 QConicalGradient ,你通常需要做以下几个步骤:
1. 创建 QConicalGradient 对象:指定渐变的中心点和起始角度。
2. 设置颜色停靠点:为渐变添加不同的颜色和对应的位置(角度)。
3. 使用渐变创建 QBrush :使用这个渐变对象来创建一个 QBrush ,然后应用到 QPainter 中进行绘图。
示例代码
下面是一个如何创建和使用 QConicalGradient 的简单示例:
#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 非常适合用于创建旋转或动态效果的图形,例如加载指示器、进度条或任何需
要圆周颜色变化的场景。
6.3 坐标转移
在 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 的绘图原点移动到控件的中心。
这在进行中心对称绘制或者需要以控件中心为基准进行绘图时特别有用。
4 画雷达案例

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QTimer>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
timer = new QTimer(this);
connect(timer, &QTimer::timeout,[=](){
//不断变化扇形的启动位置
startAngle += 30;
if(startAngle >= 360){
startAngle = 0;
}
update();
});
timer->setInterval(100);
timer->start();
}
Widget::~Widget()
{delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// 抗锯齿
painter.setRenderHint(QPainter::Antialiasing,true);
// 把背景色刷成黑色
QBrush brush(Qt::black);
painter.setBrush(brush);
painter.drawRect(rect());
// 平移坐标轴到窗口正中间
painter.translate(rect().center());
// 最小圆的半径
int rEve = height()/2/7; //800 600 600/7
int dataTmp = rEve * 7; //用height()/2 导致突出,上面那行代码除不尽
// 设置画笔,绿色,像素4
QPen pen(Qt::green,4);
painter.setPen(pen);
painter.setBrush(Qt::NoBrush); //不要画刷,否则只能看到最外面的圈
for(int i=1; i <= 7; i++){
painter.drawEllipse(QPoint(0,0),rEve*i,rEve*i); //依次画出7个圆形
}
painter.drawLine(-rEve*7,0,rEve*7,0);
painter.drawLine(0,-rEve*7,0,rEve*7);
//设置锥形渐变
QConicalGradient conGradient(0,0,-startAngle);
conGradient.setColorAt(0,QColor(0,255,0,200));
conGradient.setColorAt(0.1,QColor(0,255,0,100));
conGradient.setColorAt(0.2,QColor(0,255,0,0));
conGradient.setColorAt(1,QColor(0,255,0,0));
//直接用渐变色指定画刷
painter.setBrush(conGradient);
painter.setPen(Qt::NoPen); //去除扇形区域边框
//画出扇形,启动角度是startAngle,由定时器来修改
painter.drawPie(QRect(-dataTmp,-dataTmp,dataTmp*2,dataTmp*2),
-startAngle*16,70*16);
}
仪表表盘
5.1 初步完成

#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 >= 51){
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());
QRadialGradient radialGradient(0, 0, height()/2); // 中心和半径 (50, 50, 50)
// 设置颜色停靠点
radialGradient.setColorAt(0.0, QColor(255,0,0,50)); // 中心颜色
radialGradient.setColorAt(1.0, QColor(255,0,0,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));
// painter.drawText(0,0,QString::number(currentValue));
painter.drawText(QRect(-60,-60,120,120),Qt::AlignCenter,QString::number(currentV
alue));
//画刻度
//1. 算出一个刻度需要旋转的角度
double angle = 270*1.0 / 50;//270*1.0的作用是扩大变量类型,把int型阔成double,保留小
数
//2. 设置第一个刻度的位置
painter.setFont(QFont("华文宋体",15));
painter.save();//保存当前坐标位置,此时此刻是在原点,x在3点钟方向
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 - 10), 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);
}
//画完后旋转
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 rentangle(-height()/2+58,-height()/2+58,height()-116,height()-116);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(235,152,50,150));
painter.drawPie(rentangle,(360-135)*16,-angle*currentValue*16);//angle前面取
负数,为了让它顺时针方向画
}
5.2 稍微美化
稍微修改后,依然存在数字方向的问题!
#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(0,0,QString::number(currentValue));
painter.drawText(QRect(-60,-60,120,120),Qt::AlignCenter,QString::number(currentV
alue));
//画刻度
//1. 算出一个刻度需要旋转的角度
double angle = 240*1.0 / 60;//270*1.0的作用是扩大变量类型,把int型阔成double,保留小
数
//2. 设置第一个刻度的位置
painter.setFont(QFont("华文宋体",15));
painter.save();//保存当前坐标位置,此时此刻是在原点,x在3点钟方向
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 - 45, 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);
}
//画完后旋转
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 rentangle(-height()/2,-height()/2,height(),height());
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(235,152,50,150));
painter.drawPie(rentangle,(360-150)*16,-angle*currentValue*16);//angle前面取
负数,为了让它顺时针方向画
}
5.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();
};
#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;
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.setFont(QFont("华文宋体",20));
// painter.drawText(0,0,QString::number(currentValue));
painter.drawText(QRect(-60,-60,120,120),Qt::AlignCenter,QString::number(currentV
alue));
}
void Widget::drawScale(QPainter &painter, int radius)
{
//画刻度
//1. 算出一个刻度需要旋转的角度
angle = 240*1.0 / 60;//270*1.0的作用是扩大变量类型,把int型阔成double,保留小数
//保存当前坐标位置,此时此刻是在原点,x在3点钟方向
painter.save();
painter.setPen(QPen(Qt::white,5));
//2. 设置第一个刻度的位置
painter.rotate(startAngle);
for(int i=0;i<=60;i++){
if(i % 5 == 0){
//画长的刻度线
painter.drawLine(radius - 20, 0, radius - 3 ,0);
}else{//画短的刻度线
painter.drawLine(radius - 8, 0, radius - 3 ,0);
}
//画完后旋转
painter.rotate(angle);
}
painter.restore();
}
void Widget::drawScaleText(QPainter &painter, int radius)
{
//写刻度文字
painter.setFont(QFont("华文宋体",18));
int r = radius - 46;
for(int i=0; i<=60; i++){
if(i % 5 == 0){
//保存坐标系
painter.save();
//算出平移点,弧度=角度*3.1415/180
int delX = qCos( (210-angle*i)*M_PI/180) * 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.restore();
painter.save();
painter.rotate(startAngle + angle * currentValue);
painter.drawLine(60,0,lenth,0);
}
void Widget::drawSpeedPie(QPainter &painter, int radius)
{
painter.restore();
QRect rentangle(-radius,-radius,radius*2,radius*2);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(235,152,50,150));
painter.drawPie(rentangle,(360-startAngle)*16,-angle*currentValue*16);//angle
前面取 负数,为了让它顺时针方向画
}
void Widget::startSpeed()
{
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);
}
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);
}

5.4 画一个指针
在Qt中,使用 QPainter 来绘制一个类似指南针的指针,你通常会遵循以下步骤:
1. 创建一个QWidget或QMainWindow的子类:这是你绘图的画布。
2. 重写 paintEvent 方法:这个方法是Qt中绘制自定义图形的关键地方。
3. 使用QPainter:在 paintEvent 中创建一个 QPainter 对象,并用它来绘制你的指针。
4. 绘制指针:可以通过绘制一个线条或者一个具有特定形状的多边形来创建指针,比如一个三角形。
5. 旋转指针:如果你想让指针能够像指南针那样旋转,你可以使用 QPainter 的旋转功能。
下面是一个简单的例子,展示如何在Qt中绘制一个静态的指针:
#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);
painter.setRenderHint(QPainter::Antialiasing,true);
painter.setBrush(Qt::red);
painter.translate(rect().center());
static const QPointF points[3] = {
QPointF(-10.0, 0),
QPointF(0, -200.0),
QPointF(10.0, 0),
};
painter.drawPolygon(points, 4);
}
这个代码片段创建了一个自定义的QWidget,它在中间绘制了一个三角形作为指针。你可以通过添加更多的逻辑来使指针动态旋转,比如根据数据改变指针的方向。在实际应用中,指针的样式、大小和颜色都可以根据你的需要进行自定义。

5.5 内环
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::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);
}

5.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);
void drawEllipseInnerShine(QPainter& painter,int radius);
void drawEllipseOutterShine(QPainter& painter,int radius);
void drawLogo(QPainter& painter,int radius);
};
#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(0,0,QString::number(currentValue));
painter.drawText(QRect(-60,-60,120,70),Qt::AlignCenter,QString::number(currentVa
lue*4));
QFont font2("Arial",13);
font.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;//270*1.0的作用是扩大变量类型,把int型阔成double,保留小数
//保存当前坐标位置,此时此刻是在原点,x在3点钟方向
painter.save();
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);
}
//画完后旋转
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( (210-angle*i)*M_PI/180) * 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);
//坐标轴先回到原点
painter.restore();
}
void Widget::drawSpeedPie(QPainter &painter, int radius)
{
QRect rentangle(-radius,-radius,radius*2,radius*2);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(255,0,0,80));
painter.drawPie(rentangle,(360-startAngle)*16,-angle*currentValue*16);//angle
前面取 负数,为了让它顺时针方向画
}
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 rentangle(-radius,-radius,radius*2,radius*2);
painter.setPen(Qt::NoPen);
QRadialGradient radiaGradient(0,0,radius);
radiaGradient.setColorAt(1,QColor(255,0,0,200));
radiaGradient.setColorAt(0.97,QColor(255,0,0,120));
radiaGradient.setColorAt(0.9,QColor(0,0,0,0));
radiaGradient.setColorAt(0,QColor(0,0,0,0));
painter.setBrush(radiaGradient);
painter.drawPie(rentangle,(360-150)*16,-angle*61*16);//angle前面取 负数,为了让
它顺时针方向画
}
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);
}

6.6 汽车表盘参考样式

被折叠的 条评论
为什么被折叠?



