QT ⑤ 绘图类

一、QPaintEvent

1、QPainEvent 事件

​ 要想实现绘图操作,就必须重写QPainEvent事件,该事件是负责绘图的事件

2、重写

//声明
void paintEvent(QPaintEvent *e);

//重写画图事件
void MyPainter::paintEvent(QPaintEvent *e)
{
    //系统默认是交给基类处理
    return QWidget::paintEvent(e);
}

二、QPainter

1、QPainter绘图类

​ QPainter是绘图类的执行类,可以设置绘图所用的QPen笔、线条等。

2、setRenderHint()

功能说明:

  • 控制绘图算法的细节,如是否启用抗锯齿、文本平滑等
  • 不同的渲染提示会影响绘图性能和视觉效果(通常质量越高,性能消耗略大)

常用参数(QPainter::RenderHint):

  • QPainter::Antialiasing:启用抗锯齿(对线条、图形边缘效果明显)
  • QPainter::TextAntialiasing:启用文本抗锯齿(使文字边缘更平滑)
  • QPainter::SmoothPixmapTransform:启用图片缩放时的平滑过渡(避免失真)

示例:

QPainter painter(this);
// 启用抗锯齿(绘制图形更平滑)
painter.setRenderHint(QPainter::Antialiasing, true);
// 绘制圆形(边缘会更平滑)
painter.drawEllipse(50, 50, 100, 100);

// 启用文本抗锯齿
painter.setRenderHint(QPainter::TextAntialiasing, true);
painter.drawText(50, 200, "平滑的文字");

3、setPen(QPen)

​ 该函数用于设置画笔

4、setBrush(QBrush)

​ 该函数用于设置画刷

5、 QPainter::rotate()

基本功能

  • 作用:以当前坐标系的原点(0,0) 为中心,将坐标系旋转指定的角度。
  • 后续所有绘图操作(如 drawLine()drawRect() 等)都会在旋转后的坐标系中进行,看起来像是图形被 “旋转” 了。

函数原型

void QPainter::rotate(qreal angle);
  • 参数 angle:旋转角度,单位为度(°),而非弧度。
    • 正数:逆时针旋转(默认方向)。
    • 负数:顺时针旋转

关键特性

  1. 旋转中心 默认以当前坐标系的原点(0,0)为旋转中心。若要围绕其他点旋转(如窗口中心),需先通过 translate(x, y) 将原点移到目标点,再调用 rotate()

  2. 状态累积 多次调用 rotate()累积旋转角度。例如:

    painter.rotate(30);  // 累计旋转30°(逆时针)
    painter.rotate(60);  // 累计旋转90°(30+60)
    
  3. 状态管理 旋转会改变坐标系状态,若需恢复到旋转前的状态,需配合 save()restore() 使用:

    • save():保存当前绘图状态(包括坐标系、画笔、画刷等)。
    • restore():恢复到最近一次 save() 时的状态。

6、QPainter::translate()

基本功能

  • 核心作用:将当前绘图坐标系的原点(0,0) 移动到新位置,新位置相对于原原点的偏移量由参数指定。
  • 对后续绘图的影响:平移后,所有绘图操作的坐标值都会以新原点为基准计算,看起来像是 “整个绘图区域被移动了”,但本质是坐标系的位置发生了改变。

函数原型

void QPainter::translate(qreal dx, qreal dy);
  • 参数说明
    • dx:原点在 x 轴方向的偏移量(单位:像素)。
      • 正数:原点向右移动;
      • 负数:原点向左移动。
    • dy:原点在 y 轴方向的偏移量(单位:像素)。
      • 正数:原点向下移动(Qt 坐标系默认 “上为 y=0,下为 y 增大”);
      • 负数:原点向上移动。

关键特性

1. 坐标系原点的 “相对性”

translate 的偏移是相对于当前原点的,而非窗口左上角。例如:

// 初始原点在窗口左上角 (0,0)
painter.translate(100, 200); // 新原点 = (0+100, 0+200) = (100,200)
painter.translate(50, 50);   // 新原点 = (100+50, 200+50) = (150,250)
2. 与其他变换的配合(核心场景)

