网站首页 > 教程分享 正文
QPainter概述
QPainter是Qt提供的2D图形绘制API,它为开发者提供了一个统一的跨平台绘图接口,相比MFC的CDC绘图系统更加强大和易用。对于从MFC迁移的开发者,QPainter将是一个显著的升级。
与MFC绘图系统对比
特性 | Qt (QPainter) | MFC (CDC) |
基本概念 | QPainter对象 | CDC设备上下文 |
坐标系统 | 默认逻辑坐标,支持变换 | 设备坐标,通过映射模式转换 |
绘图表面 | QWidget, QPixmap, QImage等 | HWND, HDC, HBITMAP等 |
抗锯齿支持 | 原生支持 | 有限或无支持(GDI+除外) |
透明度支持 | 完全支持alpha通道 | 有限或无支持(GDI+除外) |
平台依赖性 | 跨平台统一API | 仅限Windows平台 |
渲染引擎 | 可选后端 (软件, OpenGL, Direct3D等) | GDI/GDI+ |
图形保存 | 多种格式支持 | 需额外代码处理 |
打印集成 | 无缝集成QPrinter | 需区分打印DC和显示DC |
基础绘图操作
在窗口部件上绘图
在Qt中,通过重写QWidget的paintEvent()方法使用QPainter进行绘图:
class MyWidget : public QWidget
{
protected:
void paintEvent(QPaintEvent *event) override
{
QPainter painter(this);
// 启用抗锯齿
painter.setRenderHint(QPainter::Antialiasing);
// 设置画笔(轮廓)
painter.setPen(QPen(Qt::blue, 2, Qt::SolidLine, Qt::RoundCap));
// 设置画刷(填充)
painter.setBrush(QBrush(Qt::yellow, Qt::SolidPattern));
// 绘制矩形
painter.drawRect(20, 20, 100, 60);
// 绘制椭圆
painter.setPen(QPen(Qt::red, 3));
painter.setBrush(Qt::green);
painter.drawEllipse(150, 20, 100, 60);
// 绘制文本
painter.setPen(Qt::black);
painter.setFont(QFont("Arial", 12, QFont::Bold));
painter.drawText(rect(), Qt::AlignCenter, "Hello QPainter!");
}
};
绘图表面比较
Qt支持多种绘图表面,每种都有不同的用途:
- QWidget - 绘制到屏幕上的窗口部件
- QPixmap - 针对屏幕优化的图像
- QImage - 用于访问单个像素和图像处理
- QPicture - 记录和重放QPainter命令
- QPrinter - 打印输出
- QOpenGLPaintDevice - OpenGL加速绘图
// 在QPixmap上绘制
QPixmap pixmap(400, 200);
pixmap.fill(Qt::white);
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(Qt::blue, 2));
painter.drawRect(20, 20, 100, 60);
painter.end();
// 保存图像
pixmap.save("output.png");
// 在QImage上绘制
QImage image(400, 200, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter imagePainter(&image);
imagePainter.setRenderHint(QPainter::Antialiasing);
imagePainter.setPen(QPen(Qt::blue, 2));
imagePainter.drawRect(20, 20, 100, 60);
imagePainter.end();
// 访问单个像素
image.setPixel(10, 10, qRgb(255, 0, 0));
// 在QPicture上记录
QPicture picture;
QPainter picturePainter(&picture);
picturePainter.setRenderHint(QPainter::Antialiasing);
picturePainter.setPen(QPen(Qt::blue, 2));
picturePainter.drawRect(20, 20, 100, 60);
picturePainter.end();
// 保存到文件
picture.save("drawing.pic");
// 在其他地方重放
QPicture loadedPicture;
loadedPicture.load("drawing.pic");
QPainter replayPainter(this);
loadedPicture.play(&replayPainter);
绘图元素与工具
QPen - 控制线条样式
QPen用于定义如何绘制线条和形状轮廓:
// 创建各种样式的画笔
QPen pen1(Qt::red, 2, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin);
QPen pen2(QColor(30, 100, 160), 3, Qt::DashLine, Qt::SquareCap, Qt::BevelJoin);
QPen pen3(QColor(0, 150, 0, 100), 4); // 半透明绿色
// 设置更多属性
pen3.setDashPattern({5, 2, 1, 2}); // 自定义虚线模式
pen3.setCosmetic(true); // 与设备无关的宽度
// 使用画笔
painter.setPen(pen1);
painter.drawLine(10, 10, 100, 10);
painter.setPen(pen2);
painter.drawLine(10, 30, 100, 30);
painter.setPen(pen3);
painter.drawLine(10, 50, 100, 50);
QBrush - 控制填充样式
QBrush用于定义如何填充形状内部:
// 创建各种样式的画刷
QBrush brush1(Qt::red, Qt::SolidPattern);
QBrush brush2(Qt::blue, Qt::DiagCrossPattern);
// 纹理画刷
QPixmap texture(":/textures/wood.png");
QBrush brush3(texture);
// 渐变画刷
QLinearGradient gradient(0, 0, 100, 100);
gradient.setColorAt(0, Qt::white);
gradient.setColorAt(0.5, Qt::green);
gradient.setColorAt(1, Qt::black);
QBrush brush4(gradient);
// 使用画刷
painter.setBrush(brush1);
painter.drawRect(10, 10, 80, 60);
painter.setBrush(brush2);
painter.drawRect(100, 10, 80, 60);
painter.setBrush(brush3);
painter.drawRect(10, 80, 80, 60);
painter.setBrush(brush4);
painter.drawRect(100, 80, 80, 60);
渐变效果
Qt支持多种渐变类型,这是MFC中难以实现的功能:
// 线性渐变
QLinearGradient linearGradient(0, 0, 300, 0);
linearGradient.setColorAt(0, Qt::white);
linearGradient.setColorAt(1, Qt::blue);
linearGradient.setSpread(QGradient::ReflectSpread); // 反射扩展模式
painter.fillRect(10, 10, 300, 50, linearGradient);
// 径向渐变
QRadialGradient radialGradient(150, 100, 100);
radialGradient.setColorAt(0, Qt::yellow);
radialGradient.setColorAt(0.5, Qt::red);
radialGradient.setColorAt(1, Qt::black);
painter.fillRect(10, 70, 300, 100, radialGradient);
// 锥形渐变
QConicalGradient conicalGradient(150, 220, 0);
conicalGradient.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.fillRect(10, 180, 300, 100, conicalGradient);
路径绘制 (QPainterPath)
QPainterPath用于创建复杂的形状:
QPainterPath path;
path.moveTo(100, 30);
path.cubicTo(150, 30, 200, 100, 100, 100);
path.cubicTo(0, 100, 50, 30, 100, 30);
painter.setPen(QPen(Qt::blue, 2));
painter.setBrush(Qt::green);
painter.drawPath(path);
// 创建文本路径
QPainterPath textPath;
textPath.addText(QPointF(10, 50), QFont("Times", 40), "Qt 路径");
painter.setPen(QPen(Qt::black, 1));
painter.setBrush(QBrush(Qt::red));
painter.drawPath(textPath);
// 组合路径
QPainterPath combinedPath;
combinedPath.addRect(10, 10, 100, 100);
combinedPath.addEllipse(50, 50, 80, 80);
combinedPath.setFillRule(Qt::OddEvenFill); // 设置填充规则
painter.setPen(QPen(Qt::black, 1));
painter.setBrush(QBrush(Qt::cyan));
painter.drawPath(combinedPath);
坐标变换与裁剪
QPainter提供了强大的坐标变换功能,远超MFC的基本映射模式:
// 保存当前状态
painter.save();
// 平移坐标系
painter.translate(100, 50);
// 旋转坐标系(角度)
painter.rotate(45);
// 缩放坐标系
painter.scale(0.5, 0.5);
// 在变换的坐标系中绘制
painter.drawRect(0, 0, 100, 100);
// 恢复之前的状态
painter.restore();
// 设置自定义变换矩阵
QTransform transform;
transform.translate(200, 200);
transform.rotate(30);
transform.scale(1.5, 0.75);
transform.shear(0.1, 0.2);
painter.setTransform(transform);
painter.drawEllipse(0, 0, 100, 100);
// 复合变换
painter.resetTransform();
painter.translate(300, 100);
painter.rotate(90);
painter.drawText(0, 0, "旋转的文本");
裁剪区域
设置裁剪区域来限制绘图范围:
// 矩形裁剪
painter.setClipRect(QRect(50, 50, 200, 200));
// 路径裁剪
QPainterPath clipPath;
clipPath.addEllipse(100, 100, 150, 100);
painter.setClipPath(clipPath);
// 组合裁剪区域
painter.setClipRect(QRect(50, 50, 300, 200), Qt::IntersectClip);
// 禁用裁剪
painter.setClipping(false);
图像处理与效果
QPainter可以绘制和处理图像:
// 加载图像
QImage image(":/images/photo.jpg");
// 绘制图像
painter.drawImage(10, 10, image);
// 缩放绘制
painter.drawImage(QRect(10, 120, 100, 75), image);
// 裁剪绘制
painter.drawImage(QPoint(120, 10), image,
QRect(image.width()/4, image.height()/4,
image.width()/2, image.height()/2));
// 应用滤镜效果
QImage processedImage = image.convertToFormat(QImage::Format_Grayscale8);
painter.drawImage(120, 120, processedImage);
合成模式
设置合成模式改变绘图元素如何与下层内容混合:
// 绘制背景
painter.fillRect(0, 0, 300, 200, Qt::white);
painter.fillRect(50, 50, 200, 100, Qt::blue);
// 默认合成模式:Source Over
painter.fillRect(100, 70, 150, 80, QColor(255, 0, 0, 128));
// 不同的合成模式
painter.setCompositionMode(QPainter::CompositionMode_Multiply);
painter.fillRect(200, 20, 80, 80, QColor(0, 255, 0, 128));
painter.setCompositionMode(QPainter::CompositionMode_Screen);
painter.fillRect(20, 100, 80, 80, QColor(0, 0, 255, 128));
painter.setCompositionMode(QPainter::CompositionMode_Difference);
painter.fillRect(150, 120, 80, 80, QColor(255, 255, 0, 255));
与MFC绘图的对比示例
为了帮助MFC开发者更好地理解转换过程,这里展示常见MFC绘图代码及其Qt等效实现:
基本形状绘制
// MFC代码
void CMyView::OnDraw(CDC* pDC)
{
// 设置画笔和画刷
CPen pen(PS_SOLID, 2, RGB(0, 0, 255));
CBrush brush(RGB(255, 255, 0));
CPen* pOldPen = pDC->SelectObject(&pen);
CBrush* pOldBrush = pDC->SelectObject(&brush);
// 绘制矩形
pDC->Rectangle(20, 20, 120, 80);
// 绘制椭圆
pDC->Ellipse(150, 20, 250, 80);
// 绘制线条
pDC->MoveTo(20, 100);
pDC->LineTo(250, 150);
// 绘制文本
pDC->SetTextColor(RGB(0, 0, 0));
pDC->SetBkMode(TRANSPARENT);
pDC->TextOut(100, 200, _T("Hello MFC!"), 10);
// 恢复GDI对象
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldBrush);
}
// Qt等效代码
void MyWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 设置画笔和画刷
painter.setPen(QPen(Qt::blue, 2));
painter.setBrush(Qt::yellow);
// 绘制矩形
painter.drawRect(20, 20, 100, 60);
// 绘制椭圆
painter.drawEllipse(150, 20, 100, 60);
// 绘制线条
painter.drawLine(20, 100, 250, 150);
// 绘制文本
painter.setPen(Qt::black);
painter.setBackgroundMode(Qt::TransparentMode);
painter.drawText(100, 200, "Hello Qt!");
}
坐标变换
// MFC代码
void CMyView::OnDraw(CDC* pDC)
{
// 保存当前状态
int nSavedDC = pDC->SaveDC();
// 设置映射模式
pDC->SetMapMode(MM_ANISOTROPIC);
// 设置窗口和视区范围
pDC->SetWindowExt(100, 100);
pDC->SetViewportExt(200, -200); // Y轴反向
pDC->SetViewportOrg(100, 300); // 原点偏移
// 在变换后坐标系绘制
pDC->Rectangle(0, 0, 50, 50);
// 恢复状态
pDC->RestoreDC(nSavedDC);
}
// Qt等效代码
void MyWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 保存当前状态
painter.save();
// 设置变换
painter.translate(100, 300);
painter.scale(2.0, -2.0); // Y轴反向
// 在变换后坐标系绘制
painter.drawRect(0, 0, 50, 50);
// 恢复状态
painter.restore();
}
双缓冲绘图
// MFC双缓冲绘图
void CMyView::OnDraw(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
// 创建内存DC和位图
CDC memDC;
memDC.CreateCompatibleDC(pDC);
CBitmap memBitmap;
memBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
CBitmap* pOldBitmap = memDC.SelectObject(&memBitmap);
// 在内存DC上绘制
memDC.FillSolidRect(rect, RGB(255, 255, 255)); // 清背景
// 实际绘图代码...
memDC.Rectangle(20, 20, 120, 80);
// 复制到屏幕
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
// 清理
memDC.SelectObject(pOldBitmap);
}
// Qt中无需手动双缓冲
void MyWidget::paintEvent(QPaintEvent *)
{
// Qt自动处理双缓冲,无需额外代码
QPainter painter(this);
// 直接绘制
painter.fillRect(rect(), Qt::white); // 清背景
painter.drawRect(20, 20, 100, 60);
}
高级绘图技术
抗锯齿设置
void MyWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 不使用抗锯齿
painter.setRenderHint(QPainter::Antialiasing, false);
painter.drawEllipse(10, 10, 100, 100);
// 使用抗锯齿
painter.setRenderHint(QPainter::Antialiasing, true);
painter.drawEllipse(120, 10, 100, 100);
// 文本抗锯齿
painter.setRenderHint(QPainter::TextAntialiasing, true);
painter.setFont(QFont("Arial", 24));
painter.drawText(10, 150, "抗锯齿文本");
// 多种渲染提示
painter.setRenderHints(
QPainter::Antialiasing |
QPainter::SmoothPixmapTransform |
QPainter::TextAntialiasing,
true);
painter.drawEllipse(230, 10, 100, 100);
}
性能优化技巧
void MyWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// 1. 仅重绘更新区域
QRegion region = event->region();
foreach (const QRect &rect, region.rects()) {
// 仅绘制需要更新的矩形区域
painter.setClipRect(rect);
drawContents(&painter, rect);
}
// 2. 避免过度绘制
if (!isVisible())
return;
// 3. 使用不透明提示优化
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.fillRect(rect(), Qt::white); // 背景总是不透明
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
// 4. 关闭不需要的渲染提示
painter.setRenderHint(QPainter::Antialiasing,
needAntialiasing()); // 仅在需要时开启
// 5. 预先计算缩放、旋转等操作
QTransform transform;
transform.rotate(45);
painter.setWorldTransform(transform);
// 6. 使用缓存技术
if (m_cachedDrawing.isNull()) {
m_cachedDrawing = QPixmap(200, 200);
QPainter cachePainter(&m_cachedDrawing);
drawComplexShape(&cachePainter);
}
// 绘制缓存的图像
painter.drawPixmap(100, 100, m_cachedDrawing);
}
打印支持
QPainter还可以无缝支持打印:
void MyWidget::print()
{
QPrinter printer(QPrinter::HighResolution);
printer.setPageSize(QPageSize(QPageSize::A4));
QPrintDialog printDialog(&printer, this);
if (printDialog.exec() == QDialog::Accepted) {
QPainter painter(&printer);
// 缩放到打印页面
QRect pageRect = printer.pageRect(QPrinter::DevicePixel).toRect();
QRect contentRect = rect();
qreal xscale = pageRect.width() / qreal(contentRect.width());
qreal yscale = pageRect.height() / qreal(contentRect.height());
qreal scale = qMin(xscale, yscale);
painter.translate(pageRect.width() / 2, pageRect.height() / 2);
painter.scale(scale, scale);
painter.translate(-contentRect.width() / 2, -contentRect.height() / 2);
// 绘制内容
render(&painter);
}
}
从MFC迁移的最佳实践
1. 绘图代码迁移策略
- 使用paintEvent()替代OnDraw()和OnPaint()
- 使用QPainter替代CDC
- 使用Qt的坐标系统(默认左上角为原点)
- 使用QPen和QBrush替代Windows GDI对象
- 使用Qt的render hints替代GDI+质量设置
2. MFC开发者常见错误
- 忘记在paintEvent以外的地方调用update()触发重绘
- 尝试保存QPainter对象以供以后使用(它只在创建它的函数范围内有效)
- 在同一时间在同一设备上使用多个QPainter
- 不处理高DPI显示缩放
3. 性能优化建议
- 减少paint事件中的逻辑处理,专注于绘图
- 缓存复杂或重复使用的图形
- 优先使用预渲染图像而非复杂矢量绘图
- 仅在需要时启用抗锯齿
- 对于复杂图形考虑使用OpenGL渲染
4. 从GDI/GDI+转换
- 使用QPainter的RenderHints替代GDI+的SmoothingMode
- 使用QPainterPath替代GraphicsPath
- 使用Qt的渐变类替代GDI+渐变
- 使用Qt的QPixmap/QImage替代Bitmap/Image
- 利用Qt内置的图像格式支持替代GDI+的编解码器
猜你喜欢
- 2025-05-09 前端开发-SVG从入门到实战(前端如何使用svg图标)
- 2025-05-09 谁说Adobe XD做不出好看的设计?那是你没搞懂这些功能
- 2025-05-09 更甚一筹的编写技巧(更甚一筹的同义词)
- 2025-05-09 HarmonyOS NEXT - 通用属性(HarmonyOS NEXT功能介绍)
- 2025-05-09 Qt 图形(QPainterPath)(qt 图形库)
- 2025-05-09 学下SVG矢量图形,你也是个小小艺术家!
- 2025-05-09 WPF的技术架构与优势(wpf的前景)
- 2025-05-09 蚁利智慧工地展厅控制系统:多媒体交互软件及中控系统开发方案
- 2025-05-09 camera raw蒙版调色入门(用蒙版调色)
- 2025-05-09 20、数据可视化:魔镜报表——React 19 图表集成
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- css导航条 (66)
- sqlinsert (63)
- js提交表单 (60)
- param (62)
- parentelement (65)
- jquery分享 (62)
- check约束 (64)
- curl_init (68)
- sql if语句 (69)
- import (66)
- chmod文件夹 (71)
- clearinterval (71)
- pythonrange (62)
- 数组长度 (61)
- javafx (59)
- 全局消息钩子 (64)
- sort排序 (62)
- jdbc (69)
- php网页源码 (59)
- assert h (69)
- httpclientjar (60)
- postgresql conf (59)
- winform开发 (59)
- mysql数字类型 (71)
- drawimage (61)
本文暂时没有评论,来添加一个吧(●'◡'●)