Qt绘图功能学习笔记
Qt绘图功能学习笔记
1. 绘图基础概述
在Qt中,绘图功能主要通过QPainter类实现。QPainter提供了高度优化的函数,用于在各种设备(如QWidget、QPixmap、QImage等)上进行绘制操作。Qt的绘图系统基于画家模型(Painter Model),这意味着绘图操作会按照代码的顺序进行叠加,后绘制的内容会覆盖先绘制的内容。
1.1 绘图的基本流程
- 创建QPainter对象
- 设置绘图属性(如画笔、画刷、字体等)
- 执行绘图操作
- 结束绘图
1.2 绘图的时机
在Qt中,绘图通常在以下情况下进行:
- 重写QWidget的paintEvent()方法
- 在QPixmap或QImage上绘图
- 打印文档时
2. QPainter类详解
2.1 创建QPainter对象
// 在窗口部件上绘图
QPainter painter(this); // this指向QWidget子类实例// 在QPixmap上绘图
QPixmap pixmap(400, 300);
QPainter painter(&pixmap);// 在QImage上绘图
QImage image(400, 300, QImage::Format_RGB32);
QPainter painter(&image);
2.2 设置绘图属性
2.2.1 设置画笔(QPen)
画笔用于绘制线条和形状的轮廓。
// 创建并设置画笔
QPen pen; // 创建默认画笔
pen.setWidth(5); // 设置线宽为5像素
pen.setColor(QColor(200, 100, 50)); // 设置颜色为RGB值
// 或者使用命名颜色
// pen.setColor(QColor("#888888")); // 使用十六进制颜色值
// pen.setColor(Qt::red); // 使用Qt预定义颜色// 设置线条样式
// pen.setStyle(Qt::DashLine); // 虚线
// pen.setStyle(Qt::DotLine); // 点线
// pen.setStyle(Qt::DashDotLine); // 点划线
// pen.setStyle(Qt::DashDotDotLine); // 点点划线// 设置端点样式
// pen.setCapStyle(Qt::RoundCap); // 圆形端点
// pen.setCapStyle(Qt::SquareCap); // 方形端点
// pen.setCapStyle(Qt::FlatCap); // 平直端点// 设置连接样式
// pen.setJoinStyle(Qt::RoundJoin); // 圆形连接
// pen.setJoinStyle(Qt::BevelJoin); // 斜角连接
// pen.setJoinStyle(Qt::MiterJoin); // 尖角连接// 将画笔应用到画家
painter.setPen(pen);// 也可以直接使用简化方式
// painter.setPen(QPen(QColor(200, 100, 50), 5, Qt::SolidLine));
2.2.2 设置画刷(QBrush)
画刷用于填充形状的内部。
// 创建并设置画刷
QBrush brush(QColor(200, 100, 50)); // 创建纯色画刷// 设置画刷样式
// brush.setStyle(Qt::SolidPattern); // 纯色填充
// brush.setStyle(Qt::Dense1Pattern); // 密集点模式1
// brush.setStyle(Qt::HorPattern); // 水平线模式
// brush.setStyle(Qt::VerPattern); // 垂直线模式
// brush.setStyle(Qt::CrossPattern); // 十字线模式
// brush.setStyle(Qt::DiagCrossPattern); // 对角线模式// 使用渐变填充
// QLinearGradient gradient(0, 0, 100, 100);
// gradient.setColorAt(0, Qt::white);
// gradient.setColorAt(1, Qt::black);
// QBrush brush(gradient);// 使用纹理填充
// QPixmap pixmap("pattern.png");
// QBrush brush(pixmap);// 将画刷应用到画家
painter.setBrush(brush);
2.2.3 设置字体
QFont font("Arial", 12, QFont::Bold);
// font.setItalic(true); // 设置为斜体
// font.setUnderline(true); // 设置下划线
// font.setStrikeOut(true); // 设置删除线
// font.setLetterSpacing(QFont::AbsoluteSpacing, 2); // 设置字符间距painter.setFont(font);
2.2.4 设置渲染提示
// 设置抗锯齿
painter.setRenderHint(QPainter::Antialiasing);// 设置文本抗锯齿
// painter.setRenderHint(QPainter::TextAntialiasing);// 设置平滑图片变换
// painter.setRenderHint(QPainter::SmoothPixmapTransform);// 设置高质量抗锯齿
// painter.setRenderHint(QPainter::HighQualityAntialiasing);
2.3 坐标系统和变换
2.3.1 基本坐标系统
Qt的坐标系统原点在左上角,X轴向右,Y轴向下。
2.3.2 坐标变换
// 平移坐标系
painter.translate(100, 50); // 将原点移动到(100, 50)// 旋转坐标系
painter.rotate(45); // 顺时针旋转45度// 缩放坐标系
painter.scale(2.0, 1.5); // X轴放大2倍,Y轴放大1.5倍// 复位变换
painter.resetTransform();// 保存和恢复状态
painter.save(); // 保存当前状态
// 进行一些变换和绘图操作
painter.restore(); // 恢复之前的状态
3. 绘图操作详解
3.1 绘制基本图形
3.1.1 绘制点
painter.drawPoint(10, 10); // 在(10, 10)位置绘制一个点// 绘制多个点
QVector<QPoint> points;
points << QPoint(10, 10) << QPoint(20, 30) << QPoint(40, 50);
painter.drawPoints(points);
3.1.2 绘制线条
// 绘制一条线
painter.drawLine(400, 400, 500, 500); // 从(400, 400)到(500, 500)的线// 也可以使用QLine或QLineF
// painter.drawLine(QLine(400, 400, 500, 500));
// painter.drawLine(QLineF(400.0, 400.0, 500.0, 500.0)); // 浮点精度// 绘制多条线
QVector<QLine> lines;
lines << QLine(10, 10, 50, 50) << QLine(60, 10, 100, 50);
painter.drawLines(lines);
3.1.3 绘制矩形
// 绘制矩形
painter.drawRect(200, 100, 100, 100); // 左上角(200, 100),宽100,高100// 也可以使用QRect或QRectF
// painter.drawRect(QRect(200, 100, 100, 100));
// painter.drawRect(QRectF(200.0, 100.0, 100.0, 100.0)); // 浮点精度// 绘制圆角矩形
// painter.drawRoundedRect(200, 100, 100, 100, 10, 10); // 圆角半径为10
3.1.4 绘制椭圆和圆
// 绘制椭圆
painter.drawEllipse(200, 200, 50, 100); // 中心(200, 200),宽50,高100// 绘制圆
// painter.drawEllipse(200, 200, 50, 50); // 中心(200, 200),半径50
3.1.5 绘制多边形
// 绘制三角形
QPolygon polygon;
polygon.setPoints(3, 100, 20, 200, 50, 300, 300);
painter.drawPolygon(polygon);// 也可以使用QVector<QPoint>构建多边形
// QVector<QPoint> points;
// points << QPoint(100, 20) << QPoint(200, 50) << QPoint(300, 300);
// painter.drawPolygon(QPolygon(points));
3.1.6 绘制弧、弦和扇形
// 绘制弧
painter.drawArc(100, 100, 100, 100, 30 * 16, 120 * 16); // 角度以1/16度为单位// 绘制弦(弧加上连接两端的直线)
// painter.drawChord(100, 100, 100, 100, 30 * 16, 120 * 16);// 绘制扇形(弧加上连接到中心的两条线)
// painter.drawPie(100, 100, 100, 100, 30 * 16, 120 * 16);
3.2 绘制文本
// 绘制文本
QRectF rectF(0, 0, 200, 100);
painter.drawText(rectF, Qt::AlignHCenter, "正点原子");// 设置文本对齐方式
// painter.drawText(rectF, Qt::AlignLeft | Qt::AlignTop, "左上对齐");
// painter.drawText(rectF, Qt::AlignRight | Qt::AlignBottom, "右下对齐");
// painter.drawText(rectF, Qt::AlignCenter, "居中对齐");// 绘制带边框的文本
// painter.drawText(rectF, Qt::AlignCenter | Qt::TextWordWrap, "这是一段长文本,会自动换行", &boundingRect);
// painter.drawRect(boundingRect); // 绘制文本边框
3.3 绘制图像
// 加载图像
QImage image("example.png");// 绘制图像
painter.drawImage(10, 10, image); // 在(10, 10)位置绘制图像// 缩放绘制
// painter.drawImage(QRect(10, 10, 200, 150), image); // 缩放到指定大小// 绘制图像的一部分
// painter.drawImage(QPoint(10, 10), image, QRect(0, 0, 100, 100)); // 只绘制图像左上角100x100的部分
3.4 绘制路径
// 创建路径
QPainterPath path;
path.moveTo(20, 80); // 移动到起点
path.lineTo(20, 30); // 添加直线
path.cubicTo(80, 0, 50, 50, 80, 80); // 添加三次贝塞尔曲线// 添加其他形状到路径
// path.addRect(100, 100, 50, 50); // 添加矩形
// path.addEllipse(200, 200, 50, 50); // 添加椭圆
// path.addText(300, 300, QFont("Arial", 12), "文本"); // 添加文本// 绘制路径
painter.drawPath(path);
4. 实例分析
以下是一个完整的绘图示例,展示了如何在QWidget中重写paintEvent方法来实现各种绘图操作。
4.1 头文件 (widget.h)
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void paintEvent(QPaintEvent *event) override;private:Ui::Widget *ui;
};
#endif // WIDGET_H
4.2 实现文件 (widget.cpp)
#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)
{Q_UNUSED(event) // 未使用的参数,避免编译警告// 创建QPainter对象,this是指定给图的对象QPainter painter(this);// 设置抗锯齿,使绘制的图形边缘平滑painter.setRenderHint(QPainter::Antialiasing);// 创建并设置画笔QPen pen;pen.setWidth(5); // 设置线宽为5像素pen.setColor(QColor(200, 100, 50)); // 设置颜色为RGB值//pen.setColor(QColor("#888888")); // 也可以使用十六进制颜色值// 创建并设置画刷QBrush brush(QColor(200, 100, 50)); // 创建纯色画刷///* brush.setColor(QColor(200, 100, 50));*/ // 注释掉的代码,设置画刷颜色// 将画笔给画家painter.setPen(pen);// 将画刷给画家(这行被注释掉了)// painter.setBrush(brush);// 绘制矩形painter.drawRect(200, 100, 100, 100); // 左上角(200, 100),宽100,高100// 绘制多边形(三角形)QPolygon polygon;polygon.setPoints(3, 100, 20, 200, 50, 300, 300); // 设置三个点的坐标painter.drawPolygon(polygon);// 绘制直线painter.drawLine(400, 400, 500, 500); // 从(400, 400)到(500, 500)的线// 绘制椭圆painter.drawEllipse(200, 200, 50, 100); // 中心(200, 200),宽50,高100// 绘制文本QRectF rectF(0, 0, 200, 100); // 文本区域painter.drawText(rectF, Qt::AlignHCenter, "正点原子"); // 水平居中对齐// 绘制路径QPainterPath path;path.moveTo(20, 80); // 移动到起点path.lineTo(20, 30); // 添加直线path.cubicTo(80, 0, 50, 50, 80, 80); // 添加三次贝塞尔曲线painter.drawPath(path); // 绘制路径
}
4.3 主函数 (main.cpp)
#include "widget.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
4.4 项目文件 (45.pro)
QT += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \widget.cppHEADERS += \widget.hFORMS += \widget.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
5. 高级绘图技术
5.1 双缓冲绘图
双缓冲绘图可以避免闪烁,特别是在复杂绘图或动画中。
void Widget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);// 创建一个与窗口大小相同的QPixmapQPixmap pixmap(size());pixmap.fill(Qt::white); // 填充白色背景// 在QPixmap上绘图QPainter painter(&pixmap);painter.setRenderHint(QPainter::Antialiasing);// 执行绘图操作...painter.drawRect(100, 100, 200, 150);painter.drawEllipse(150, 150, 100, 100);// 结束在QPixmap上的绘图painter.end();// 将QPixmap绘制到窗口上QPainter widgetPainter(this);widgetPainter.drawPixmap(0, 0, pixmap);
}
5.2 渐变填充
void Widget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);// 线性渐变QLinearGradient linearGradient(0, 0, 400, 0); // 水平渐变linearGradient.setColorAt(0, Qt::red); // 起点颜色linearGradient.setColorAt(0.5, Qt::yellow); // 中点颜色linearGradient.setColorAt(1, Qt::blue); // 终点颜色painter.setBrush(QBrush(linearGradient));painter.drawRect(50, 50, 400, 100);// 辐射渐变QRadialGradient radialGradient(300, 250, 100); // 中心(300, 250),半径100radialGradient.setColorAt(0, Qt::white); // 中心颜色radialGradient.setColorAt(1, Qt::green); // 边缘颜色painter.setBrush(QBrush(radialGradient));painter.drawEllipse(200, 150, 200, 200);// 锥形渐变QConicalGradient conicalGradient(500, 250, 0); // 中心(500, 250),起始角度0conicalGradient.setColorAt(0, Qt::red);conicalGradient.setColorAt(0.25, Qt::yellow);conicalGradient.setColorAt(0.5, Qt::green);conicalGradient.setColorAt(0.75, Qt::blue);conicalGradient.setColorAt(1, Qt::red);painter.setBrush(QBrush(conicalGradient));painter.drawEllipse(400, 150, 200, 200);
}
5.3 图像效果
void Widget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);QPainter painter(this);// 加载图像QImage image("example.png");// 应用不透明度painter.setOpacity(0.5); // 50%不透明度painter.drawImage(10, 10, image);// 恢复完全不透明painter.setOpacity(1.0);// 应用组合模式painter.setCompositionMode(QPainter::CompositionMode_Multiply);painter.drawImage(220, 10, image);// 恢复默认组合模式painter.setCompositionMode(QPainter::CompositionMode_SourceOver);// 应用图像变换QImage transformed = image.scaled(200, 150, Qt::KeepAspectRatio, Qt::SmoothTransformation);painter.drawImage(430, 10, transformed);
}
5.4 剪切区域
void Widget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);// 创建剪切路径QPainterPath clipPath;clipPath.addEllipse(100, 100, 200, 200); // 椭圆形剪切区域// 应用剪切路径painter.setClipPath(clipPath);// 在剪切区域内绘图// 只有在椭圆内的部分会被显示painter.fillRect(50, 50, 300, 300, Qt::blue);// 重置剪切区域painter.setClipping(false);// 在整个窗口绘图painter.setPen(QPen(Qt::red, 2));painter.drawRect(50, 50, 300, 300);
}
6. 动画绘图
6.1 使用定时器实现动画
// 在widget.h中添加
private:QTimer *timer;int angle;private slots:void updateAnimation();// 在widget.cpp的构造函数中添加
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), angle(0)
{ui->setupUi(this);// 创建定时器timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &Widget::updateAnimation);timer->start(50); // 每50毫秒更新一次
}// 添加更新函数
void Widget::updateAnimation()
{angle = (angle + 5) % 360; // 增加角度,并保持在0-359范围内update(); // 触发重绘
}// 修改paintEvent
void Widget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);// 移动到窗口中心painter.translate(width() / 2, height() / 2);// 旋转坐标系painter.rotate(angle);// 绘制旋转的矩形painter.setPen(QPen(Qt::blue, 2));painter.drawRect(-50, -50, 100, 100);
}
6.2 使用QPropertyAnimation实现动画
// 在widget.h中添加
#include <QPropertyAnimation>private:QPropertyAnimation *animation;QRect rectPosition;// 在widget.cpp的构造函数中添加
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), rectPosition(50, 50, 100, 100)
{ui->setupUi(this);// 创建属性动画animation = new QPropertyAnimation(this, "rectPosition");animation->setDuration(2000); // 2秒animation->setStartValue(QRect(50, 50, 100, 100));animation->setEndValue(QRect(width() - 150, height() - 150, 100, 100));animation->setEasingCurve(QEasingCurve::OutBounce); // 弹跳效果animation->setLoopCount(-1); // 无限循环animation->start();
}// 添加属性访问器
QRect Widget::getRectPosition() const
{return rectPosition;
}void Widget::setRectPosition(const QRect &rect)
{rectPosition = rect;update(); // 触发重绘
}// 修改paintEvent
void Widget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);// 绘制动画矩形painter.setPen(QPen(Qt::blue, 2));painter.setBrush(QBrush(Qt::yellow));painter.drawRect(rectPosition);
}
7. 性能优化技巧
7.1 减少重绘区域
// 只更新需要重绘的区域,而不是整个窗口
void Widget::updateSpecificArea()
{// 只更新矩形区域update(100, 100, 200, 200);// 或者使用QRegion更新复杂区域QRegion region(100, 100, 100, 100, QRegion::Ellipse);update(region);
}
7.2 使用缓存
// 在widget.h中添加
private:QPixmap cachedBackground;bool backgroundDirty;// 在构造函数中初始化
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), backgroundDirty(true)
{ui->setupUi(this);
}// 在resizeEvent中处理缓存
void Widget::resizeEvent(QResizeEvent *event)
{Q_UNUSED(event);backgroundDirty = true; // 窗口大小改变时,标记缓存为脏
}// 在paintEvent中使用缓存
void Widget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);QPainter painter(this);// 如果缓存为脏或不存在,重新创建if (backgroundDirty || cachedBackground.isNull()) {cachedBackground = QPixmap(size());cachedBackground.fill(Qt::white);QPainter cachePainter(&cachedBackground);cachePainter.setRenderHint(QPainter::Antialiasing);// 绘制复杂的静态背景drawComplexBackground(&cachePainter);backgroundDirty = false;}// 绘制缓存的背景painter.drawPixmap(0, 0, cachedBackground);// 绘制动态内容drawDynamicContent(&painter);
}
7.3 避免不必要的绘图操作
void Widget::paintEvent(QPaintEvent *event)
{// 获取需要重绘的区域QRegion region = event->region();QPainter painter(this);// 只有当区域包含特定部分时才绘制if (region.intersects(QRect(100, 100, 200, 200))) {// 绘制第一部分drawFirstPart(&painter);}if (region.intersects(QRect(300, 300, 200, 200))) {// 绘制第二部分drawSecondPart(&painter);}
}
8.3 绘图质量问题
问题:绘制的图形边缘锯齿明显。
解决方案:
- 启用抗锯齿:
painter.setRenderHint(QPainter::Antialiasing);
- 使用浮点精度的坐标(QPointF、QRectF等)
- 对于文本,启用文本抗锯齿:
painter.setRenderHint(QPainter::TextAntialiasing);