translate 常与 rotate()(旋转)、scale()(缩放)配合,实现 “围绕任意点变换”。 典型场景:围绕窗口中心旋转图形(如时钟指针围绕中心旋转):

// 步骤1:将原点平移到窗口中心(目标旋转点)
painter.translate(width()/2, height()/2); 
// 步骤2:围绕新原点(窗口中心)旋转
painter.rotate(30); // 此时旋转中心是窗口中心,而非初始左上角
// 步骤3:绘制图形(以新原点为基准)
painter.drawLine(0, 0, 100, 0); // 从中心(新原点)向右画线段
3. 状态管理(save/restore)

translate 会改变坐标系状态,若需恢复到平移前的状态(避免影响后续绘图),必须配合 save()restore()

  • save():保存当前绘图状态(包括坐标系、画笔、画刷等);
  • restore():恢复到最近一次 save() 时的状态。

7、QPainter::drawArc()

​ 函数用于绘制椭圆弧(当宽高相等时为圆弧),其参数决定了圆弧的位置、大小和角度范围

void drawArc(int x, int y, int width, int height, int startAngle, int spanAngle);

参数详解

1. 位置与大小参数(定义外接矩形)+ 角度参数(定义圆弧范围)
参数 含义
x / y 外接矩形的左上角坐标(以窗口左上角为原点,x 向右递增,y 向下递增)。
width / height 外接矩形的宽度和高度: - 若 width == height,绘制的是圆弧(圆的一部分); - 若 width != height,绘制的是椭圆弧(椭圆的一部分)。
rect QRect 对象,等价于 (x, y, width, height) 的封装,直接定义外接矩形的位置和大小。
startAngle 圆弧的起始角度,单位为1/16 度(非直接度数)。 - 基准方向:3 点钟方向为 0°(即水平向右)。 - 角度递增方向:默认逆时针为正方向。
spanAngle 圆弧跨越的角度范围,单位同样为1/16 度: - 正数:逆时针绘制圆弧; - 负数:顺时针绘制圆弧; - 范围限制:通常在 -360°×16 到 360°×16 之间(即完整圆的角度)。

角度转换规则:实际角度(度)需乘以 16 才能作为参数,例如:

  • 90° → 90 * 16 = 1440
  • 180° → 180 * 16 = 2880
  • 360° → 360 * 16 = 5760

三、QPen

概述

QPen 是 Qt 绘图系统中用于定义线条绘制属性的类,主要控制图形边缘、轮廓线的样式,如颜色、宽度、线条样式(实线、虚线等)、端点样式和连接样式等。它通常与 QPainter 配合使用,用于绘制直线、矩形、椭圆等图形的轮廓。

常用构造函数

构造函数 说明
QPen() 默认构造函数,创建黑色、1px 宽的实线画笔
QPen(const QColor &color) 使用指定颜色创建画笔,默认宽度 1px,实线

常用成员函数

1. 颜色与填充相关

函数 说明
void setColor(const QColor &color) 设置画笔颜色
QColor color() const 获取当前画笔颜色
void setBrush(const QBrush &brush) 设置画笔填充样式(可用于渐变、纹理等)
QBrush brush() const 获取当前画笔的填充样式

2. 线条宽度与样式

函数 说明
void setWidth(int width) 设置线条宽度(整数像素)
void setWidthF(qreal width) 设置线条宽度(支持小数像素,更精确)
int width() const / qreal widthF() const 获取线条宽度
void setStyle(Qt::PenStyle style) 设置线条样式(如实线、虚线等)
Qt::PenStyle style() const 获取当前线条样式

常用 Qt::PenStyle 枚举值

  • Qt::SolidLine:实线(默认)
  • Qt::DashLine:虚线(短横线)
  • Qt::DotLine:点线
  • Qt::DashDotLine: dash-dot 交替线
  • Qt::DashDotDotLine: dash-dot-dot 交替线
  • Qt::NoPen:无线条(不绘制轮廓)

3. 端点与连接样式

