MFC转QT:Qt高级特性 - QPainter绘图
cac55 2025-05-11 14:24 8 浏览 0 评论
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+的编解码器
相关推荐
- 让组策略保护Windows XP的安全
-
默认安装完WindowsXP之后,我们的WindowsXP并不很安全。因此,我们有必要对系统进行一些修修补补,一般情况下我们都要动用到注册表。诚然,修改注册表是一种非常有效的方法,但是它需要一定的...
- 你造吗?十种方式保护你免受"零日攻击"
-
|责编:王迪WindowsXP的寿终正寝,数据安全问题又再一次成为人们关注的焦点。近日,微软透漏,一个基于InternetExplorer的“零日攻击”给用户带来了严重破坏。“零日攻击”一种利用...
- 特立独行——打造游戏专用独立系统
-
大部分人的电脑是为了学习和工作用的,所以,如果你是一个游戏迷,那么推荐你安装一个独立系统专用于游戏,做到工作娱乐两不相扰。方案1:游戏专用移动WindowsXP目的:解决游戏兼容性问题喜欢玩游戏的都...
- 驰为VX8 3G Win8入门教程篇
-
距离Win8.1的正式发布也将近1年了,凭借着Win8.1在移动便携以及娱乐办公上的优势,现在的Win8平板越来越受到消费者的追捧,而驰为VX83G就是其中一款,搭载了卓越的英特尔Z3735G四核芯,...
- 易淘收银软件说明
-
易淘收银系统,简称易淘收银,专为小型及连锁零售、餐饮行业打造。基于SaaS模式,智能便捷,无需维护,轻量级设计却功能强大,简约而不失专业,助力门店高效管理收银。1、前台系统:收银客户端;2、后台系...
- CAD打不开怎么办?原因可能是电脑中毒了,6步就能完美解决问题
-
一、问题描述我的CAD安装后无法打开,安装过程中没有出现任何问题,但是安装后打开就出现一个对话框“DBXCAS0”点击后又出现“FATALERROR:UnhandledAccessViola...
- 腾讯QQ6.1正式版发布更新
-
2014-07-2405:12:00作者:张林【中关村在线软件资讯】7月24日消息:腾讯QQ官网小幅更新了QQ6.1正式版,最新版本号升级至11905,继续主打扁平化、炫酷登录窗口、支持同步最近一...
- Win10等网页版OneDrive无法登陆怎么办?
-
IT之家(www.ithome.com):Win10等网页版OneDrive无法登陆怎么办?Win10之家报道,微软OneDrive云网盘是跨平台的数据同步和存储服务,支持WindowsPC(如Wi...
- 经典回顾:折戟沉沙的Windows Longhorn有着惊艳的登录屏幕
-
尽管微软原先计划让WindowsLonghorn继承WindowsXP操作系统的衣钵,但这个充满雄心壮志的操作系统项目最终还是未能迎来曙光,而是被微软用WindowsVista取而代...
- 电脑怎么优化
-
电脑配置和宽带流量也是硬件,但这些要求其实并不需要很高,关键还是怎么去安全使用电脑并进行有效的优化。电脑的应用和优化处理一、电脑的应用和优化处理二、目前,大家使用的个人电脑,配置方面均没多大问题,比如...
- 怎么安装usb驱动
-
USB驱动主要是针对WIN98时代的说法,如今WINXP已集成大部分USB驱动,通常都能识别。只有极少数情况下,例如手机、打印机或扫描仪等办公设备的USB驱动可能无法自动识别。1、USB驱动偶尔无法...
- 普通话考试多名考生信息被泄露,接投诉后涉事网站被限制访问
-
“陕西普通话成绩查询网(sxpth.cn)”泄露个人信息网站截图网传图片显示,407名普通话考试考生的姓名、身份证号码等个人信息疑似被泄露。9月26日下午,涉事网站sxpth.cn的域名注册商——成...
- 电脑伪技巧——个人电脑无需设置登录密码
-
默认情况下,我们每次登录系统都要输入登录账户对应的密码才能进入桌面。有些朋友觉得这样很麻烦,由于电脑只是自己使用,还不如不要设置密码,这样每次可以自动登录。大家知道,账户密码是系统验证用户合法性的唯一...
- Windows 10/11 自带远程桌面:实用技巧与操作指南
-
Windows10/11自带远程桌面:实用技巧与操作指南在当今快节奏的数字时代,远程访问和控制计算机的需求日益增长。微软在Windows10和Windows11中内置了远程桌面功能,为用户提供...
- 不升级系统的5大原因造吗?
-
2015-01-2405:54:00作者:陈占伟Windows10系统的发布,让人们重新将目光聚焦到生命力长久的Windows系统之上。如今操作系统越来越多,似乎Windows升级的获得的关注度...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 如何绘制折线图 (52)
- javaabstract (48)
- 新浪微博头像 (53)
- grub4dos (66)
- s扫描器 (51)
- httpfile dll (48)
- ps实例教程 (55)
- taskmgr (51)
- s spline (61)
- vnc远程控制 (47)
- 数据丢失 (47)
- wbem (57)
- flac文件 (72)
- 网页制作基础教程 (53)
- 镜像文件刻录 (61)
- ug5 0软件免费下载 (78)
- debian下载 (53)
- ubuntu10 04 (60)
- web qq登录 (59)
- 笔记本变成无线路由 (52)
- flash player 11 4 (50)
- 右键菜单清理 (78)
- cuteftp 注册码 (57)
- ospf协议 (53)
- ms17 010 下载 (60)