Qt C++ 图形绘制完全指南:从基础到进阶实战
QPainter:执行绘制操作的主要类:绘制设备的抽象基类:定义QPainter如何在特定平台绘制QWidget:最常用的绘制设备:图像绘制设备Q_OBJECTpublic:protected:private:setWindowTitle("Qt图形绘制示例");// 设置定时器用于动画// 20 FPS// 设置白色背景// 绘制各种图形// 绘制标题。
Qt C++ 图形绘制完全指南:从基础到进阶实战
前言
Qt框架提供了强大的2D图形绘制能力,通过QPainter类及其相关组件,开发者可以轻松实现各种复杂的图形绘制需求。本文将系统介绍Qt图形绘制的核心技术,并通过实例代码演示各种绘制技巧。
一、Qt图形绘制基础架构
1.1 核心类介绍
Qt图形绘制主要涉及以下核心类:
- QPainter:执行绘制操作的主要类
- QPaintDevice:绘制设备的抽象基类
- QPaintEngine:定义QPainter如何在特定平台绘制
- QWidget:最常用的绘制设备
- QPixmap/QImage:图像绘制设备
1.2 绘制系统架构图
┌─────────────┐
│ QPainter │ ← 绘制工具
└──────┬──────┘
│
┌──────▼──────┐
│QPaintDevice │ ← 绘制表面
└──────┬──────┘
│
┌──────▼──────┐
│QPaintEngine │ ← 底层引擎
└─────────────┘
二、基础图形绘制实战
2.1 创建自定义绘制Widget
首先创建一个自定义Widget类,重写paintEvent方法:
DrawWidget.h
#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H
#include <QWidget>
#include <QPainter>
#include <QPaintEvent>
#include <QTimer>
#include <cmath>
class DrawWidget : public QWidget
{
Q_OBJECT
public:
explicit DrawWidget(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *event) override;
private slots:
void updateAnimation();
private:
int m_animationAngle;
QTimer *m_timer;
void drawBasicShapes(QPainter &painter);
void drawGradients(QPainter &painter);
void drawTransformations(QPainter &painter);
void drawPath(QPainter &painter);
void drawText(QPainter &painter);
};
#endif // DRAWWIDGET_H
DrawWidget.cpp
#include "DrawWidget.h"
#include <QLinearGradient>
#include <QRadialGradient>
#include <QPainterPath>
#include <QFont>
DrawWidget::DrawWidget(QWidget *parent)
: QWidget(parent), m_animationAngle(0)
{
setFixedSize(800, 600);
setWindowTitle("Qt图形绘制示例");
// 设置定时器用于动画
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &DrawWidget::updateAnimation);
m_timer->start(50); // 20 FPS
}
void DrawWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
// 设置白色背景
painter.fillRect(rect(), Qt::white);
// 绘制各种图形
drawBasicShapes(painter);
drawGradients(painter);
drawTransformations(painter);
drawPath(painter);
drawText(painter);
}
void DrawWidget::drawBasicShapes(QPainter &painter)
{
painter.save();
// 绘制标题
painter.setPen(QPen(Qt::black, 2));
painter.setFont(QFont("Arial", 12, QFont::Bold));
painter.drawText(20, 30, "基础图形绘制");
// 设置画笔和画刷
QPen pen(Qt::blue, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
painter.setPen(pen);
// 绘制直线
painter.drawLine(50, 50, 150, 50);
// 绘制矩形
painter.setBrush(QBrush(Qt::yellow, Qt::SolidPattern));
painter.drawRect(50, 70, 100, 60);
// 绘制圆角矩形
painter.setBrush(QBrush(Qt::cyan, Qt::Dense4Pattern));
painter.drawRoundedRect(170, 70, 100, 60, 15, 15);
// 绘制椭圆
painter.setBrush(QBrush(Qt::magenta, Qt::DiagCrossPattern));
painter.drawEllipse(290, 70, 100, 60);
// 绘制多边形
QPolygon polygon;
polygon << QPoint(420, 70) << QPoint(470, 70)
<< QPoint(495, 100) << QPoint(470, 130)
<< QPoint(420, 130) << QPoint(395, 100);
painter.setBrush(QBrush(Qt::green, Qt::CrossPattern));
painter.drawPolygon(polygon);
// 绘制弧形
painter.setBrush(Qt::NoBrush);
painter.setPen(QPen(Qt::red, 3));
painter.drawArc(520, 70, 80, 60, 30 * 16, 120 * 16);
// 绘制扇形
painter.setBrush(QBrush(Qt::darkCyan));
painter.drawPie(620, 70, 80, 60, 45 * 16, 90 * 16);
painter.restore();
}
void DrawWidget::drawGradients(QPainter &painter)
{
painter.save();
// 绘制标题
painter.setPen(QPen(Qt::black, 2));
painter.setFont(QFont("Arial", 12, QFont::Bold));
painter.drawText(20, 180, "渐变效果");
// 线性渐变
QLinearGradient linearGrad(50, 200, 150, 260);
linearGrad.setColorAt(0, Qt::red);
linearGrad.setColorAt(0.5, Qt::yellow);
linearGrad.setColorAt(1, Qt::green);
painter.setBrush(linearGrad);
painter.drawRect(50, 200, 100, 60);
// 径向渐变
QRadialGradient radialGrad(220, 230, 50);
radialGrad.setColorAt(0, Qt::white);
radialGrad.setColorAt(0.5, Qt::cyan);
radialGrad.setColorAt(1, Qt::blue);
painter.setBrush(radialGrad);
painter.drawEllipse(170, 200, 100, 60);
// 锥形渐变
QConicalGradient conicalGrad(320, 230, 0);
conicalGrad.setColorAt(0, Qt::red);
conicalGrad.setColorAt(0.25, Qt::yellow);
conicalGrad.setColorAt(0.5, Qt::green);
conicalGrad.setColorAt(0.75, Qt::blue);
conicalGrad.setColorAt(1, Qt::red);
painter.setBrush(conicalGrad);
painter.drawEllipse(290, 200, 60, 60);
painter.restore();
}
void DrawWidget::drawTransformations(QPainter &painter)
{
painter.save();
// 绘制标题
painter.setPen(QPen(Qt::black, 2));
painter.setFont(QFont("Arial", 12, QFont::Bold));
painter.drawText(20, 310, "变换效果(动画)");
// 保存原始状态
painter.save();
// 平移到旋转中心
painter.translate(100, 360);
// 应用旋转(动画)
painter.rotate(m_animationAngle);
// 绘制旋转的矩形
painter.setBrush(QBrush(Qt::darkBlue));
painter.drawRect(-30, -20, 60, 40);
painter.restore();
// 缩放效果
painter.save();
painter.translate(220, 360);
double scale = 1.0 + 0.5 * sin(m_animationAngle * M_PI / 180.0);
painter.scale(scale, scale);
painter.setBrush(QBrush(Qt::darkGreen));
painter.drawEllipse(-30, -30, 60, 60);
painter.restore();
// 错切效果
painter.save();
painter.translate(340, 360);
painter.shear(0.5 * sin(m_animationAngle * M_PI / 180.0), 0);
painter.setBrush(QBrush(Qt::darkRed));
painter.drawRect(-30, -20, 60, 40);
painter.restore();
painter.restore();
}
void DrawWidget::drawPath(QPainter &painter)
{
painter.save();
// 绘制标题
painter.setPen(QPen(Qt::black, 2));
painter.setFont(QFont("Arial", 12, QFont::Bold));
painter.drawText(20, 440, "路径绘制");
// 创建复杂路径
QPainterPath path;
path.moveTo(50, 460);
path.lineTo(100, 460);
path.cubicTo(120, 440, 140, 480, 160, 460);
path.lineTo(200, 460);
path.quadTo(220, 480, 240, 460);
path.arcTo(240, 440, 40, 40, 0, 180);
// 绘制路径
painter.setPen(QPen(Qt::darkMagenta, 3));
painter.setBrush(QBrush(Qt::lightGray));
painter.drawPath(path);
// 创建星形路径
QPainterPath starPath;
int starX = 400, starY = 470;
for (int i = 0; i < 5; ++i) {
double angle = i * 72 * M_PI / 180.0 - M_PI / 2;
double x = starX + 30 * cos(angle);
double y = starY + 30 * sin(angle);
if (i == 0) {
starPath.moveTo(x, y);
} else {
starPath.lineTo(x, y);
}
angle = (i * 72 + 36) * M_PI / 180.0 - M_PI / 2;
x = starX + 15 * cos(angle);
y = starY + 15 * sin(angle);
starPath.lineTo(x, y);
}
starPath.closeSubpath();
painter.setPen(QPen(Qt::darkYellow, 2));
painter.setBrush(QBrush(Qt::yellow));
painter.drawPath(starPath);
painter.restore();
}
void DrawWidget::drawText(QPainter &painter)
{
painter.save();
// 绘制标题
painter.setPen(QPen(Qt::black, 2));
painter.setFont(QFont("Arial", 12, QFont::Bold));
painter.drawText(20, 540, "文字绘制");
// 普通文字
painter.setFont(QFont("Times New Roman", 14));
painter.setPen(Qt::darkBlue);
painter.drawText(50, 565, "Hello Qt Graphics!");
// 带阴影的文字
painter.setFont(QFont("Arial", 16, QFont::Bold));
painter.setPen(Qt::gray);
painter.drawText(202, 567, "Shadow Text");
painter.setPen(Qt::black);
painter.drawText(200, 565, "Shadow Text");
// 旋转文字
painter.save();
painter.translate(400, 560);
painter.rotate(-30);
painter.setFont(QFont("Courier", 14));
painter.setPen(Qt::darkGreen);
painter.drawText(0, 0, "Rotated Text");
painter.restore();
// 渐变文字
QLinearGradient textGrad(500, 550, 600, 570);
textGrad.setColorAt(0, Qt::blue);
textGrad.setColorAt(1, Qt::red);
painter.setPen(QPen(QBrush(textGrad), 2));
painter.setFont(QFont("Arial", 18, QFont::Bold));
painter.drawText(500, 565, "Gradient");
painter.restore();
}
void DrawWidget::updateAnimation()
{
m_animationAngle = (m_animationAngle + 5) % 360;
update(); // 触发重绘
}
2.2 主窗口实现
main.cpp
#include <QApplication>
#include "DrawWidget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
DrawWidget widget;
widget.show();
return app.exec();
}
三、高级绘制技术
3.1 双缓冲绘制
为了避免闪烁,Qt默认启用双缓冲。也可以手动实现:
void CustomWidget::paintEvent(QPaintEvent *event)
{
QPixmap pixmap(size());
pixmap.fill(Qt::white);
QPainter pixmapPainter(&pixmap);
pixmapPainter.setRenderHint(QPainter::Antialiasing);
// 在pixmap上绘制
drawContent(pixmapPainter);
// 将pixmap绘制到widget
QPainter widgetPainter(this);
widgetPainter.drawPixmap(0, 0, pixmap);
}
3.2 图形项框架 (Graphics View Framework)
对于复杂的图形场景,使用Graphics View框架更合适:
// 场景图形项
class CustomItem : public QGraphicsItem
{
public:
CustomItem()
{
setFlag(ItemIsMovable);
setFlag(ItemIsSelectable);
}
QRectF boundingRect() const override
{
return QRectF(-50, -50, 100, 100);
}
void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget) override
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setBrush(isSelected() ? Qt::yellow : Qt::lightBlue);
painter->setPen(QPen(Qt::black, 2));
painter->drawEllipse(boundingRect());
}
};
// 使用场景
QGraphicsScene *scene = new QGraphicsScene();
scene->addItem(new CustomItem());
QGraphicsView *view = new QGraphicsView(scene);
view->setRenderHint(QPainter::Antialiasing);
view->show();
3.3 OpenGL集成
Qt支持OpenGL绘制,可以实现高性能3D图形:
class OpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
protected:
void initializeGL() override
{
initializeOpenGLFunctions();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
void paintGL() override
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// OpenGL绘制代码
}
void resizeGL(int w, int h) override
{
glViewport(0, 0, w, h);
}
};
四、性能优化技巧
4.1 绘制优化策略
- 减少重绘区域
void Widget::updatePartial()
{
// 只更新特定区域
update(QRect(10, 10, 100, 100));
}
- 使用绘制缓存
class CachedWidget : public QWidget
{
private:
QPixmap m_cache;
bool m_cacheValid = false;
protected:
void paintEvent(QPaintEvent *event) override
{
if (!m_cacheValid) {
m_cache = QPixmap(size());
QPainter cachePainter(&m_cache);
drawComplexContent(cachePainter);
m_cacheValid = true;
}
QPainter painter(this);
painter.drawPixmap(0, 0, m_cache);
}
};
- 批量绘制操作
// 低效方式
for (int i = 0; i < 1000; ++i) {
painter.drawPoint(points[i]);
}
// 高效方式
painter.drawPoints(points.data(), 1000);
4.2 渲染提示设置
painter.setRenderHint(QPainter::Antialiasing, true); // 抗锯齿
painter.setRenderHint(QPainter::TextAntialiasing, true); // 文字抗锯齿
painter.setRenderHint(QPainter::SmoothPixmapTransform, true); // 平滑变换
五、实战案例:自定义图表控件
5.1 简单饼图实现
class PieChart : public QWidget
{
private:
QVector<QPair<QString, double>> m_data;
QVector<QColor> m_colors;
public:
void setData(const QVector<QPair<QString, double>> &data)
{
m_data = data;
generateColors();
update();
}
protected:
void paintEvent(QPaintEvent *event) override
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QRect pieRect = rect().adjusted(20, 20, -20, -20);
double total = 0;
for (const auto &item : m_data) {
total += item.second;
}
int startAngle = 0;
for (int i = 0; i < m_data.size(); ++i) {
int spanAngle = static_cast<int>(
m_data[i].second / total * 360 * 16
);
painter.setBrush(m_colors[i]);
painter.drawPie(pieRect, startAngle, spanAngle);
startAngle += spanAngle;
}
}
private:
void generateColors()
{
m_colors.clear();
for (int i = 0; i < m_data.size(); ++i) {
m_colors.append(QColor::fromHsv(
i * 360 / m_data.size(), 200, 200
));
}
}
};
六、常见问题与解决方案
6.1 绘制闪烁问题
问题:快速更新时界面闪烁
解决方案:
- 启用双缓冲(Qt默认启用)
- 使用
setAttribute(Qt::WA_OpaquePaintEvent)
- 避免在paintEvent中进行复杂计算
6.2 坐标系统混淆
问题:绘制位置不正确
解决方案:
// 理解Qt坐标系统
// (0,0) 在左上角
// x轴向右增长
// y轴向下增长
// 坐标变换
painter.translate(100, 100); // 移动原点
painter.scale(2.0, 2.0); // 缩放
painter.rotate(45); // 旋转(顺时针)
6.3 性能问题
问题:绘制大量图形时卡顿
解决方案:
- 使用Graphics View框架
- 实现图形项的LOD(细节层次)
- 使用OpenGL加速
- 裁剪不可见区域
七、最佳实践总结
-
合理选择绘制方式
- 简单绘制:重写paintEvent
- 复杂场景:Graphics View框架
- 3D/高性能:OpenGL
-
性能优化原则
- 最小化重绘区域
- 缓存复杂绘制结果
- 批量处理绘制操作
-
代码组织建议
- 分离绘制逻辑和业务逻辑
- 使用绘制状态的保存/恢复
- 合理使用渲染提示
八、项目配置
.pro文件配置
QT += core gui widgets
CONFIG += c++11
TARGET = QtGraphicsDemo
TEMPLATE = app
SOURCES += \
main.cpp \
DrawWidget.cpp
HEADERS += \
DrawWidget.h
# 如果使用OpenGL
# QT += opengl
总结
本文系统介绍了Qt C++图形绘制的核心技术,从基础的QPainter使用到高级的Graphics View框架和OpenGL集成。通过实际代码示例,展示了各种绘制技巧和优化方法。掌握这些技术,可以开发出功能丰富、性能优异的图形应用程序。
Qt的图形系统非常强大和灵活,本文只是涵盖了最常用的部分。建议读者在实际项目中根据具体需求选择合适的技术方案,并持续关注Qt的最新特性和最佳实践。
参考资源
作者: Qt开发者
发布时间: 2024
标签: Qt, C++, 图形绘制, QPainter, Graphics View
更多推荐
所有评论(0)