函数 说明
void setCapStyle(Qt::PenCapStyle cap) 设置线条端点样式
Qt::PenCapStyle capStyle() const 获取端点样式
void setJoinStyle(Qt::PenJoinStyle join) 设置线条连接点样式(多段线拐角处)
Qt::PenJoinStyle joinStyle() const 获取连接点样式

常用端点样式

  • Qt::SquareCap:方形端点(默认)
  • Qt::RoundCap:圆形端点
  • Qt::FlatCap:平端(不超出线段终点)

常用连接样式

  • Qt::BevelJoin:斜角连接(默认)
  • Qt::RoundJoin:圆角连接
  • Qt::MiterJoin:尖角连接

使用示例

#include <QPainter>
#include <QWidget>

void MyWidget::paintEvent(QPaintEvent *event) {
    Q_UNUSED(event);
    QPainter painter(this);
    
    // 创建画笔并设置属性
    QPen pen;
    pen.setColor(Qt::blue);          // 颜色
    pen.setWidth(3);                 // 宽度
    pen.setStyle(Qt::DashDotLine);   // 线条样式
    pen.setCapStyle(Qt::RoundCap);   // 端点样式
    pen.setJoinStyle(Qt::RoundJoin); // 连接样式
    
    // 将画笔应用到 painter
    painter.setPen(pen);
    
    // 绘制图形(使用当前画笔)
    painter.drawRect(50, 50, 200, 100); // 矩形
    painter.drawLine(50, 200, 250, 200); // 直线
}

四、QBrush

概述

QBrush 是 Qt 绘图系统中用于定义填充区域属性的类,主要控制图形(如矩形、椭圆、多边形等)内部的填充效果,包括纯色填充、渐变填充、纹理填充等。它通常与 QPainter 配合使用,与 QPen(控制轮廓线)共同决定图形的外观。

常用构造函数

构造函数 说明
QBrush() 默认构造函数,创建无填充的画刷
QBrush(Qt::GlobalColor color, Qt::BrushStyle style = Qt::SolidPattern) 使用指定颜色和样式创建画刷
QBrush(const QColor &color, Qt::BrushStyle style = Qt::SolidPattern) 使用 QColor 对象和样式创建画刷
QBrush(const QPixmap &pixmap) 使用像素图作为纹理创建画刷
QBrush(const QGradient &gradient) 使用渐变对象创建画刷

常用成员函数

1. 填充样式相关

函数 说明
void setStyle(Qt::BrushStyle style) 设置填充样式
Qt::BrushStyle style() const 获取当前填充样式
bool isOpaque() const 判断画刷是否不透明

2. 颜色与纹理相关

函数 说明
void setColor(const QColor &color) 设置画刷颜色(对纯色、渐变等样式有效)
QColor color() const 获取当前画刷颜色
void setTexture(const QPixmap &pixmap) 设置纹理填充(使用像素图)
QPixmap texture() const 获取当前纹理像素图

3. 渐变相关

函数 说明
void setGradient(const QGradient &gradient) 设置渐变填充
const QGradient *gradient() const 获取当前渐变对象(若有)

常用枚举类型

Qt::BrushStyle(填充样式)

枚举值 说明
Qt::NoBrush 无填充(透明)
Qt::SolidPattern 纯色填充(默认)
Qt::Dense1Pattern ~ Qt::Dense7Pattern 不同密度的点 / 线图案填充(1 最密,7 最疏)
Qt::HorPattern 水平线条填充
Qt::VerPattern 垂直线条填充
Qt::CrossPattern 十字线填充
Qt::DiagCrossPattern 对角线十字填充
Qt::LinearGradientPattern 线性渐变填充
Qt::RadialGradientPattern 径向渐变填充
Qt::ConicalGradientPattern 锥形渐变填充
Qt::TexturePattern 纹理填充(使用像素图)

渐变类型(QGradient 派生类)

类名 说明
QLinearGradient 线性渐变(沿直线从起点到终点过渡颜色)
QRadialGradient 径向渐变(从中心点向外辐射过渡颜色)
QConicalGradient 锥形渐变(围绕中心点旋转过渡颜色)

