真圆角必须用QPainterPath裁剪:创建圆形路径→setClipPath→drawPixmap,需开启抗锯齿、适配设备像素比,并缓存路径提升性能。

Qt 中用 QPainter 绘制圆角头像的正确姿势
直接用 QPixmap::scaled() 再塞进 QLabel 显示,永远得不到真圆角——那是矩形图被硬裁边,缩放还容易糊。真圆角必须在绘制时用路径裁剪。
核心是:创建一个 QPainterPath 圆形区域,用 QPainter::setClipPath() 截取图像,再画图。注意别漏掉抗锯齿和设备像素比适配:
-
painter.setRenderHint(QPainter::Antialiasing)必开,否则边缘发虚 - 如果头像来自高分屏(如 macOS Retina 或 Windows 缩放 125%+),记得用
pixmap.devicePixelRatio()调整裁剪半径,否则圆角会偏小 - 别在
paintEvent里每次都 newQPainterPath—— 提前缓存好路径对象,尤其用于列表项时性能差很多
void drawRoundedAvatar(QPainter &p, const QPixmap &pixmap, const QRect &rect, int radius) {
QPainterPath path;
path.addRoundedRect(rect, radius, radius);
p.setClipPath(path);
p.drawPixmap(rect, pixmap.scaled(rect.size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
}
QWidget 实现无边框异形窗口的遮罩关键点
设 setWindowFlags(Qt::FramelessWindowHint) 只是第一步,真正让窗口“按形状响应鼠标/事件”的核心是 setMask() —— 它作用于整个窗口几何区,不是视觉层。
常见错误:用一张 PNG 做遮罩图,但没处理 alpha 通道;或用 QBitmap 从 pixmap 转换时忘了调 alphaChannel()。结果是窗口变黑块、点击穿透、拖动失效。
- 遮罩必须是单色位图(
QBitmap),不能直接传QPixmap - 推荐路径:用
QPainterPath描出你想要的窗口轮廓 →QRegion(path.toFillPolygon().toPolygon())→setMask(region),比位图更精准、缩放不糊 - 如果窗口支持缩放(比如 DPI 变化),
setMask()必须在changeEvent(QEvent::ScreenChangePropagated)或重写highDpiScaleFactorChanged()后重新计算,否则遮罩错位
QML 中实现圆角头像更简单但有陷阱
QML 的 Image + layer.enabled: true + layer.effect: OpacityMask 看似一行解决,实际在嵌入式或旧显卡上容易崩溃或不生效。
更稳的方案是用 ShaderEffect 自定义圆角裁剪,或退回到 BorderImage 搭配预生成的九宫格圆角蒙版图——但后者不支持动态尺寸。
- 用
OpacityMask时,maskSource必须是纯灰度图(非 alpha 图),且尺寸严格匹配sourceSize,否则边缘毛刺 -
ShaderEffect的 GLSL 里别直接用gl_FragCoord做圆角判断——要先转成归一化 UV,否则不同分辨率下圆角半径失真 - QML 场景下,
Image.sourceSize和width/height不一致时,圆角半径按哪个算?答案是按width/height,所以务必显式设好尺寸
异形窗口拖动和阴影的兼容性问题
Windows 上用 setMask() 后,系统默认阴影消失,而且 mouseMoveEvent 无法捕获到 mask 区域外的拖动——这不是 bug,是 Windows DWM 的限制。
绕过方法只有两个:要么放弃系统阴影,自己用 QGraphicsDropShadowEffect 加在主 widget 上(注意 z-order 和 mask 重叠);要么改用 Qt::WindowShadeButtonHint 等传统窗口装饰,但那就不是异形了。
macOS 更麻烦:setMask() 在全屏或 Mission Control 下行为不可控,建议只在正常窗口模式启用,进入全屏前 clearMask(),退出后再恢复。
最易忽略的一点:异形窗口的 resizeEvent 触发后,setMask() 必须重算——哪怕你只是拉宽了 1 像素,旧 mask 会残留黑边或点击失效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











