Qt5/6实战:用QPainter在Widget上画个带边框和填充色的矩形(附源码)
Qt5/6实战:用QPainter绘制带边框与填充色的矩形
第一次在Qt中看到QPainter绘制出的矩形时,那种"代码即界面"的奇妙感至今难忘。作为Qt图形系统的核心组件,QPainter就像数字世界的画笔,让开发者能够精确控制每个像素的呈现。本文将带你从零开始,在Widget上绘制一个具有自定义边框和填充效果的矩形,过程中不仅会涉及基础API调用,更会分享那些官方文档没明说但实际开发中绕不开的细节。
1. 环境准备与项目创建
推荐使用Qt Creator作为开发环境,无论是Qt5.15还是Qt6.2都能完美运行本示例。新建项目时选择"Qt Widgets Application",模板会自动生成主窗口类。这里有个容易踩坑的地方:项目命名时避免使用特殊字符和空格,否则可能遇到编译问题。
在widget.h中声明重绘事件处理函数:
protected: void paintEvent(QPaintEvent *event) override;提示:现代Qt开发中建议使用
override关键字明确表示重写虚函数,这能让编译器帮助检查函数签名是否正确。
2. 理解Qt坐标系系统
在实现绘制逻辑前,必须清楚Qt的坐标系规则:
- 原点(0,0)位于Widget的内容区域左上角(不包括窗口边框)
- X轴向右为正方向,Y轴向下为正方向
- 默认单位是像素,但可通过QTransform实现缩放
void Widget::paintEvent(QPaintEvent *event) { QPainter painter(this); // 测试坐标系 painter.drawText(10, 20, "坐标系原点在这里"); }运行这段代码,文字会出现在距离窗口内边框右侧10像素、下方20像素的位置。如果发现显示位置有偏差,检查是否忽略了窗口边框的宽度。
3. 实现基础矩形绘制
核心绘制流程分为三个关键步骤:
- 创建QPainter对象:绑定到当前Widget
- 配置绘制工具:
- QPen控制边框样式
- QBrush控制填充样式
- 执行绘制命令:调用drawRect等绘图方法
void Widget::paintEvent(QPaintEvent *event) { QPainter painter(this); // 配置红色虚线边框 QPen borderPen(QColor(255, 0, 0)); borderPen.setStyle(Qt::DashLine); borderPen.setWidth(3); // 配置半透明绿色填充 QBrush fillBrush(QColor(0, 255, 0, 150)); painter.setPen(borderPen); painter.setBrush(fillBrush); // 绘制矩形:左上角(50,50),宽200,高100 painter.drawRect(50, 50, 200, 100); }参数说明表格:
| 参数 | 类型 | 说明 | 常用值示例 |
|---|---|---|---|
| QPen宽度 | int | 边框粗细 | 1-5像素 |
| QPen样式 | Qt::PenStyle | 边框线型 | SolidLine, DashLine, DotLine |
| QColor透明度 | int | Alpha通道 | 0(全透明)-255(不透明) |
| drawRect坐标 | int | 矩形位置尺寸 | (x,y,width,height) |
4. 高级样式定制技巧
4.1 使用预设样式
Qt提供了多种预设颜色和样式,避免手动定义RGB值:
// 使用系统预设 QPen pen(Qt::GlobalColor::darkBlue); pen.setStyle(Qt::DashDotLine); // 画刷填充图案 QBrush brush; brush.setColor(Qt::yellow); brush.setStyle(Qt::Dense4Pattern);4.2 抗锯齿优化
绘制斜线或曲线时开启抗锯齿可获得更平滑的效果:
painter.setRenderHint(QPainter::Antialiasing); painter.drawRect(100, 100, 150, 150);4.3 动态效果实现
结合定时器可以实现动态绘图效果。在Widget类中添加:
private slots: void animateRect(); private: int m_offset = 0;实现动画逻辑:
// 构造函数中连接定时器 QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &Widget::animateRect); timer->start(30); // 动画处理 void Widget::animateRect() { m_offset = (m_offset + 2) % 100; update(); // 触发重绘 } void Widget::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawRect(50 + m_offset, 50, 100, 100); }5. 常见问题解决方案
问题1:绘制内容不显示
- 检查是否漏掉了
painter.begin(this)或使用了错误的PaintDevice - 确认Widget的
autoFillBackground属性为false
问题2:性能优化建议
- 复杂图形考虑使用
QPixmap缓存绘制结果 - 只在变化时调用
update()而非连续重绘
问题3:高DPI屏幕适配
// 获取设备像素比 qreal ratio = devicePixelRatioF(); painter.scale(ratio, ratio);完整项目源码结构:
/ProjectRoot ├── widget.h # 头文件声明 ├── widget.cpp # 绘制实现 ├── main.cpp # 应用入口 └── CMakeLists.txt # 构建配置实际开发中,我习惯将复杂的绘制逻辑拆分成多个方法,比如drawBorder()和drawContent(),这样既方便维护也利于性能优化。当需要绘制多个矩形时,可以考虑使用QGraphicsView框架替代直接Widget绘制,这在需要交互或大量图形元素的场景下性能更好。
