目录
5.2、在 paintEvent() 中使用 QPainter
一. 窗口和屏幕管理
请跳转章节,此处不再重复:QtGUI模块功能详细说明,窗口和屏幕管理(一)
二. 绘图和渲染
1、基础概念与核心
1.1、2D 绘图与渲染概述
2D 绘图是指在二维平面(通常以像素为单位)上绘制图形、图像和文本的过程。Qt 的 2D 绘图系统以 QPainter 为核心,结合 QPaintDevice 及其派生类(如 QWidget、QImage、QPixmap)实现灵活的绘图功能。
渲染流程
-
选择绘图设备:确定绘图目标(如 QWidget、QImage、QPixmap)。
-
初始化 QPainter:创建 QPainter 对象并绑定到绘图设备。
-
执行绘制操作:调用 QPainter 的方法绘制点、线、形状、图像或文本。
-
结束绘制:完成绘制并释放资源。
-
显示或保存:将结果显示在屏幕上或保存为文件。
1.2、核心绘图引擎:QPainter
QPainter 是 Qt 2D 绘图的核心类,负责执行所有绘制操作。它通过与 QPaintDevice 交互,将图形命令渲染到目标设备上。
1.2.1、QPainter 的生命周期
-
初始化:QPainter 必须绑定到一个 QPaintDevice 才能工作。
-
通过调用 begin(QPaintDevice*) 绑定。
-
QPainter 对象也可以在构造时直接绑定设备。
-
-
绘制:在 begin() 和 end() 之间调用绘制方法。
-
结束:调用 end() 释放资源,完成绘制。
-
构造时绑定设备 → 隐式调用
begin()→ 绘制内容 → 析构时隐式调用end()。
1.2.2、基本用法
-
创建 QPainter:手动创建并绑定、构造函数绑定。
-
设置画笔和画刷:
-
画笔(QPen):控制线条的颜色、宽度、样式(如实线、虚线)。
-
画刷(QBrush):控制填充的颜色、样式(如纯色、渐变、图案)。
-
-
启用抗锯齿:减少或消除图形边缘出现的锯齿状阶梯现象。
1.2.3、绘制基本图形
-
点 (Point):使用 drawPoint() 方法绘制单个点。
-
线 (Line):使用 drawLine() 方法绘制两点之间的直线段。
-
折线 (Polyline):使用 drawPolyline() 方法绘制由多个连接的线段组成的折线。
-
矩形 (Rectangle):使用 drawRect() 方法绘制矩形。可以指定左上角坐标和宽高,或指定两个对角点。
-
绘制圆角矩形 (Rounded Rectangle),使用 drawRoundedRect()。
-
椭圆 (Ellipse):使用 drawEllipse() 方法绘制椭圆。通常指定外切矩形来定义椭圆的位置和大小。
-
圆弧 (Arc):使用 drawArc() 方法绘制椭圆的一部分,即圆弧。需要指定外切矩形、起始角度和跨越角度。
-
弦 (Chord):使用 drawChord() 方法绘制椭圆的一个部分,由圆弧及其两个端点连成的直线(弦)围成。需要指定外切矩形、起始角度和跨越角度。
-
扇形 (Pie):使用 drawPie() 方法绘制椭圆的一个扇形区域,由圆心到圆弧两个端点的半径和圆弧围成。需要指定外切矩形、起始角度和跨越角度。
-
多边形 (Polygon):使用 drawPolygon() 方法绘制封闭的多边形。需要提供多边形各个顶点的坐标。
-
路径 (Path):使用 drawPath() 方法绘制由 QPainterPath 对象定义的任意复杂图形。包含直线、曲线(贝塞尔曲线)、圆弧等多种元素,并且可以组合它们来构建复杂的形状。
1.2.4、绘制图像
-
QImage:使用drawImage(),适合像素级操作,CPU 渲染,常用于图像处理。
-
QPixmap:使用drawPixmap(),优化为屏幕显示,GPU 加速,适合 GUI 渲染。
1.2.4、绘制文本
-
绘制简单文本:使用drawText()绘制文本,使用setFont()设置字体。
-
文本对齐与排版:使用drawText()绘制文本,使用QRect现在对齐,对齐方式包括
-
Qt::AlignLeft
-
Qt::AlignRight
-
Qt::AlignTop
-
Qt::AlignBottom
-
1.2.5、保存与恢复绘图状态
QPainter 支持状态栈,用于保存和恢复绘图设置(如画笔、画刷、变换矩阵等)
-
保存状态:save() 将当前状态压入栈。
-
恢复状态:restore() 恢复最近保存的状态。
-
注:save() 和 restore() 必须配对使用,避免栈溢出或状态丢失。
1.2.6、示例代码
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QColor>
#include <QFont>
#include <QPixmap>
#include <QImage>
#include <QPainterPath>
#include <QPolygon>
#include <QRect>
#include <QPoint>
#include <QPointF>
#include <QPaintEvent>
class DrawingWidget : public QWidget
{
Q_OBJECT
public:
DrawingWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
setWindowTitle("QPainter 核心功能示例 (C++)");
setFixedSize(800, 600);
// 创建一个简单的 QImage 和 QPixmap 用于演示
sampleImage = QImage(100, 50, QImage::Format_RGB32);
sampleImage.fill(Qt::blue); // 填充蓝色
samplePixmap = QPixmap(100, 50);
samplePixmap.fill(Qt::green); // 填充绿色
}
~DrawingWidget() override = default;
protected:
// 重写 paintEvent 方法
void paintEvent(QPaintEvent *event) override
{
Q_UNUSED(event); // 避免编译器警告,event 在此例中未使用
//===============================================================
// 1.2.1、QPainter 的生命周期 - 在 paintEvent 中自动处理 begin/end
//===============================================================
// QPainter 对象在构造时直接绑定设备 (this 指向 DrawingWidget)
QPainter painter(this);
//===============================================================
// 1.2.2、基本用法 - 设置画笔、画刷、启用抗锯齿
//===============================================================
// 启用抗锯齿
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::SmoothPixmapTransform, true); // 平滑缩放图片
// 设置默认画笔 (黑色,宽度2)
QPen defaultPen(Qt::black, 2, Qt::SolidLine);
painter.setPen(defaultPen);
// 设置默认画刷 (无填充)
QBrush defaultBrush(Qt::NoBrush);
painter.setBrush(defaultBrush);
int y_offset = 20; // 起始绘制的 Y 坐标
//===============================================================
// --- 绘制基本图形 ---
//===============================================================
// 1.2.3、绘制基本图形 - 点
painter.setPen(QPen(Qt::red, 5, Qt::SolidLine, Qt::RoundCap)); // 粗点
painter.drawPoint(50, y_offset);
painter.setFont(QFont("Arial", 8));
painter.setPen(Qt::black);
painter.drawText(60, y_offset + 5, "drawPoint");
y_offset += 20;
// 1.2.3、绘制基本图形 - 线
painter.setPen(QPen(Qt::blue, 2, Qt::SolidLine));
painter.drawLine(50, y_offset, 150, y_offset + 30);
painter.drawText(160, y_offset + 30, "drawLine");
y_offset += 40;
// 1.2.3、绘制基本图形 - 折线
painter.setPen(QPen(Qt::darkGreen, 2, Qt::DashLine));
QVector<QPoint> polylinePoints; // 使用QVector存储点
polylinePoints << QPoint(50, y_offset) << QPoint(100, y_offset + 20) << QPoint(50, y_offset + 40) << QPoint(100, y_offset + 60);
painter.drawPolyline(QPolygon(polylinePoints));
painter.setPen(Qt::black);
painter.drawText(110, y_offset + 60, "drawPolyline");
y_offset += 70;
// 1.2.3、绘制基本图形 - 矩形 (实心填充)
painter.setPen(QPen(Qt::black, 1));
painter.setBrush(QBrush(Qt::yellow));
painter.drawRect(50, y_offset, 80, 50);
painter.setBrush(defaultBrush); // 恢复无填充
painter.setPen(Qt::black);
painter.drawText(140, y_offset + 30, "drawRect (Filled)");
y_offset += 60;
// 1.2.3、绘制基本图形 - 圆角矩形 (轮廓)
painter.setPen(QPen(Qt::magenta, 2));
painter.drawRoundedRect(50, y_offset, 80, 50, 15, 15); // x, y, w, h, xRadius, yRadius
painter.setPen(Qt::black);
painter.drawText(140, y_offset + 30, "drawRoundedRect");
y_offset += 60;
// 1.2.3、绘制基本图形 - 椭圆 (实心填充)
painter.setPen(QPen(Qt::black, 1));
painter.setBrush(QBrush(Qt::cyan));
painter.drawEllipse(50, y_offset, 80, 50);
painter.setBrush(defaultBrush);
painter.setPen(Qt::black);
painter.drawText(140, y_offset + 30, "drawEllipse (Filled)");
y_offset += 60;
// 1.2.3、绘制基本图形 - 圆弧, 弦, 扇形 (基于同一个椭圆)
QRect arcRect(50, y_offset, 100, 100);
painter.setPen(QPen(Qt::red, 2));
painter.drawArc(arcRect, 0 * 16, 90 * 16); // 从0度开始,跨越90度 (Qt角度单位为1/16度)
painter.setPen(Qt::black);
painter.drawText(160, y_offset + 20, "drawArc");
painter.setPen(QPen(Qt::blue, 2));
painter.drawChord(arcRect, 90 * 16, 90 * 16); // 从90度开始,跨越90度
painter.setPen(Qt::black);
painter.drawText(160, y_offset + 50, "drawChord");
painter.setPen(QPen(Qt::darkYellow, 2));
painter.setBrush(QBrush(Qt::lightGray));
painter.drawPie(arcRect, 180 * 16, 90 * 16); // 从180度开始,跨越90度
painter.setBrush(defaultBrush);
painter.setPen(Qt::black);
painter.drawText(160, y_offset + 80, "drawPie (Filled)");
y_offset += 110;
// 1.2.3、绘制基本图形 - 多边形 (实心填充)
painter.setPen(QPen(Qt::darkMagenta, 2));
painter.setBrush(QBrush(Qt::darkCyan));
QVector<QPoint> polygonPoints;
polygonPoints << QPoint(50, y_offset) << QPoint(80, y_offset + 40) << QPoint(20, y_offset + 40);
painter.drawPolygon(QPolygon(polygonPoints));
painter.setBrush(defaultBrush);
painter.setPen(Qt::black);
painter.drawText(90, y_offset + 20, "drawPolygon (Filled)");
y_offset += 50;
// 1.2.3、绘制基本图形 - 路径 (QPainterPath)
painter.setPen(QPen(Qt::darkRed, 2));
painter.setBrush(QBrush(Qt::yellow));
QPainterPath path;
path.moveTo(200, 50);
path.cubicTo(250, 10, 300, 90, 350, 50); // 立方贝塞尔曲线
path.addEllipse(300, 60, 50, 30); // 添加一个椭圆到路径
painter.drawPath(path); // 绘制并填充路径
painter.setBrush(defaultBrush);
painter.setPen(Qt::black);
painter.drawText(360, 50, "drawPath (Cubic Bezier + Ellipse)");
//===============================================================
// --- 绘制图像 ---
//===============================================================
int y_offset_image = 160;
int x_offset_image = 200;
// 1.2.4、绘制图像 - QImage
painter.drawImage(x_offset_image, y_offset_image, sampleImage);
painter.drawText(x_offset_image + sampleImage.width() + 10, y_offset_image + 20, "drawImage (QImage)");
y_offset_image += sampleImage.height() + 20;
// 1.2.4、绘制图像 - QPixmap
painter.drawPixmap(x_offset_image, y_offset_image, samplePixmap);
painter.drawText(x_offset_image + samplePixmap.width() + 10, y_offset_image + 20, "drawPixmap (QPixmap)");
y_offset_image += samplePixmap.height() + 20;
// --- 绘制文本 ---
int y_offset_text = 300;
int x_offset_text = 200;
// 1.2.4、绘制文本 - 简单文本
painter.setPen(Qt::black);
painter.setFont(QFont("Times", 12, QFont::Bold));
painter.drawText(x_offset_text, y_offset_text, "Hello QPainter Text!");
y_offset_text += 30;
// 1.2.4、绘制文本 - 文本对齐与排版
QRect alignRect(x_offset_text, y_offset_text, 200, 50);
painter.setPen(QPen(Qt::gray, 1, Qt::DashLine));
painter.drawRect(alignRect); // 绘制对齐区域的边框
painter.setPen(Qt::darkBlue);
painter.setFont(QFont("Courier New", 10));
// 在矩形内右对齐+垂直居中
painter.drawText(alignRect, Qt::AlignVCenter | Qt::AlignRight, "Aligned Text\n(Right & VCenter)");
y_offset_text += 60;
//===============================================================
// --- 保存与恢复绘图状态 ---
//===============================================================
int y_offset_state = 400;
int x_offset_state = 200;
painter.setPen(QPen(Qt::black, 1));
painter.drawText(x_offset_state, y_offset_state, "Before save(): Black Pen");
// 1.2.5、保存状态
painter.save();
// 修改状态
painter.setPen(QPen(Qt::darkCyan, 4, Qt::DotLine));
painter.drawText(x_offset_state, y_offset_state + 20, "After save(), changed Pen: Dark Cyan Dot Line");
// 1.2.5、恢复状态
painter.restore();
painter.drawText(x_offset_state, y_offset_state + 40, "After restore(): Black Pen is back");
// 绘制一个形状来确认画笔确实恢复了
painter.drawRect(x_offset_state, y_offset_state + 50, 50, 30);
// QPainter 的析构函数会自动调用 end(),所以在 paintEvent 结束时不需要手动调用。
// 如果您在 paintEvent 之外手动调用了 painter.begin(...),则需要手动调用 painter.end()。
}
private:
QImage sampleImage;
QPixmap samplePixmap;
};
// 主函数
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
DrawingWidget mainWindow;
mainWindow.show();
return app.exec();
}
#include "main.moc" // 包含 moc 文件,用于处理 Q_OBJECT 宏生成的代码
1.3、绘图设备:QPaintDevice
QPaintDevice 是 Qt 绘图系统的抽象基类,表示可以被 QPainter 绘制的目标。
派生类包括:
-
QImage:光栅图像,适合像素级操作。
-
QPixmap:优化为屏幕显示,适合 GUI。
-
QPicture:记录绘图命令,可回放。
-
QPrinter:用于打印输出。
-
QOpenGLPaintDevice:支持 OpenGL 渲染。
-
QWidget(间接支持):通过 paintEvent 使用 QPainter 绘制。
示例代码
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QImage>
#include <QPixmap>
#include <QPicture>
#include <QPrinter>
#include <QPushButton>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QMessageBox>
#include <QPrintDialog>
// 自定义窗口类,用于测试 QPaintDevice 的各种派生类
class PaintDeviceWidget : public QWidget {
public:
PaintDeviceWidget(QWidget *parent = nullptr) : QWidget(parent) {
// 设置窗口大小
setFixedSize(400, 500);
// 创建按钮用于触发保存和打印
QPushButton *saveImageButton = new QPushButton("Save as Image", this);
QPushButton *savePixmapButton = new QPushButton("Save as Pixmap", this);
QPushButton *savePictureButton = new QPushButton("Save as Picture", this);
QPushButton *printButton = new QPushButton("Print", this);
// 布局
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(saveImageButton);
layout->addWidget(savePixmapButton);
layout->addWidget(savePictureButton);
layout->addWidget(printButton);
layout->addStretch();
setLayout(layout);
// 连接按钮信号,保存时注意填写后缀
connect(saveImageButton, &QPushButton::clicked, this, &PaintDeviceWidget::saveToImage);
connect(savePixmapButton, &QPushButton::clicked, this, &PaintDeviceWidget::saveToPixmap);
connect(savePictureButton, &QPushButton::clicked, this, &PaintDeviceWidget::saveToPicture);
connect(printButton, &QPushButton::clicked, this, &PaintDeviceWidget::print);
}
protected:
// 重写 paintEvent,在 QWidget 上绘制
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
drawCommonGraphics(&painter, "Drawing on QWidget");
}
private:
// 通用的绘制函数,适用于所有 QPaintDevice
void drawCommonGraphics(QPainter *painter, const QString &title) {
// 设置画笔和画刷
painter->setPen(QPen(Qt::blue, 2));
painter->setBrush(QBrush(Qt::yellow));
// 绘制矩形
painter->drawRect(50, 50, 100, 80);
// 绘制椭圆
painter->setPen(QPen(Qt::red, 3));
painter->setBrush(QBrush(Qt::green));
painter->drawEllipse(50, 150, 100, 80);
// 绘制文本
painter->setFont(QFont("Arial", 12));
painter->setPen(Qt::black);
painter->drawText(50, 250, title);
}
// 在 QImage 上绘制并保存,保存时注意填写后缀
void saveToImage() {
QImage image(200, 300, QImage::Format_ARGB32);
image.fill(Qt::white); // 设置背景色
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
drawCommonGraphics(&painter, "Drawing on QImage");
QString fileName = QFileDialog::getSaveFileName(this, "Save Image", "", "PNG Files (*.png)");
if (!fileName.isEmpty()) {
if (image.save(fileName)) {
QMessageBox::information(this, "Success", "Image saved successfully!");
} else {
QMessageBox::warning(this, "Error", "Failed to save image!");
}
}
}
// 在 QPixmap 上绘制并保存,保存时注意填写后缀
void saveToPixmap() {
QPixmap pixmap(200, 300);
pixmap.fill(Qt::white); // 设置背景色
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::Antialiasing);
drawCommonGraphics(&painter, "Drawing on QPixmap");
QString fileName = QFileDialog::getSaveFileName(this, "Save Pixmap", "", "PNG Files (*.png)");
if (!fileName.isEmpty()) {
if (pixmap.save(fileName)) {
QMessageBox::information(this, "Success", "Pixmap saved successfully!");
} else {
QMessageBox::warning(this, "Error", "Failed to save pixmap!");
}
}
}
// 在 QPicture 上绘制并保存,保存时注意填写后缀
void saveToPicture() {
QPicture picture;
QPainter painter(&picture);
painter.setRenderHint(QPainter::Antialiasing);
drawCommonGraphics(&painter, "Drawing on QPicture");
painter.end(); // QPicture 需要显式结束
QString fileName = QFileDialog::getSaveFileName(this, "Save Picture", "", "Picture Files (*.pic)");
if (!fileName.isEmpty()) {
if (picture.save(fileName)) {
QMessageBox::information(this, "Success", "Picture saved successfully!");
} else {
QMessageBox::warning(this, "Error", "Failed to save picture!");
}
}
}
// 在 QPrinter 上绘制并打印
void print() {
QPrinter printer;
QPrintDialog dialog(&printer, this);
if (dialog.exec() == QDialog::Accepted) {
QPainter painter(&printer);
painter.setRenderHint(QPainter::Antialiasing);
drawCommonGraphics(&painter, "Drawing on QPrinter");
}
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
PaintDeviceWidget widget;
widget.show();
return app.exec();
}
2、样式与填充
2.1、线条样式:QPen
QPen 是 Qt 中用于绘制线条样式的类,适用于路径、形状的轮廓等。它可以配置颜色、宽度、风格、线帽和连接方式。
-
创建与配置 QPen:可以通过构造函数或 setter 方法配置。
-
设置线条颜色setColor():颜色可以通过 QColor 或预定义的 Qt::GlobalColor 设置。
-
设置线条宽度setWidth():宽度以像素为单位,整数或浮点数。宽度为 0 表示“化妆笔”,宽度不随缩放变化。
-
设置线条风格setStyle(),通过 Qt::PenStyle 枚举设置:
-
Qt::SolidLine:实线
-
Qt::DashLine:虚线
-
Qt::DotLine:点线
-
Qt::DashDotLine:点划线
-
Qt::DashDotDotLine:点划点线
-
Qt::CustomDashLine:自定义虚线
-
-
设置线帽风格setCapStyle(),线帽风格定义线条端点的形状,通过 Qt::PenCapStyle 枚举设置:,通过 Qt::PenCapStyle 枚举设置:
-
Qt::SquareCap:方形端点(延伸半个线宽)
-
Qt::RoundCap:圆形端点
-
Qt::FlatCap:平端点(不延伸)
-
-
设置连接风格setJionStyle(),连接风格定义多段线连接处的形状,通过 Qt::PenJoinStyle 枚举设置:
-
Qt::MiterJoin:尖角连接
-
Qt::RoundJoin:圆角连接
-
Qt::BevelJoin:斜角连接
-
-
使用 QPen 绘制路径和形状:与 QPainter 结合使用,设置后用于绘制线条或形状的轮廓。
-
示例代码
#include <QApplication> #include <QWidget> #include <QPainter> #include <QPen> #include <QColor> #include <QRect> #include <QPaintEvent> // 定义一个自定义的QWidget类,用于演示QPen的使用 class QPenExampleWidget : public QWidget { Q_OBJECT // 需要Q_OBJECT宏来支持信号和槽,虽然这个例子没有使用信号槽 public: QPenExampleWidget(QWidget *parent = nullptr) : QWidget(parent) { setWindowTitle("QPen 示例"); // 设置窗口标题 setMinimumSize(400, 350); // 设置窗口最小大小 } protected: // 重写paintEvent函数,在这里进行绘图操作 void paintEvent(QPaintEvent *event) override { Q_UNUSED(event); // 标记事件参数未使用,避免编译器警告 QPainter painter(this); // 创建一个QPainter对象,用于在当前widget上绘图 painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿,使线条更平滑 int startX = 10; int startY = 20; int lineLength = 90; int shapeWidth = 90; int shapeHeight = 30; int spacing = 50; // 1. 默认画笔 (通常是黑色、实线、1像素宽、方头、斜角连接) painter.setPen(QPen()); painter.drawText(startX, startY, "默认画笔:"); painter.drawLine(startX, startY + 10, startX + lineLength, startY + 10); // 2. 设置特定颜色和宽度的画笔 QPen redPen(Qt::red); // 创建红色画笔 redPen.setWidth(3); // 设置线宽为3像素 painter.setPen(redPen); painter.drawText(startX, startY + spacing, "红色画笔 (宽度 3):"); painter.drawRect(startX, startY + spacing + 10, shapeWidth, shapeHeight); // 绘制矩形 // 3. 设置浮点数宽度和不同颜色的画笔 QPen bluePen(QColor(0, 0, 255)); // 使用RGB值创建蓝色画笔 bluePen.setWidthF(2.5); // 设置线宽为2.5像素(浮点数) painter.setPen(bluePen); painter.drawText(startX, startY + 2 * spacing, "蓝色画笔 (宽度 2.5 浮点数):"); painter.drawEllipse(startX, startY + 2 * spacing + 10, shapeWidth, shapeHeight); // 绘制椭圆 // 4. 设置不同线条风格的画笔 (虚线, 点线, 点划线) int styleStartX = 150; painter.drawText(styleStartX, startY, "不同风格画笔:"); QPen dashPen(Qt::black, 2, Qt::DashLine); // 创建黑色、2像素宽、虚线画笔 painter.setPen(dashPen); painter.drawText(styleStartX, startY + 10, "虚线:"); painter.drawLine(styleStartX + 50, startY + 10, styleStartX + 50 + lineLength, startY + 10); QPen dotPen(Qt::black, 2, Qt::DotLine); // 创建黑色、2像素宽、点线画笔 painter.setPen(dotPen); painter.drawText(styleStartX, startY + spacing - 10, "点线:"); painter.drawLine(styleStartX + 50, startY + spacing - 10, styleStartX + 50 + lineLength, startY + spacing - 10); QPen dashDotPen(Qt::black, 2, Qt::DashDotLine); // 创建黑色、2像素宽、点划线画笔 painter.setPen(dashDotPen); painter.drawText(styleStartX, startY + 2 * spacing - 20, "点划线:"); painter.drawLine(styleStartX + 50, startY + 2 * spacing - 20, styleStartX + 50 + lineLength, startY + 2 * spacing - 20); // 5. 设置不同线帽风格 (Cap Style) int capStartX = 10; int capStartY = startY + 3 * spacing; int capLineLength = 90; int capSpacing = 25; int capPenWidth = 8; painter.drawText(capStartX, capStartY, "线帽风格 (宽度为8以示区别):"); QPen squareCapPen(Qt::darkMagenta, capPenWidth, Qt::SolidLine, Qt::SquareCap); // 方形线帽 painter.setPen(squareCapPen); painter.drawText(capStartX, capStartY + 10, "SquareCap (方形帽):"); painter.drawLine(capStartX, capStartY + 20, capStartX + capLineLength, capStartY + 20); QPen roundCapPen(Qt::darkMagenta, capPenWidth, Qt::SolidLine, Qt::RoundCap); // 圆形线帽 painter.setPen(roundCapPen); painter.drawText(capStartX, capStartY + 10 + capSpacing, "RoundCap (圆形帽):"); painter.drawLine(capStartX, capStartY + 20 + capSpacing, capStartX + capLineLength, capStartY + 20 + capSpacing); QPen flatCapPen(Qt::darkMagenta, capPenWidth, Qt::SolidLine, Qt::FlatCap); // 平坦线帽 painter.setPen(flatCapPen); painter.drawText(capStartX, capStartY + 10 + 2*capSpacing, "FlatCap (平坦帽):"); painter.drawLine(capStartX, capStartY + 20 + 2*capSpacing, capStartX + capLineLength, capStartY + 20 + 2*capSpacing); // 6. 设置不同连接风格 (Join Style) - 需要绘制连接的多段线才能看出来 int joinStartX = 150; int joinStartY = startY + 3 * spacing; int joinPenWidth = 5; // 使用较粗的线条更明显 int joinSpacing = 60; painter.drawText(joinStartX, joinStartY, "连接风格 (使用折线展示):"); // 定义一个V字形的多段线点 QPoint pointsMiter[] = {QPoint(joinStartX, joinStartY + 30), QPoint(joinStartX + 20, joinStartY + 10), QPoint(joinStartX + 40, joinStartY + 30)}; QPen miterJoinPen(Qt::darkCyan, joinPenWidth, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin); // 斜角连接 painter.setPen(miterJoinPen); painter.drawText(joinStartX, joinStartY + 40, "MiterJoin (斜角连接):"); painter.drawPolyline(pointsMiter, 3); QPoint pointsRound[] = {QPoint(joinStartX + joinSpacing, joinStartY + 30), QPoint(joinStartX + joinSpacing + 20, joinStartY + 10), QPoint(joinStartX + joinSpacing + 40, joinStartY + 30)}; QPen roundJoinPen(Qt::darkCyan, joinPenWidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin); // 圆角连接 painter.setPen(roundJoinPen); painter.drawText(joinStartX + joinSpacing, joinStartY + 40, "RoundJoin (圆角连接):"); painter.drawPolyline(pointsRound, 3); QPoint pointsBevel[] = {QPoint(joinStartX + 2*joinSpacing, joinStartY + 30), QPoint(joinStartX + 2*joinSpacing + 20, joinStartY + 10), QPoint(joinStartX + 2*joinSpacing + 40, joinStartY + 30)}; QPen bevelJoinPen(Qt::darkCyan, joinPenWidth, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin); // 斜面连接 painter.setPen(bevelJoinPen); painter.drawText(joinStartX + 2*joinSpacing, joinStartY + 40, "BevelJoin (斜面连接):"); painter.drawPolyline(pointsBevel, 3); // 7. 综合应用各种风格的画笔 QPen customPen(Qt::darkGreen, 4, Qt::DashDotDotLine, Qt::RoundCap, Qt::BevelJoin); // 创建一个自定义画笔 painter.setPen(customPen); painter.drawText(startX, startY + 6 * spacing, "综合画笔示例:"); painter.drawRect(startX, startY + 6 * spacing + 10, shapeWidth * 2, shapeHeight); } }; // main函数,程序的入口 int main(int argc, char *argv[]) { QApplication a(argc, argv); // 创建QApplication对象 QPenExampleWidget w; // 创建自定义的窗口widget w.show(); // 显示窗口 return a.exec(); // 启动应用程序事件循环 } // 需要moc处理,否则无法编译,因为使用了Q_OBJECT宏 #include "main.moc"
2.2、填充样式:QBrush
QBrush 用于定义填充样式,适用于闭合形状(如矩形、椭圆)或路径的内部填充。它支持纯色、渐变、图案和纹理。
-
创建与配置 QBrush:可以通过构造函数或 setter 方法配置。
-
设置填充颜色setColor():填充颜色可以通过 QColor 或 Qt::GlobalColor 设置。
-
设置填充风格setStyle():通过 Qt::BrushStyle 枚举设置:
-
Qt::NoBrush:无填充
-
Qt::SolidPattern:纯色填充
-
Qt::Dense1Pattern ~ Qt::Dense7Pattern:不同密度的点图案
-
Qt::HorPattern:水平线图案
-
Qt::VerPattern:垂直线图案
-
Qt::CrossPattern:交叉线图案
-
Qt::BDiagPattern:反斜线图案
-
Qt::FDiagPattern:正斜线图案
-
Qt::DiagCrossPattern:斜交叉图案
-
Qt::LinearGradientPattern:线性渐变
-
Qt::RadialGradientPattern:径向渐变
-
Qt::ConicalGradientPattern:圆锥渐变
-
Qt::TexturePattern:纹理填充
-
-
使用 QBrush 填充路径和形状:与 QPainter 结合,用于填充形状或路径。
-
示例代码
#include <QApplication> #include <QWidget> #include <QPainter> #include <QBrush> #include <QColor> #include <QRect> #include <QPaintEvent> class QBrushExampleWidget : public QWidget { Q_OBJECT // 需要Q_OBJECT宏来支持信号和槽等元对象特性 public: QBrushExampleWidget(QWidget *parent = nullptr) : QWidget(parent) { setWindowTitle("QBrush 示例 (整合)"); setMinimumSize(500, 400); } protected: // 重写paintEvent函数,在这里进行绘图操作 void paintEvent(QPaintEvent *event) override { Q_UNUSED(event); // 标记事件参数未使用,避免编译器警告 QPainter painter(this); // 创建一个QPainter对象,用于在当前widget上绘图 painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿,使形状边缘更平滑 // 这里使用一个细的黑色边框,这样填充区域和背景有清晰界限 QPen thinBlackPen(Qt::black, 1); painter.setPen(thinBlackPen); int startX = 10; int startY = 20; int shapeSize = 60; int spacing = 80; int rowSpacing = 120; // 1. 默认画刷 (通常是 Qt::NoBrush,即无填充) painter.drawText(startX, startY, "默认画刷 (无填充):"); painter.setBrush(QBrush()); // 设置默认画刷 (或使用 QBrush(Qt::NoBrush) ) painter.drawRect(startX, startY + 10, shapeSize, shapeSize); // 2. 纯色填充 (Qt::SolidPattern) QBrush solidBrush(Qt::green, Qt::SolidPattern); // 创建一个绿色纯色画刷 painter.drawText(startX + shapeSize + spacing, startY, "纯色填充 (绿色):"); painter.setBrush(solidBrush); // 设置当前画刷 painter.drawRect(startX + shapeSize + spacing, startY + 10, shapeSize, shapeSize); // 绘制填充矩形 QBrush yellowBrush(QColor(255, 255, 0)); // 使用RGB值创建一个黄色纯色画刷 painter.drawText(startX + 2 * (shapeSize + spacing), startY, "纯色填充 (黄色):"); painter.setBrush(yellowBrush); painter.drawEllipse(startX + 2 * (shapeSize + spacing), startY + 10, shapeSize, shapeSize); // 绘制填充椭圆 // 3. 不同密度的点图案 (Dense Patterns) int patternY = startY + rowSpacing; painter.drawText(startX, patternY, "点图案填充:"); QBrush dense1Brush(Qt::blue, Qt::Dense1Pattern); // 蓝色密集点图案 painter.setBrush(dense1Brush); painter.drawText(startX, patternY + 10, "Dense1Pattern (最密集):"); painter.drawRect(startX, patternY + 20, shapeSize, shapeSize); QBrush dense3Brush(Qt::red, Qt::Dense3Pattern); // 红色中等密度点图案 painter.setBrush(dense3Brush); painter.drawText(startX + shapeSize + spacing, patternY + 10, "Dense3Pattern (中等密度):"); painter.drawRect(startX + shapeSize + spacing, patternY + 20, shapeSize, shapeSize); QBrush dense7Brush(Qt::gray, Qt::Dense7Pattern); // 灰色稀疏点图案 painter.setBrush(dense7Brush); painter.drawText(startX + 2 * (shapeSize + spacing), patternY + 10, "Dense7Pattern (最稀疏):"); painter.drawRect(startX + 2 * (shapeSize + spacing), patternY + 20, shapeSize, shapeSize); // 4. 不同线图案 (Line Patterns) int linePatternY = startY + 2 * rowSpacing; painter.drawText(startX, linePatternY, "线图案填充:"); QBrush horBrush(Qt::darkGreen, Qt::HorPattern); // 水平线图案画刷 painter.setBrush(horBrush); painter.drawText(startX, linePatternY + 10, "HorPattern (水平线):"); painter.drawRect(startX, linePatternY + 20, shapeSize, shapeSize); QBrush verBrush(Qt::darkBlue, Qt::VerPattern); // 垂直线图案画刷 painter.setBrush(verBrush); painter.drawText(startX + shapeSize + spacing, linePatternY + 10, "VerPattern (垂直线):"); painter.drawRect(startX + shapeSize + spacing, linePatternY + 20, shapeSize, shapeSize); QBrush crossBrush(Qt::darkRed, Qt::CrossPattern); // 交叉线图案画刷 painter.setBrush(crossBrush); painter.drawText(startX + 2 * (shapeSize + spacing), linePatternY + 10, "CrossPattern (交叉线):"); painter.drawRect(startX + 2 * (shapeSize + spacing), linePatternY + 20, shapeSize, shapeSize); // 5. 斜线图案 (Diagonal Patterns) int diagPatternY = startY + 3 * rowSpacing; painter.drawText(startX, diagPatternY, "斜线图案填充:"); QBrush bDiagBrush(Qt::magenta, Qt::BDiagPattern); // 反斜线图案画刷 (从左下到右上) painter.setBrush(bDiagBrush); painter.drawText(startX, diagPatternY + 10, "BDiagPattern (反斜线):"); painter.drawRect(startX, diagPatternY + 20, shapeSize, shapeSize); QBrush fDiagBrush(Qt::cyan, Qt::FDiagPattern); // 正斜线图案画刷 (从左上到右下) painter.setBrush(fDiagBrush); painter.drawText(startX + shapeSize + spacing, diagPatternY + 10, "FDiagPattern (正斜线):"); painter.drawRect(startX + shapeSize + spacing, diagPatternY + 20, shapeSize, shapeSize); QBrush diagCrossBrush(Qt::darkYellow, Qt::DiagCrossPattern); // 斜交叉图案画刷 painter.setBrush(diagCrossBrush); painter.drawText(startX + 2 * (shapeSize + spacing), diagPatternY + 10, "DiagCrossPattern (斜交叉):"); painter.drawRect(startX + 2 * (shapeSize + spacing), diagPatternY + 20, shapeSize, shapeSize); // 注意:渐变 (LinearGradientPattern, RadialGradientPattern, ConicalGradientPattern) // 关于纹理填充 (TexturePattern) // 纹理填充需要一个QPixmap或QImage作为纹理源, // 这里为了保持代码简洁不直接包含图片文件,如果需要可以自行加载图片创建QBrush。 // QBrush textureBrush(QPixmap(":/path/to/your/texture.png")); // 示例:加载图片创建纹理画刷 // painter.setBrush(textureBrush); // painter.drawRect(...); } }; // main函数,程序的入口 int main(int argc, char *argv[]) { QApplication a(argc, argv); QBrushExampleWidget w; w.show(); // 显示窗口 return a.exec(); } // 需要moc处理,否则无法编译,因为自定义类中使用了Q_OBJECT宏 #include "main.moc"
2.3、渐变填充:QGradient
QGradient 是 QBrush 的高级填充方式,提供线性、径向和圆锥渐变效果。一般使用其子类:QLinearGradient、QRadialGradient 和 QConicalGradient。通过 QBrush实现效果。
注意:当渐变定义区域小于实际绘制区域是,应设置扩展方式获得预期的渐变效果。
2.3.1、QGradient 基类
QGradient 提供通用的渐变设置方法:
-
setColorAt(qreal pos, const QColor &color):设置颜色停止点
-
setSpread(QGradient::Spread spread):设置渐变扩展方式:
-
QGradient::PadSpread:填充边界外的区域
-
QGradient::ReflectSpread:反射渐变
-
QGradient::RepeatSpread:重复渐变
-
2.3.2、QLinearGradient:线性渐变:线性渐变沿直线从起点到终点过渡颜色。
2.3.2.1、构造函数
-
QLinearGradient():默认构造,未初始化起点和终点。
-
QLinearGradient(qreal x1, qreal y1, qreal x2, qreal y2):指定起点 (x1, y1) 和终点 (x2, y2)。
-
QLinearGradient(const QPointF &start, const QPointF &finalStop):使用 QPointF 指定起点和终点。
2.3.2.2、几何设置函数:设置或获取线性渐变的起点和终点。
-
void setStart(qreal x, qreal y) :设置渐变的起点坐标。
-
QPointF start() const:返回渐变的起点坐标。
-
void setFinalStop(qreal x, qreal y):设置渐变的终点坐标。
-
QPointF finalStop() const:返回渐变的终点坐标。
2.3.3、QRadialGradient:径向渐变:径向渐变从中心点向外扩展,基于半径和可选的焦点。
2.3.3.1、构造函数
-
QRadialGradient():默认构造,未初始化中心、半径或焦点。
-
QRadialGradient(qreal cx, qreal cy, qreal radius):指定中心 (cx, cy) 和半径,焦点默认与中心重合。
-
QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qreal fy):指定中心、半径和焦点 (fx, fy)。
-
QRadialGradient(const QPointF ¢er, qreal radius, const QPointF &focalPoint):使用 QPointF。
2.3.3.2、几何设置函数:设置或获取径向渐变的中心、半径和焦点
-
void setCenter(qreal x, qreal y) :设置渐变的中心点坐标。
-
void setFocalPoint(qreal x, qreal y):设置渐变的焦点坐标(颜色渐变的起点)。
-
void setRadius(qreal radius):设置渐变的半径。
2.3.4、QConicalGradient:圆锥渐变:圆锥渐变围绕中心点按角度旋转颜色。
2.3.4.1、构造函数
-
QConicalGradient():默认构造,未初始化中心或角度。
-
QConicalGradient(qreal cx, qreal cy, qreal angle):指定中心 (cx, cy) 和起始角度。
-
QConicalGradient(const QPointF ¢er, qreal angle):使用 QPointF。
2.3.4.2、几何设置函数:这些函数用于设置或获取圆锥渐变的中心和起始角度。
-
void setCenter(qreal x, qreal y) :设置渐变的中心点坐标。
-
void setAngle(qreal angle):设置渐变的起始角度。
2.3.5、示例代码:注意渐变区域与实际绘制区域大小
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QBrush>
#include <QColor>
#include <QRect>
#include <QPaintEvent>
#include <QLinearGradient> // 包含线性渐变的头文件
#include <QRadialGradient> // 包含径向渐变的头文件
#include <QConicalGradient> // 包含圆锥渐变的头文件
class GradientWidget : public QWidget
{
Q_OBJECT
public:
GradientWidget(QWidget *parent = nullptr) : QWidget(parent)
{
setWindowTitle("QGradient 示例 (整合)");
setMinimumSize(600, 650);
}
protected:
// 重写paintEvent函数,在这里进行绘图操作
void paintEvent(QPaintEvent *event) override
{
Q_UNUSED(event); // 标记事件参数未使用,避免编译器警告
QPainter painter(this); // 创建一个QPainter对象,用于在当前widget上绘图
painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿,使形状边缘更平滑
// 设置一个细的黑色边框,这样填充区域和背景有清晰界限
QPen thinBlackPen(Qt::black, 1);
painter.setPen(thinBlackPen);
int startX = 10;
int startY = 20;
int shapeWidth = 150;
int shapeHeight = 80;
int spacing = 20;
int rowSpacing = 120;
painter.drawText(startX, startY, "渐变填充示例:");
// 1. 线性渐变 (QLinearGradient)
int linearY = startY + spacing;
// 定义线性渐变的起始点和结束点
QLinearGradient linearGradient(startX, linearY + 10, startX + shapeWidth, linearY + 10 + shapeHeight);
// 添加颜色停止点:位置 (0.0 到 1.0) 和颜色
linearGradient.setColorAt(0.0, Qt::red); // 起始点 (0.0) 颜色为红色
linearGradient.setColorAt(0.5, Qt::yellow); // 中间点 (0.5) 颜色为黄色
linearGradient.setColorAt(1.0, Qt::blue); // 结束点 (1.0) 颜色为蓝色
QBrush linearBrush(linearGradient); // 使用线性渐变创建画刷
painter.setBrush(linearBrush); // 设置当前画刷
painter.drawText(startX, linearY, "QLinearGradient (线性渐变):");
painter.drawRect(startX, linearY + 10, shapeWidth, shapeHeight); // 绘制一个矩形来展示渐变
// 2. 径向渐变 (QRadialGradient)
int radialY = startY + rowSpacing;
// 定义径向渐变的中心点、半径和焦点 (焦点是颜色开始扩散的点,通常与中心点相同)
// QRadialGradient(centerX, centerY, radius, focalX, focalY)
QRadialGradient radialGradient(startX + shapeWidth/2, radialY + 10 + shapeHeight/2, shapeWidth/2, // 中心点和半径
startX + shapeWidth/2, radialY + 10 + shapeHeight/2); // 焦点 (此处与中心点重合)
radialGradient.setColorAt(0.0, Qt::white); // 中心颜色为白色
radialGradient.setColorAt(0.6, Qt::darkCyan); // 中间颜色为深青色
radialGradient.setColorAt(1.0, Qt::black); // 边缘颜色为黑色
QBrush radialBrush(radialGradient); // 使用径向渐变创建画刷
painter.setBrush(radialBrush);
painter.drawText(startX, radialY, "QRadialGradient (径向渐变):");
painter.drawEllipse(startX, radialY + 10, shapeWidth, shapeHeight); // 绘制一个椭圆来展示径向渐变
// 3. 圆锥渐变 (QConicalGradient)
int conicalY = startY + 2 * rowSpacing;
// 定义圆锥渐变的中心点和起始角度 (角度为顺时针,0度在3点钟方向)
// QConicalGradient(centerX, centerY, startAngle)
QConicalGradient conicalGradient(startX + shapeWidth/2, conicalY + 10 + shapeHeight/2, 0.0); // 中心点,起始角度0度
conicalGradient.setColorAt(0.0, Qt::red); // 0度 (右侧) 颜色为红色
conicalGradient.setColorAt(0.25, Qt::green); // 90度 (下方) 颜色为绿色
conicalGradient.setColorAt(0.5, Qt::blue); // 180度 (左侧) 颜色为蓝色
conicalGradient.setColorAt(0.75, Qt::yellow); // 270度 (上方) 颜色为黄色
conicalGradient.setColorAt(1.0, Qt::red); // 360度颜色回到红色 (与0度连接)
QBrush conicalBrush(conicalGradient); // 使用圆锥渐变创建画刷
painter.setBrush(conicalBrush);
painter.drawText(startX, conicalY, "QConicalGradient (圆锥渐变):");
painter.drawEllipse(startX, conicalY + 10, shapeWidth, shapeHeight); // 绘制一个椭圆来展示圆锥渐变
// 4. 渐变扩展模式 (Spread Modes)
int spreadY = startY + 3 * rowSpacing;
painter.drawText(startX, spreadY, "渐变扩展模式 (以线性渐变为例):");
// 定义一个只覆盖一部分区域的线性渐变
QLinearGradient partialGradient(startX, spreadY + 10, startX + shapeWidth/2, spreadY + 10);
partialGradient.setColorAt(0.0, Qt::magenta);
partialGradient.setColorAt(1.0, Qt::cyan);
// QGradient::PadSpread (默认): 边界外的区域用渐变的结束颜色填充
partialGradient.setSpread(QGradient::PadSpread);
QBrush padSpreadBrush(partialGradient);
painter.setBrush(padSpreadBrush);
painter.drawText(startX, spreadY + 10, "PadSpread (填充):");
painter.drawRect(startX, spreadY + 20, shapeWidth, shapeHeight/2);
// QGradient::RepeatSpread: 渐变图案在边界外重复
partialGradient.setSpread(QGradient::RepeatSpread);
QBrush repeatSpreadBrush(partialGradient);
painter.setBrush(repeatSpreadBrush);
painter.drawText(startX, spreadY + 10 + shapeHeight/2 + spacing/2, "RepeatSpread (重复):");
painter.drawRect(startX, spreadY + 20 + shapeHeight/2 + spacing/2, shapeWidth, shapeHeight/2);
// QGradient::ReflectSpread: 渐变图案在边界外反射(镜像)重复
int reflectY = startY + 4 * rowSpacing;
painter.drawText(startX, reflectY, "ReflectSpread (反射):");
// 重新定义一个不同的部分渐变
QLinearGradient reflectGradient(startX, reflectY + 10, startX + shapeWidth/2, reflectY + 10);
reflectGradient.setColorAt(0.0, Qt::darkYellow);
reflectGradient.setColorAt(1.0, Qt::darkGreen);
reflectGradient.setSpread(QGradient::ReflectSpread);
QBrush reflectSpreadBrush(reflectGradient);
painter.setBrush(reflectSpreadBrush);
painter.drawRect(startX, reflectY + 20, shapeWidth, shapeHeight);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GradientWidget w;
w.show();
return a.exec();
}
// 需要moc处理,否则无法编译,因为自定义类中使用了Q_OBJECT宏
#include "main.moc"
3、复杂图形与区域
3.1、复杂路径绘制:QPainterPath
QPainterPath 是一个强大的类,用于定义和管理复杂的 2D 路径。它可以表示任意形状的路径,包括直线、曲线、基本形状和文本,并支持描边和填充。
-
定义复杂的 2D 路径,包含直线、贝塞尔曲线、矩形、椭圆等。
-
支持路径的布尔运算(联合、相交等),便于构造复杂形状。
-
可用于描边(stroke)、填充(fill)以及裁剪。
3.1.1、构建路径:支持基本形状、线条、曲线和文本。
3.1.1.1、添加基本形状
-
void addRect(const QRectF &rect):添加矩形路径。
-
void addEllipse(const QRectF &rect):添加椭圆路径(矩形定义外接框)。
-
void addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius):添加圆角矩形。
-
void addPolygon(const QPolygonF &polygon):添加多边形路径。
3.1.1.2、添加线条与曲线
-
void moveTo(qreal x, qreal y) / void moveTo(const QPointF &point):移动当前点到指定位置(不绘制)。
-
void lineTo(qreal x, qreal y) / void lineTo(const QPointF &point):从当前点绘制直线到指定点。
-
void quadTo(qreal cx, qreal cy, qreal endX, qreal endY):绘制二次贝塞尔曲线(一个控制点)。
-
void cubicTo(qreal c1x, qreal c1y, qreal c2x, qreal c2y, qreal endX, qreal endY):绘制三次贝塞尔曲线(两个控制点)。
-
void arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength):绘制椭圆弧。
3.1.1.3、添加文本
-
void addText(qreal x, qreal y, const QFont &font, const QString &text):在指定位置添加文本路径。
3.1.1.4、路径闭合
-
void closeSubpath():闭合当前子路径(连接当前点与子路径起点)。
3.1.2、路径操作:联合、相交、差集:用于组合多个路径
-
QPainterPath united(const QPainterPath &other):返回两个路径的联合。
-
QPainterPath intersected(const QPainterPath &other):返回两个路径的交集。
-
QPainterPath subtracted(const QPainterPath &other):返回当前路径减去另一路径的差集。
-
QPainterPath simplified():简化路径,移除冗余点。
-
operator|, operator&, operator-:分别对应联合、相交、差集的快捷操作。
3.1.3、使用 QPainterPath 进行绘制:结合 QPen 和 QBrush 实现描边和填充
QPainterPath 与 QPainter 结合,通过 drawPath 绘制路径。
-
QPainter::drawPath(const QPainterPath &path):绘制路径。
-
QPainter::setPen(QPen):设置描边样式。
-
QPainter::setBrush(QBrush):设置填充样式。
3.1.4、路径碰撞检测
-
bool contains(const QPointF &point) const:判断点是否在路径内(填充区域)。
-
bool contains(const QRectF &rect) const:判断矩形是否完全在路径内。
-
bool intersects(const QRectF &rect) const:判断矩形是否与路径相交。
3.1.5、示例代码
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QPainterPath> // 包含QPainterPath头文件
#include <QPen>
#include <QBrush>
#include <QColor>
#include <QRectF>
#include <QPointF>
#include <QFont>
#include <QPaintEvent>
#include <QPolygonF> // 包含QPolygonF头文件
class PathWidget : public QWidget
{
Q_OBJECT // 需要Q_OBJECT宏来支持信号和槽等元对象特性
public:
PathWidget(QWidget *parent = nullptr) : QWidget(parent)
{
setWindowTitle("QPainterPath 示例 (整合)");
setMinimumSize(800, 700);
}
protected:
// 重写paintEvent函数,在这里进行绘图操作
void paintEvent(QPaintEvent *event) override
{
Q_UNUSED(event); // 标记事件参数未使用,避免编译器警告
QPainter painter(this); // 创建一个QPainter对象,用于在当前widget上绘图
painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿,使路径边缘更平滑
// 设置默认的描边和填充画笔
painter.setPen(QPen(Qt::black, 2)); // 黑色,2像素宽描边
painter.setBrush(QBrush(Qt::cyan, Qt::SolidPattern)); // 青色纯色填充
int startX = 10;
int startY = 20;
int spacingY = 150;
int spacingX = 200;
//=======================================
// --- 1. 构建路径示例 ---
//=======================================
// 1.1 添加基本形状
painter.drawText(startX, startY, "1.1 添加基本形状:");
QPainterPath shapesPath;
shapesPath.addRect(QRectF(startX, startY + 10, 100, 50)); // 添加矩形
shapesPath.addEllipse(QRectF(startX + 120, startY + 10, 50, 50)); // 添加椭圆
shapesPath.addRoundedRect(QRectF(startX + 180, startY + 10, 100, 50), 10, 10); // 添加圆角矩形
QPolygonF polygon; // 创建一个多边形
polygon << QPointF(startX + 300, startY + 60) << QPointF(startX + 350, startY + 10) << QPointF(startX + 400, startY + 60) << QPointF(startX + 350, startY + 100);
shapesPath.addPolygon(polygon); // 添加多边形
painter.setBrush(Qt::lightGray); // 为形状路径设置不同的填充颜色
painter.drawPath(shapesPath); // 绘制包含基本形状的路径
painter.setBrush(Qt::cyan); // 恢复默认填充
// 1.2 添加线条与曲线
int linesY = startY + spacingY;
painter.drawText(startX, linesY, "1.2 添加线条与曲线:");
QPainterPath linesPath;
linesPath.moveTo(startX, linesY + 10); // 移动到起点 (不绘制)
linesPath.lineTo(startX + 50, linesY + 60); // 绘制直线到 (50, 60)
linesPath.quadTo(startX + 100, linesY + 10, startX + 150, linesY + 60); // 绘制二次贝塞尔曲线 (控制点 100, 10)
linesPath.cubicTo(startX + 200, linesY + 10, startX + 250, linesY + 110, startX + 300, linesY + 60); // 绘制三次贝塞尔曲线 (控制点 200, 10 和 250, 110)
linesPath.arcTo(QRectF(startX + 320, linesY + 10, 50, 50), 0, 90); // 添加一个 90 度的圆弧 (从 0 度到 90 度)
// 闭合一个子路径示例 (三角形)
linesPath.moveTo(startX + 400, linesY + 10);
linesPath.lineTo(startX + 450, linesY + 60);
linesPath.lineTo(startX + 400, linesY + 60);
linesPath.closeSubpath(); // 闭合当前子路径,形成三角形
painter.setBrush(Qt::NoBrush); // 只描边,不填充
painter.drawPath(linesPath); // 绘制包含线条和曲线的路径
painter.setBrush(Qt::cyan); // 恢复默认填充
// 1.3 添加文本
int textY = startY + 2 * spacingY;
painter.drawText(startX, textY, "1.3 添加文本:");
QPainterPath textPath;
QFont font("Arial", 30, QFont::Bold); // 创建一个粗体 Arial 字体,大小为 30
textPath.addText(startX, textY + 40, font, "Hello Qt Path!"); // 将文本转换为路径
painter.setBrush(Qt::darkBlue); // 用深蓝色填充文本路径
painter.drawPath(textPath); // 绘制文本路径
painter.setBrush(Qt::cyan); // 恢复默认填充
//==========================================
// --- 2. 路径操作示例 ---
//==========================================
int opsY = startY + 3 * spacingY;
painter.drawText(startX, opsY, "2. 路径操作 (联合、相交、差集):");
// 创建两个用于操作的原始路径
QPainterPath pathA, pathB;
pathA.addEllipse(QRectF(startX, opsY + 20, 100, 100)); // 圆形 A
pathB.addRect(QRectF(startX + 50, opsY + 70, 100, 100)); // 矩形 B
// 2.1 联合 (United)
QPainterPath unionPath = pathA.united(pathB); // A 和 B 的联合
painter.drawText(startX, opsY + 140, "A 联合 B:");
painter.setPen(QPen(Qt::darkGreen, 2)); // 绿色描边
painter.setBrush(Qt::green); // 绿色填充
painter.drawPath(unionPath); // 绘制联合路径
// 2.2 相交 (Intersected)
QPainterPath intersectPath = pathA.intersected(pathB); // A 和 B 的交集
painter.drawText(startX + spacingX, opsY + 140, "A 相交 B:");
painter.translate(spacingX, 0); // 临时平移绘图坐标系
painter.setPen(QPen(Qt::darkRed, 2)); // 红色描边
painter.setBrush(Qt::red); // 红色填充
painter.drawPath(intersectPath); // 绘制相交路径
painter.translate(-spacingX, 0); // 恢复绘图坐标系
// 2.3 差集 (Subtracted)
QPainterPath subtractPath = pathA.subtracted(pathB); // A 减去 B 的差集
painter.drawText(startX + 2 * spacingX, opsY + 140, "A 减去 B:");
painter.translate(2 * spacingX, 0); // 临时平移
painter.setPen(QPen(Qt::darkBlue, 2)); // 蓝色描边
painter.setBrush(Qt::blue); // 蓝色填充
painter.drawPath(subtractPath); // 绘制差集路径
painter.translate(-2 * spacingX, 0); // 恢复
// 绘制原始路径以便对比 (可选)
painter.setPen(QPen(Qt::gray, 1, Qt::DashLine)); // 灰色虚线
painter.setBrush(Qt::NoBrush); // 无填充
painter.drawPath(pathA);
painter.drawPath(pathB);
// --- 3. 路径碰撞检测 (注意:在 paintEvent 中直观演示碰撞检测结果较复杂,这里仅通过文本说明概念) ---
// bool contains(const QPointF &point) const;
// bool contains(const QRectF &rect) const;
// bool intersects(const QRectF &rect) const;
// 例如:
// QPainterPath detectionPath;
// detectionPath.addEllipse(QRectF(500, 500, 100, 100));
// QPointF testPoint(550, 550);
// bool isInside = detectionPath.contains(testPoint); // 判断点是否在路径内
// 在 paintEvent 中,你通常会根据这些检测结果来改变绘制行为或状态,而不是直接绘制布尔值。
// 由于空间和复杂性考虑,这里不进行绘制演示,仅通过上述代码片段说明用法。
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
PathWidget w;
w.show(); // 显示窗口
return a.exec();
}
// 需要moc处理,否则无法编译,因为自定义类中使用了Q_OBJECT宏
#include "main.moc"
3.2、2D 区域:QRegion
QRegion 表示 2D 平面上的区域,是一个由像素组成的区域,通常由矩形、多边形或其他形状组成,常用于裁剪、碰撞检测和窗口形状定义。
3.2.1、创建 QRegion:可以从矩形、多边形、位图等创建。
-
QRegion():创建空区域。
-
QRegion(const QRect &rect):从矩形创建区域。
-
QRegion(const QPolygon &polygon):从多边形创建区域。
-
QRegion(const QBitmap &bitmap):从位图创建区域(非零像素表示区域)。
-
QRegion(int x, int y, int w, int h):从指定坐标和尺寸创建矩形区域。
-
QRegion translated(int dx, int dy) const:返回平移后的区域。
-
static QRegion fromBitmap(const QBitmap &bitmap):从位图创建(静态方法)。
3.2.2、区域操作:联合、相交、差集、异或:用于组合多个区域
-
QRegion united(const QRegion &other) const:联合(并集)。
-
QRegion intersected(const QRegion &other) const:相交(交集)。
-
QRegion subtracted(const QRegion &other) const:差集(当前区域减去另一区域)。
-
QRegion xored(const QRegion &other) const:异或(非重叠部分)。
-
operator|, operator&, operator-, operator^:分别对应联合、相交、差集、异或。
3.2.3、使用 QRegion 进行裁剪 :通过 QPainter::setClipRegion 设置裁剪区域
-
void QPainter::setClipRegion(const QRegion ®ion):设置裁剪区域。
-
QRegion QPainter::clipRegion() const:返回当前裁剪区域。
3.2.4、使用 QRegion 进行碰撞检测
-
bool contains(const QPoint &point) const:判断点是否在区域内。
-
bool contains(const QRect &rect) const:判断矩形是否完全在区域内。
-
bool intersects(const QRegion ®ion) const:判断区域是否与另一区域相交。
-
bool isEmpty() const:判断区域是否为空。
3.2.5、示例代码
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QRegion>
#include <QPolygon>
#include <QBitmap>
#include <QDebug>
class MyWidget : public QWidget {
protected:
void paintEvent(QPaintEvent *) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing); // 启用抗锯齿
//=========================
// 2.2 创建 QRegion
//=========================
// 2.2.1 构造函数
// 矩形区域
QRegion region1(QRect(50, 50, 100, 100));
// 多边形区域,三角形
QPolygon polygon({QPoint(150, 50), QPoint(200, 100), QPoint(150, 150)});
QRegion region2(polygon);
// 位图区域,圆形
QBitmap bitmap(50, 50);
bitmap.fill(Qt::color0); // 透明
QPainter bitmapPainter(&bitmap);
bitmapPainter.setBrush(Qt::color1); // 不透明
bitmapPainter.drawEllipse(0, 0, 50, 50);
QRegion region3(bitmap);
region3 = region3.translated(250, 50); // 平移到合适位置
// 2.2.2 其他创建方法
// 平移区域
QRegion translatedRegion = region1.translated(20, 20);
//=========================================
// 2.3 区域操作:联合、相交、差集、异或
//=========================================
QRegion region4(QRect(100, 100, 100, 100));
QRegion unionRegion = region1.united(region4); // 联合
QRegion intersectRegion = region1.intersected(region4); // 相交
QRegion subtractRegion = region1.subtracted(region4); // 差集
QRegion xorRegion = region1.xored(region4); // 异或
//================================
// 2.4 使用 QRegion 进行裁剪
//================================
// 绘制裁剪效果:仅在 region1 内填充蓝色
painter.setClipRegion(region1);
painter.fillRect(0, 0, 200, 200, Qt::blue); // 仅 (50,50,100,100) 被填充
painter.setClipping(false); // 关闭裁剪以继续其他绘制
// 绘制原始区域
painter.setPen(QPen(Qt::black, 1));
painter.setBrush(QBrush(Qt::yellow, Qt::DiagCrossPattern));
painter.drawRects(region1.rects()); // 绘制 region1
painter.setBrush(QBrush(Qt::green, Qt::CrossPattern));
painter.drawPolygon(polygon); // 绘制 region2
painter.setBrush(QBrush(Qt::red, Qt::Dense4Pattern));
painter.drawEllipse(250, 50, 50, 50); // 绘制 region3
painter.setBrush(QBrush(Qt::cyan, Qt::HorPattern));
painter.drawRects(translatedRegion.rects()); // 绘制 translatedRegion
// 绘制布尔运算结果
painter.setPen(QPen(Qt::black, 1));
painter.setBrush(QBrush(Qt::magenta, Qt::SolidPattern));
painter.translate(0, 200); // 下移绘制区域
painter.drawRects(unionRegion.rects()); // 联合
painter.drawText(10, 150, "Union");
painter.translate(150, 0);
painter.drawRects(intersectRegion.rects()); // 相交
painter.drawText(10, 150, "Intersection");
painter.translate(150, 0);
painter.drawRects(subtractRegion.rects()); // 差集
painter.drawText(10, 150, "Subtraction");
painter.translate(150, 0);
painter.drawRects(xorRegion.rects()); // 异或
painter.drawText(10, 150, "XOR");
//===============================
// 2.5 使用 QRegion 进行碰撞检测
//===============================
bool pointInside = region1.contains(QPoint(100, 100)); // true
bool rectInside = region1.contains(QRect(60, 60, 20, 20)); // true
bool regionsIntersect = region1.intersects(region4); // true
bool isEmpty = region1.isEmpty(); // false
// 输出碰撞检测结果到控制台
qDebug() << "Point (100,100) in region1:" << pointInside;
qDebug() << "Rect (60,60,20,20) in region1:" << rectInside;
qDebug() << "Region1 intersects region4:" << regionsIntersect;
qDebug() << "Region1 is empty:" << isEmpty;
// 在窗口显示碰撞检测结果
painter.resetTransform(); // 重置变换
painter.drawText(10, 450, QString("Point in region1: %1").arg(pointInside));
painter.drawText(10, 470, QString("Rect in region1: %1").arg(rectInside));
painter.drawText(10, 490, QString("Regions intersect: %1").arg(regionsIntersect));
painter.drawText(10, 510, QString("Region1 is empty: %1").arg(isEmpty));
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MyWidget widget;
widget.resize(500, 450);
widget.show();
return app.exec();
}
4、变换与坐标系统
4.1、2D 变换:QTransform
QTransform 是 Qt 框架中用于处理 2D 图形变换的核心类,支持平移、缩放、旋转和剪切等操作。它本质上是一个 3x3 矩阵,用于描述 2D 空间中的线性变换和仿射变换。QPainter 提供了 setTransform() 方法将 QTransform 应用于绘制操作。
4.1.1、理解 2D 变换的概念
2D 变换用于改变图形的几何属性,包括位置、大小、方向和形状。
-
平移(Translation):将图形沿 X 或 Y 轴移动指定距离。
-
数学表示:(x, y) → (x + dx, y + dy)。
-
-
缩放(Scaling):按比例放大或缩小图形。
-
数学表示:(x, y) → (sx * x, sy * y),其中 sx 和 sy 是缩放因子。
-
-
旋转(Rotation):围绕某点(通常是原点)旋转图形。
-
数学表示:(x, y) → (x * cosθ - y * sinθ, x * sinθ + y * cosθ),θ 为旋转角度。
-
-
剪切(Shearing):使图形沿某个轴倾斜,产生形变。
-
数学表示:(x, y) → (x + shx * y, y + shy * x),shx 和 shy 是剪切因子。
-
4.1.2、创建与配置 QTransform
-
默认构造函数:QTransform()
-
直接指定矩阵元素:QTransform(m11, m12, m13, m21, m22, m23, dx, dy, m33);
[ m11 m12 dx ] [ m21 m22 dy ] [ m13 m23 m33 ] -
通常 m33 = 1,m13 = m23 = 0,用于仿射变换。
4.1.3、应用变换:translate(), scale(), rotate(), shear()
-
平移:translate(dx, dy): 向x轴移动 dx像素,向Y轴移动 dy 像素
-
缩放:scale(sx, sy): X 轴放大sx 倍,Y 轴缩小到sy 倍
-
旋转:rotate(angle, axis = Qt::ZAxis):角度以度为单位,逆时针为正。
-
剪切:shear(shx, shy): X 轴剪切因子 shx,Y 轴剪切因子 shy。
-
组合变换: 变换是累积的,顺序会影响结果,先平移再旋转与先旋转再平移效果不同。
3.1.4、矩阵变换 (Matrix Transformation)
QTransform 内部使用 3x3 矩阵表示仿射变换,矩阵形式为:
[ m11 m12 dx ]
[ m21 m22 dy ]
[ 0 0 1 ]
-
点变换:对点 (x, y) 应用变换,结果为:
x' = m11 * x + m21 * y + dx y' = m12 * x + m22 * y + dy -
自定义矩阵:
QTransform transform; transform.setMatrix(1, 0.2, 0, 0.1, 1, 0, 50, 50, 1); // 自定义矩阵 -
矩阵运算: QTransform 支持矩阵乘法(变换组合):
QTransform t1, t2; t1.translate(100, 0); t2.rotate(30); QTransform combined = t1 * t2; // 先旋转再平移
3.1.5、示例代码
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QTransform>
#include <QPaintEvent>
class TransformWidget : public QWidget {
public:
TransformWidget(QWidget *parent = nullptr) : QWidget(parent) {
setMinimumSize(600, 400); // 设置窗口最小尺寸
}
protected:
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing); // 启用抗锯齿
// 保存初始状态
painter.save();
// 1. 绘制原始矩形(无变换,作为参考)
painter.setPen(Qt::black);
painter.setBrush(Qt::NoBrush);
painter.drawText(20, 20, "Original Rect (No Transform)");
painter.drawRect(20, 30, 50, 30);
// 2. 平移变换
QTransform translateTransform;
translateTransform.translate(100, 100); // 向右 100 像素,向下 100 像素
painter.setTransform(translateTransform);
painter.setPen(Qt::blue);
painter.drawText(0, -10, "Translated Rect");
painter.drawRect(0, 0, 50, 30);
// 恢复状态,避免影响后续绘制
painter.restore();
painter.save();
// 3. 缩放变换
QTransform scaleTransform;
scaleTransform.scale(2.0, 0.5); // X 轴放大 2 倍,Y 轴缩小 0.5 倍
painter.setTransform(scaleTransform);
painter.setPen(Qt::red);
painter.drawText(100, 60, "Scaled Rect");
painter.drawRect(100, 70, 50, 30);
// 恢复状态
painter.restore();
painter.save();
// 4. 旋转变换
QTransform rotateTransform;
rotateTransform.translate(200, 100); // 先平移到 (200, 100)
rotateTransform.rotate(45); // 绕 Z 轴旋转 45 度
painter.setTransform(rotateTransform);
painter.setPen(Qt::green);
painter.drawText(0, -10, "Rotated Rect");
painter.drawRect(0, 0, 50, 30);
// 恢复状态
painter.restore();
painter.save();
// 5. 剪切变换
QTransform shearTransform;
shearTransform.shear(0.2, 0.1); // X 轴剪切因子 0.2,Y 轴剪切因子 0.1
painter.setTransform(shearTransform);
painter.setPen(Qt::magenta);
painter.drawText(20, 120, "Sheared Rect");
painter.drawRect(20, 130, 50, 30);
// 恢复状态
painter.restore();
painter.save();
// 6. 自定义矩阵变换
QTransform matrixTransform;
matrixTransform.setMatrix(1, 0.2, 0, 0.1, 1, 0, 50, 50, 1); // 自定义 3x3 矩阵
painter.setTransform(matrixTransform);
painter.setPen(Qt::cyan);
painter.drawText(0, -10, "Custom Matrix Rect");
painter.drawRect(0, 0, 50, 30);
// 恢复状态
painter.restore();
painter.save();
// 7. 组合变换(平移 + 旋转 + 缩放)
QTransform combinedTransform;
combinedTransform.translate(300, 200); // 平移到 (300, 200)
combinedTransform.rotate(30); // 旋转 30 度
combinedTransform.scale(1.5, 1.5); // 放大 1.5 倍
painter.setTransform(combinedTransform);
painter.setPen(Qt::darkYellow);
painter.drawText(0, -10, "Combined Transform Rect");
painter.drawRect(0, 0, 50, 30);
// 恢复状态
painter.restore();
// 8. 矩阵运算:组合两个变换
QTransform t1, t2, combinedMatrix;
t1.translate(100, 300); // 平移
t2.rotate(30); // 旋转
combinedMatrix = t1 * t2; // 先旋转再平移
painter.setTransform(combinedMatrix);
painter.setPen(Qt::darkGreen);
painter.drawText(0, -10, "Matrix Multiplication Rect");
painter.drawRect(0, 0, 50, 30);
// 最终恢复状态
painter.restore();
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
TransformWidget widget;
widget.setWindowTitle("QTransform Demo");
widget.show();
return app.exec();
}
4.2、高级变换与坐标(常用于 OpenGL 相关)
4.2.1、QVector2D, QVector3D, QVector4D:向量表示
QVector2D、QVector3D 和 QVector4D 是 Qt 提供的向量类,用于表示 2D、3D 和 4D 空间中的点或向量(4D 向量常用于齐次坐标)。
-
QVector2D:在 2D/3D 空间中的点和向量,表示 2D 向量或点 (x, y)。
-
QVector3D:表示 3D 向量或点 (x, y, z)
-
QVector4D:表示 4D 向量 (x, y, z, w),常用于齐次坐标。
4.2.2、基本向量运算
-
加法/减法:+ -
-
标量乘法:V * 1.0
-
点积:v1.dotProduct(v1, v2)
-
叉积(仅 3D): v1.crossProduct(v1, v2)
-
长度与归一化: v.length() v.normalized()
4.2.3、QMatrix4x4:4x4 矩阵
QMatrix4x4 是 Qt 提供的 4x4 矩阵类,广泛用于 3D 变换和 OpenGL 渲染。它支持齐次坐标,能够表示复杂的 3D 变换(包括透视投影)。
4.2.3.1 理解齐次坐标与 4x4 矩阵在 2D/3D 变换中的作用
-
齐次坐标:
-
在 2D 中,点 (x, y) 表示为 (x, y, 1)。
-
在 3D 中,点 (x, y, z) 表示为 (x, y, z, 1)。
-
齐次坐标允许平移、旋转、缩放等变换通过矩阵乘法统一处理。
-
-
4x4 矩阵:
-
用于表示 3D 空间中的所有仿射变换和投影变换。
-
矩阵形式:
[ m11 m12 m13 m14 ] [ m21 m22 m23 m24 ] [ m31 m32 m33 m34 ] [ m41 m42 m43 m44 ] -
通常 m44 = 1,m14, m24, m34 = 0(仿射变换)。
-
-
平移:matrix.translate(10.0, 20.0, 30.0); // 平移 (10, 20, 30)
-
缩放:matrix.scale(2.0, 0.5, 1.0); // X 放大 2 倍,Y 缩小 0.5 倍,Z 不变
-
旋转:matrix.rotate(45.0, 0, 0, 1); // 绕 Z 轴旋转 45 度
-
透视投影:matrix.perspective(60.0, 4.0/3.0, 0.1, 100.0); // 视场角 60 度,宽高比 4:3,近裁面 0.1,远裁面 100。
4.2.3.2、在 OpenGL 渲染中
QMatrix4x4 常用于:
-
模型变换:将物体从模型坐标转换为世界坐标。
-
视图变换:将世界坐标转换为相机坐标。
-
投影变换:将 3D 场景投影到 2D 屏幕。
4.2.4、示例代码
#include <QApplication>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QMatrix4x4>
#include <QVector2D>
#include <QVector3D>
#include <QVector4D>
#include <QTimer>
#include <QDebug>
// 自定义 OpenGL 窗口部件,用于展示向量和矩阵操作
class OpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT
public:
OpenGLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {
// 设置定时器以实现动画效果
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [this]() { this->update(); });
timer->start(16); // 约 60 FPS
}
protected:
// 初始化 OpenGL 环境
void initializeGL() override {
initializeOpenGLFunctions(); // 初始化 OpenGL 函数
// 设置着色器程序
m_program = new QOpenGLShaderProgram(this);
// 顶点着色器:处理顶点位置并应用 MVP 矩阵
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
"#version 330 core\n"
"layout(location = 0) in vec3 vertexPos;\n"
"uniform mat4 mvp;\n"
"void main() {\n"
" gl_Position = mvp * vec4(vertexPos, 1.0);\n"
"}"
);
// 片段着色器:设置固定颜色(青色)
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
"#version 330 core\n"
"out vec4 fragColor;\n"
"void main() {\n"
" fragColor = vec4(0.0, 0.8, 0.8, 1.0);\n"
"}"
);
m_program->link(); // 链接着色器
m_program->bind(); // 绑定着色器
// 定义简单立方体的顶点(8 个顶点)
GLfloat vertices[] = {
// 前表面
-0.5f, -0.5f, 0.5f, // 左下
0.5f, -0.5f, 0.5f, // 右下
0.5f, 0.5f, 0.5f, // 右上
-0.5f, 0.5f, 0.5f, // 左上
// 后表面
-0.5f, -0.5f, -0.5f, // 左下
0.5f, -0.5f, -0.5f, // 右下
0.5f, 0.5f, -0.5f, // 右上
-0.5f, 0.5f, -0.5f // 左上
};
// 定义立方体的索引(12 个三角形)
GLushort indices[] = {
0, 1, 2, 2, 3, 0, // 前表面
1, 5, 6, 6, 2, 1, // 右表面
5, 4, 7, 7, 6, 5, // 后表面
4, 0, 3, 3, 7, 4, // 左表面
3, 2, 6, 6, 7, 3, // 上表面
4, 5, 1, 1, 0, 4 // 下表面
};
// 创建并绑定顶点缓冲对象 (VBO) 和索引缓冲对象 (IBO)
glGenBuffers(1, &m_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &m_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 设置顶点属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), nullptr);
glEnableVertexAttribArray(0);
// 启用深度测试
glEnable(GL_DEPTH_TEST);
// 设置背景颜色(深灰色)
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
}
// 处理窗口大小调整
void resizeGL(int w, int h) override {
glViewport(0, 0, w, h); // 设置视口
m_projection.setToIdentity(); // 重置投影矩阵
// 设置透视投影:60° 视场角,宽高比,近裁面 0.1,远裁面 100
m_projection.perspective(60.0f, static_cast<float>(w) / h, 0.1f, 100.0f);
}
// 绘制场景
void paintGL() override {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓冲
m_program->bind(); // 绑定着色器程序
// 演示向量操作
demonstrateVectorOperations();
// 设置变换矩阵
QMatrix4x4 model, view;
model.setToIdentity(); // 初始化模型矩阵
view.setToIdentity(); // 初始化视图矩阵
// 模型变换:平移、旋转、缩放
model.translate(0.0f, 0.0f, -5.0f); // 将立方体沿 Z 轴向后移动 5 单位
model.rotate(m_rotationAngle, 1.0f, 1.0f, 0.0f); // 绕 (1,1,0) 轴旋转
model.scale(1.5f, 1.5f, 1.5f); // 放大 1.5 倍
// 视图变换:设置相机位置
view.translate(0.0f, 0.0f, -5.0f); // 相机沿 Z 轴后移 5 单位
// 组合矩阵:模型-视图-投影 (MVP)
QMatrix4x4 mvp = m_projection * view * model;
m_program->setUniformValue("mvp", mvp); // 传递 MVP 矩阵给着色器
// 绘制立方体
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, nullptr);
m_program->release(); // 释放着色器程序
// 更新旋转角度以实现动画
m_rotationAngle += 1.0f;
if (m_rotationAngle >= 360.0f) m_rotationAngle -= 360.0f;
}
private:
// 演示向量操作
void demonstrateVectorOperations() {
// 2.1.1 向量创建
QVector2D v2d(3.0f, 4.0f); // 2D 向量 (3, 4)
QVector3D v3d(1.0f, 2.0f, 3.0f); // 3D 向量 (1, 2, 3)
QVector4D v4d(1.0f, 2.0f, 3.0f, 1.0f); // 4D 齐次坐标向量 (1, 2, 3, 1)
// 2.1.2 向量运算
// 加法
QVector3D v3d2(4.0f, 5.0f, 6.0f);
QVector3D sum = v3d + v3d2; // 结果:(5, 7, 9)
qDebug() << "向量加法:" << sum.x() << sum.y() << sum.z();
// 标量乘法
QVector3D scaled = v3d * 2.0f; // 结果:(2, 4, 6)
qDebug() << "标量乘法:" << scaled.x() << scaled.y() << scaled.z();
// 点积
float dot = QVector3D::dotProduct(v3d, v3d2); // 1*4 + 2*5 + 3*6 = 32
qDebug() << "点积:" << dot;
// 叉积
QVector3D cross = QVector3D::crossProduct(v3d, v3d2);
qDebug() << "叉积:" << cross.x() << cross.y() << cross.z();
// 长度与归一化
float length = v3d.length(); // 计算向量长度
QVector3D normalized = v3d.normalized(); // 归一化向量
qDebug() << "长度:" << length << "归一化:" << normalized.x() << normalized.y() << normalized.z();
}
QOpenGLShaderProgram *m_program; // 着色器程序
GLuint m_vbo, m_ibo; // 顶点缓冲对象和索引缓冲对象
QMatrix4x4 m_projection; // 投影矩阵
float m_rotationAngle = 0.0f; // 旋转角度
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建并显示 OpenGL 窗口
OpenGLWidget widget;
widget.setWindowTitle("QMatrix4x4 和 QVector 演示");
widget.resize(800, 600); // 设置窗口大小
widget.show();
return app.exec();
}
#include "main.moc"
5、与 UI 集成:在控件上绘图说明
5.1、QWidget 的 paintEvent()
paintEvent() 是 QWidget 类中的一个虚拟函数,当控件需要重绘时由 Qt 的事件循环自动调用。它是控件进行自定义绘图的主要入口点。
Qt 会在以下情况下触发 paintEvent():
-
控件首次显示时。
-
控件被调整大小、移动或显示状态改变时。
-
控件被其他窗口遮挡后重新暴露出来。
-
开发者手动调用 update() 或 repaint() 请求重绘。
实现方式
-
重写 QWidget 或其子类的 paintEvent() 函数。
-
在函数中使用 QPainter 进行绘图操作。
-
paintEvent() 由 Qt 的事件循环调用,开发者不应直接调用它。
5.2、在 paintEvent() 中使用 QPainter
QPainter 是 Qt 的核心绘图类,用于在 paintEvent() 中执行具体的绘图操作。它提供了一套丰富的 API,用于绘制基本几何形状、文本、图像以及处理变换、抗锯齿等。
使用步骤
-
创建 QPainter 对象:将 QWidget 指针(通常是 this)传递给 QPainter 构造函数,使其绑定到当前控件。
-
配置绘图属性:
-
设置画笔(QPen):控制线条颜色、宽度、样式等。
-
设置画刷(QBrush):控制填充颜色、渐变、图案等。
-
启用抗锯齿:painter.setRenderHint(QPainter::Antialiasing);
-
设置变换:支持平移、旋转、缩放等。
-
-
执行绘图操作:
-
绘制基本形状:drawLine()、drawRect()、drawEllipse() 等。
-
绘制文本:drawText()。
-
绘制图像:drawPixmap()、drawImage()。
-
-
自动清理:QPainter 对象在离开作用域时会自动销毁,释放资源。
高级特性
-
裁剪区域:使用 painter.setClipRect() 或 painter.setClipRegion() 限制绘图区域,仅绘制需要更新的部分,提升性能。
-
坐标变换:使用 painter.translate()、painter.rotate()、painter.scale() 实现复杂的图形变换。
-
抗锯齿:通过 setRenderHint(QPainter::Antialiasing) 改善绘制质量,适合绘制曲线或斜线。
-
渐变填充:使用 QLinearGradient、QRadialGradient 等实现平滑的颜色过渡。
5.3、更新控件:update()、repaint()
它们都会导致 paintEvent() 被调用,但使用场景和行为有所不同。
5.3.1、update():请求异步重绘控件,将重绘事件放入 Qt 的事件队列,稍后由事件循环处理。
-
特点:
-
异步操作,多个 update() 调用可能被合并为一次重绘,减少不必要的绘制。
-
适合大多数场景,如控件内容发生变化(例如用户交互、数据更新)时。
-
不会立即触发 paintEvent(),延迟较小但性能更高。
-
-
使用场景:
-
数据更新后需要刷新界面。
-
响应用户输入(如鼠标点击后更新选中状态)。
-
5.3.2、repaint():请求同步重绘控件,立即调用 paintEvent()。
-
特点:
-
同步操作,会立刻触发重绘,可能导致性能开销。
-
不会合并多次调用,每次调用都会触发一次完整重绘。
-
适合需要立即刷新的场景,但应谨慎使用以避免频繁调用。
-
-
使用场景:
-
实时动画或快速更新的图形(如动态波形图)。
-
需要立即看到绘制结果的调试场景。
-
5.3.3、update() vs repaint()
| 特性 | update() | repaint() |
|---|---|---|
| 调用方式 | 异步,放入事件队列 | 同步,立即执行 |
| 性能 | 高,合并多次调用 | 较低,每次调用都重绘 |
| 使用场景 | 常规更新,响应用户交互 | 实时动画,立即刷新 |
| 延迟 | 可能有微小延迟 | 无延迟 |
5.4、双缓冲绘图以减少闪烁
闪烁(flickering)是指控件重绘时出现短暂的空白或不完整画面,通常在复杂绘图或频繁更新时发生。
5.4.1、双缓冲原理
-
双缓冲:在内存中维护一个与控件大小相同的缓冲区(后台缓冲),所有绘图操作先在缓冲区中完成,然后将缓冲区内容一次性绘制到屏幕上(前台缓冲)。
-
避免了绘图过程中屏幕上出现不完整的中间状态。
-
提高了绘图的流畅性,减少闪烁。
-
-
Qt 的 QWidget 默认启用双缓冲,开发者无需手动管理缓冲区。
5.4.2、默认双缓冲行为
-
在 paintEvent() 中,QPainter 自动将绘图操作应用到后台缓冲区。
-
绘制完成后,Qt 将后台缓冲区的内容复制到屏幕。
-
这一过程对开发者透明,无需额外代码。
5.4.3、优化双缓冲以减少闪烁
尽管 Qt 默认启用了双缓冲,但在某些情况下仍可能出现闪烁。以下是优化建议:
-
避免不必要的重绘:
-
使用 update() 而非 repaint(),以合并多次重绘请求。
-
仅在内容实际改变时调用 update(),避免重复调用。
-
-
限制重绘区域:
-
在 paintEvent() 中检查 QPaintEvent::rect() 或 QPaintEvent::region(),仅绘制受影响的区域。
-
使用 QPainter::setClipRect() 裁剪绘图区域。
-
-
使用 QPixmap 进行预渲染:
-
对于静态或变化不频繁的内容,预先将绘图结果渲染到 QPixmap,然后在 paintEvent() 中直接绘制 QPixmap。
-
适合复杂的图形或需要重复绘制的场景。
-
-
禁用不必要的背景擦除:
-
默认情况下,Qt 会在绘制前擦除控件的背景,可能导致闪烁。
-
通过设置 Qt::WA_OpaquePaintEvent 属性,告诉 Qt 控件会完全绘制其区域,无需擦除背景。
-
-
启用硬件加速:
-
如果绘图性能瓶颈明显,可考虑使用 QOpenGLWidget 替代 QWidget,利用 GPU 加速渲染。
-
注意硬件加速可能增加复杂性,需确保目标平台支持 OpenGL。
-
5.4.4、示例代码
QWidget 的 paintEvent():
-
重写了 paintEvent(),用于绘制静态内容(鼠标点击的圆点、网格线)和动态内容(旋转矩形)。
-
使用 QPainter 进行绘图,结合 setClipRect() 限制重绘区域,优化性能。
在 paintEvent() 中使用 QPainter:
-
启用了抗锯齿(setRenderHint(QPainter::Antialiasing))以提高绘制质量。
-
使用 translate() 和 rotate() 实现旋转矩形的动画效果。
-
绘制多种元素:圆点(drawEllipse)、矩形(drawRect)、文本(drawText)、网格线(drawLine)。
更新控件:update(), repaint():
-
鼠标点击时调用 update(),异步更新静态内容(圆点)。
-
定时器触发时调用 repaint(),同步更新动态内容(旋转矩形),确保动画流畅。
-
通过检查数据变化(m_buffer.isNull())避免不必要的重绘。
双缓冲绘图以减少闪烁:
-
使用 QPixmap m_buffer 作为后台缓冲区,预渲染静态内容(圆点和网格线)。
-
在 paintEvent() 中,将缓冲区内容绘制到控件上,仅更新受影响区域(event->rect())。
-
设置 Qt::WA_OpaquePaintEvent 属性,禁用背景擦除,减少闪烁。
-
控件大小变化时(resizeEvent),清空缓冲区以适应新尺寸。
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QMouseEvent>
#include <QTimer>
#include <QList>
#include <QPoint>
class CustomWidget : public QWidget {
Q_OBJECT
public:
CustomWidget(QWidget *parent = nullptr) : QWidget(parent) {
// 启用双缓冲和优化属性
setAttribute(Qt::WA_OpaquePaintEvent); // 禁用背景擦除,减少闪烁
setMouseTracking(true); // 启用鼠标跟踪
// 启动定时器,每50ms更新一次,用于动画
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &CustomWidget::onTimerUpdate);
timer->start(50);
// 初始化旋转角度
m_angle = 0;
// 初始化缓冲区
m_buffer = QPixmap();
}
protected:
void paintEvent(QPaintEvent *event) override {
// 如果缓冲区无效或大小不匹配,重新绘制
if (m_buffer.isNull() || m_buffer.size() != size()) {
m_buffer = QPixmap(size());
m_buffer.fill(Qt::white); // 设置背景色
QPainter bufferPainter(&m_buffer);
bufferPainter.setRenderHint(QPainter::Antialiasing); // 启用抗锯齿
// 绘制静态内容:鼠标点击的圆点
bufferPainter.setPen(Qt::blue);
bufferPainter.setBrush(Qt::yellow);
for (const QPoint &point : m_points) {
bufferPainter.drawEllipse(point, 5, 5);
}
// 绘制网格线(示例静态内容)
bufferPainter.setPen(QPen(Qt::gray, 1, Qt::DashLine));
for (int x = 0; x < width(); x += 50) {
bufferPainter.drawLine(x, 0, x, height());
}
for (int y = 0; y < height(); y += 50) {
bufferPainter.drawLine(0, y, width(), y);
}
}
// 在控件上绘制缓冲区内容
QPainter painter(this);
painter.setClipRect(event->rect()); // 限制绘制区域,优化性能
painter.drawPixmap(event->rect(), m_buffer, event->rect());
// 绘制动态内容:旋转矩形(不使用缓冲区)
painter.setRenderHint(QPainter::Antialiasing);
painter.translate(width() / 2, height() / 2); // 平移到控件中心
painter.rotate(m_angle); // 应用旋转
painter.setPen(Qt::red);
painter.setBrush(Qt::green);
painter.drawRect(-25, -25, 50, 50); // 绘制旋转矩形
// 绘制提示文本
painter.resetTransform(); // 重置变换
painter.setPen(Qt::black);
painter.drawText(10, 20, "Click to add points, animation runs automatically");
}
void mousePressEvent(QMouseEvent *event) override {
// 记录鼠标点击位置并请求重绘
if (event->button() == Qt::LeftButton) {
m_points.append(event->pos());
m_buffer = QPixmap(); // 清空缓冲区,强制重新绘制
update(); // 异步更新
}
}
void resizeEvent(QResizeEvent *event) override {
// 控件大小变化时清空缓冲区
m_buffer = QPixmap();
update();
}
private slots:
void onTimerUpdate() {
// 更新旋转角度并立即重绘(适合动画)
m_angle += 5;
if (m_angle >= 360) m_angle -= 360;
repaint(); // 同步更新以确保动画流畅
}
private:
QList<QPoint> m_points; // 存储鼠标点击位置
QPixmap m_buffer; // 双缓冲区
qreal m_angle; // 旋转角度
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建并显示自定义控件
CustomWidget widget;
widget.setWindowTitle("Custom Drawing Example");
widget.resize(400, 300);
widget.show();
return app.exec();
}
#include "main.moc" // 包含 moc 文件以支持 Qt 的信号槽机制
三. 图像处理
支持图像创建、操作和文件读写。
-
QImage: 像素级图像操作,适合图像处理。
-
QPixmap: 优化用于屏幕显示的图像。
-
QBitmap: 单色位图。
-
QPicture: 记录和重放绘图指令。
-
QImageReader, QImageWriter: 图像文件读写(支持 PNG、JPEG 等格式)。
-
QMovie: 动态图像播放(如 GIF)。
-
QImageIOHandler: 自定义图像格式处理。
-
QImageEncoderSettings, QImageDecoder (Qt 6): 图像编码/解码设置。
四. 字体和文本
提供字体管理和低级文本渲染功能。
-
QFont: 字体属性管理(大小、粗细、样式)。
-
QFontMetrics, QFontMetricsF: 字体度量,计算文本尺寸。
-
QFontDatabase: 系统字体资源访问。
-
QTextLayout, QTextOption: 高级文本布局和格式化。
-
QRawFont: 直接访问字体文件数据。
-
QTextFragment, QTextBlock: 低级文本结构(用于复杂文本渲染)。
-
QGlyphRun: 字形级文本渲染。
五. 事件和输入处理
处理 GUI 相关的用户输入和交互事件。
-
QMouseEvent: 鼠标点击、移动事件。
-
QHoverEvent: 鼠标悬停事件。
-
QWheelEvent: 鼠标滚轮事件。
-
QKeyEvent: 键盘输入事件。
-
QTouchEvent: 触摸输入事件。
-
QNativeGestureEvent: 平台特定手势(如 macOS 手势)。
-
QInputMethod, QInputMethodEvent: 输入法支持(虚拟键盘、语言切换)。
-
QDrag, QDropEvent: 拖放操作。
-
QClipboard: 剪贴板访问。
-
QTabletEvent: 数位板输入事件。
-
QEnterEvent (Qt 6): 鼠标进入/离开事件。
-
QExposeEvent: 窗口暴露事件。
-
QPlatformDrag: 平台特定的拖放实现。
六. OpenGL 和硬件加速
支持 OpenGL 和 Vulkan 渲染,适用于高性能图形。
-
QOpenGLContext: OpenGL 上下文管理。
-
QOpenGLFunctions, QOpenGLExtraFunctions: OpenGL API 封装。
-
QOpenGLFramebufferObject: 帧缓冲对象,用于离屏渲染。
-
QOpenGLShader, QOpenGLShaderProgram: 着色器支持。
-
QOpenGLTexture: 纹理管理。
-
QOpenGLBuffer: 顶点和索引缓冲区。
-
QOpenGLVertexArrayObject: 顶点数组对象。
-
QOpenGLTimerQuery, QOpenGLTimeMonitor: OpenGL 性能监控。
-
QAbstractOpenGLFunctions (Qt 6): 抽象化的 OpenGL 函数接口。
-
QVulkanInstance, QVulkanWindow (Qt 6): Vulkan 渲染支持。
七. 颜色和外观
管理颜色和外观设置。
-
QColor: 颜色表示(支持 RGB、HSV、CMYK)。
-
QPalette: 颜色方案管理(前景、背景等)。
-
QColorSpace (Qt 6): 颜色空间管理(支持 ICC 配置文件)。
-
QColormap: 颜色映射(主要用于旧平台)。
八. 图标和光标
支持图标和鼠标光标管理。
-
QIcon: 图标管理,支持多分辨率和状态。
-
QCursor: 鼠标光标样式和自定义形状。
-
QIconEngine: 自定义图标渲染引擎。
九. 平台和渲染后端
提供平台特定集成和渲染后端支持。
-
QPlatformIntegration: 平台特定的窗口系统集成(Windows、X11、Wayland 等)。
-
QRasterPaintEngine: 软件光栅化渲染引擎。
-
QPlatformSurface: 平台特定的渲染表面。
-
QPlatformTheme: 平台主题(如按钮样式、对话框风格)。
-
QPlatformGraphicsBuffer: 平台特定的图形缓冲区。
-
QPlatformSharedGraphicsCache: 共享图形缓存,加速渲染。
十. 国际化(GUI 相关)
支持 GUI 相关的字符编码和区域设置。
-
QTextCodec(部分):字符编码支持(仅限 GUI 文本显示)。
-
QLocale(部分):区域设置(仅限 GUI 格式,如日期、数字显示)。
&spm=1001.2101.3001.5002&articleId=147340081&d=1&t=3&u=550503dd637849bd932a9e6b7a3ca912)
6204

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