使用示例

#include <QPainter>
#include <QWidget>
#include <QLinearGradient>

void MyWidget::paintEvent(QPaintEvent *event) {
    Q_UNUSED(event);
    QPainter painter(this);
    
    // 1. 纯色填充
    QBrush solidBrush(Qt::lightGray, Qt::SolidPattern);
    painter.setBrush(solidBrush);
    painter.drawRect(50, 50, 100, 80);
    
    // 2. 纹理填充
    QBrush textureBrush(QPixmap("pattern.png")); // 使用图片作为纹理
    painter.setBrush(textureBrush);
    painter.drawRect(200, 50, 100, 80);
    
    // 3. 线性渐变填充
    QLinearGradient gradient(50, 200, 150, 280); // 从(50,200)到(150,280)的渐变
    gradient.setColorAt(0, Qt::red);    // 起点颜色
    gradient.setColorAt(1, Qt::yellow); // 终点颜色
    QBrush gradientBrush(gradient);
    painter.setBrush(gradientBrush);
    painter.drawEllipse(50, 200, 100, 80);
}

五、绘制时钟示例

头文件

#ifndef MYPAINTER_H
#define MYPAINTER_H

#include <QWidget>
#include <QPaintEvent>      //画图事件
#include <QPainter>         //画图类
#include <QPen>             //画笔类
#include <QBrush>           //画刷类
#include <QRgb>             //rgb颜色类
#include <QTimer>           //定时器
#include <QPushButton>
#include <QLabel>


QT_BEGIN_NAMESPACE
namespace Ui {
class MyPainter;
}
QT_END_NAMESPACE

class MyPainter : public QWidget
{
    Q_OBJECT

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



private slots:
    //开始计时槽函数
    void startTimerSlot();
    //暂停计时槽函数
    void stopTimerSlot();
    //计时超时
    void timer_out();
private:
    Ui::MyPainter *ui;
    //重写paint事件
    void paintEvent(QPaintEvent *e);
    //绘制时钟刻度
    void painTheClock();
    //绘制秒针
    void painSecond();
    //绘制分针
    void painMinute();
    //绘制时针
    void painHour();
private:
    //开始计时按钮
    QPushButton *startTimer;
    //暂停计时按钮
    QPushButton *stopTimer;
    //计时显示
    QLabel *label;
    //计时器
    QTimer *timer;
    //秒计数器
    int second = 0;
};
#endif // MYPAINTER_H

实现文件

#include "mypainter.h"
#include "ui_mypainter.h"

MyPainter::MyPainter(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyPainter)
{
    //ui->setupUi(this);
    setFixedSize(640,480);
    setWindowTitle("定制时钟");
    setWindowIcon(QIcon(":/favicon.ico"));
    setStyleSheet(QString("background-color:rgb(220, 220, 220);"));   //设置背景颜色

    //初始化秒数显示
    label = new QLabel(this);
    label->setFixedSize(100,20);
    label->setText(QString("当前秒数: %1").arg(second));
    label->move(width()-120,height()-90);
    //初始化开始计时按钮
    startTimer = new QPushButton(this);
    startTimer->setText("开启计时器");
    startTimer->setFixedSize(60,20);
    startTimer->move(width()-120,height()-60);
    //初始化暂停计时按钮
    stopTimer = new QPushButton(this);
    stopTimer->setText("暂停计时器");
    stopTimer->setFixedSize(60,20);
    stopTimer->move(width()-120,height()-30);
    //初始化计时器
    timer = new QTimer;

    //绑定开始计时信号与槽
    connect(startTimer , &QPushButton::clicked , this , &MyPainter::startTimerSlot);
    //绑定暂停计时信号与槽
    connect(stopTimer , &QPushButton::clicked , this , &MyPainter::stopTimerSlot);
    //绑定计时器信号与槽
    connect(timer , &QTimer::timeout , this , &MyPainter::timer_out);
}

MyPainter::~MyPainter()
{
    //delete ui;
}

//开始计时
void MyPainter::startTimerSlot()
{
    timer->start(10);                                       //10毫秒触发一次
}

//暂停计时
void MyPainter::stopTimerSlot()
{
    timer->stop();
}

//计时超时
void MyPainter::timer_out()
{
    //qDebug() << second;
    this->repaint();                                        //重绘界面
    second++;                                               //增加秒计数
    label->setText(QString("当前秒数: %1").arg(second));     //更新label显示秒数

}

//绘制时钟刻度
void MyPainter::painTheClock()
{
    //这里pain(this) 中 this 表示画图显示在哪个画图设备上,也可以显示在Pixmap或者其他画图设备上
    QPainter pain(this);
    pain.setRenderHint(QPainter::Antialiasing,true);        //启动抗锯齿
    pain.translate(width() / 2, height() / 2);              //确定圆心

    int radius = height() / 2 - 10;                         //确定半径

    QPen pen;                                               //画笔类
    pen.setColor(QColor(qRgb(170,255,255)));                //设置颜色为rgb
    pen.setWidth(10);                                       //设置画笔宽度 10px
    pen.setStyle(Qt::SolidLine);                            //设置画笔线条样式

    pain.setPen(pen);                                       //设置画笔

    int i = 0;                                              //控制变量
    const int angleStep = 6;                                //角度步长
    const int minute = 10;                                  //分钟刻度
    const int hour = 15;                                    //小时刻度
    while(i < 60){

        // 保存当前绘图状态
        pain.save();

        // 旋转坐标系到当前刻度的角度
        pain.rotate(i * angleStep);

        // 根据是否为小时刻度设置不同长度 和 颜色
        int tickLength;
        if(i % 5 == 0)
        {
            pen.setColor(QColor(qRgb(255,170,255)));
            tickLength = hour;
            pain.setPen(pen);
        }
        else
        {
            pen.setColor(QColor(qRgb(170,255,255)));
            tickLength = minute;
            pain.setPen(pen);
        }


        // 绘制刻度(从圆的边缘向内绘制一条短线)
        pain.drawLine(radius - tickLength, 0, radius, 0);

        // 恢复绘图状态
        pain.restore();
        i++;
    }
}

//重写画图事件
void MyPainter::paintEvent(QPaintEvent *e)
{

    //绘制时钟刻度
    this->painTheClock();

    //绘制时分秒针
    QPainter pain(this);
    pain.setRenderHint(QPainter::Antialiasing,true);        //启动抗锯齿
    pain.translate(width() / 2, height() / 2);              //确定圆心

    QPen pen;                                               //画笔类
    pen.setColor(QColor(qRgb(170,170,255)));                //设置颜色为rgb
    pen.setWidth(10);                                       //设置画笔宽度 10px
    pen.setStyle(Qt::SolidLine);                            //设置画笔线条样式
    pain.setPen(pen);                                       //设置画笔



    const int sLen = 180;                                   //秒针长度
    const int mLen = 120;                                   //分针长度
    const int hLen = 80;                                    //时针长度

    const int angleStep = 6;                                //角度步长
    int angle = 0;                                          //角度初始化,默认为3点钟方向


    pain.save();                                            //保存绘图状态
    //绘制秒针
    angle = second * angleStep - 90;                        //计算秒针角度
    pain.rotate(angle);                                     //旋转角度
    pain.drawLine(0,0,sLen,0);                              //绘制秒针
    pain.rotate(-angle);                                    //恢复角度
    pain.restore();                                         //恢复绘图状态


    pain.save();
    //绘制分针
    angle = second / 60 * angleStep - 90;                   //计算分针角度
    pain.rotate(angle);                                     //旋转角度
    pain.drawLine(0,0,mLen,0);                              //绘制分针
    pain.rotate(-angle);                                    //恢复角度
    pain.restore();


    pain.save();
    //绘制时针
    angle = second / 720 * angleStep - 90;                  //计算时针角度
    pain.rotate(angle);                                     //旋转角度
    pain.drawLine(0,0,hLen,0);                              //绘制时针
    pain.restore();

}

运行截图

alt