最可靠方法是用QPainter在内存中手动裁剪圆形:创建透明QPixmap,开启抗锯齿,绘制圆形路径,再用CompositionMode_SourceIn合成原图。

QPixmap + QPainter 绘制圆形裁剪最可靠
直接用 QPainter::drawPixmap 画原图不行,必须手动裁出圆形区域。Qt 没有内置「圆形图片控件」,得靠 QPainter 在内存中抠图。核心思路是:创建一个同尺寸的透明 QPixmap,用 QPainter 开启抗锯齿,画一个圆形路径作为裁剪区,再把原图贴上去。
常见错误是忘记设置 QPainter::Antialiasing,边缘锯齿明显;或者用 setClipPath 后没调 setRenderHint,导致圆边发虚或偏移。
- 务必用
QImage::Format_ARGB32_Premultiplied创建目标图像,否则透明通道处理异常 - 裁剪半径 =
qMin(pixmap.width(), pixmap.height()) / 2,避免椭圆 - 绘制时中心点坐标要对齐:
center = QPoint(pixmap.width()/2, pixmap.height()/2)
QLabel 显示圆形头像的最小可行代码
别折腾自定义控件,QLabel 配合 setMask 或 QPixmap 裁剪就能搞定。但注意:setMask 在高 DPI 下易错位,推荐优先走 QPixmap 裁剪 + setPixmap 路线。
下面这段可以直接粘贴进 slot 或构造函数:
QPixmap original(":/avatar.jpg");
QPixmap rounded(original.size());
rounded.fill(Qt::transparent);
QPainter painter(&rounded);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(Qt::black);
painter.drawEllipse(rounded.rect());
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
painter.drawPixmap(0, 0, original);
ui->label_avatar->setPixmap(rounded);
关键点:CompositionMode_SourceIn 才能用圆形区域“抠”出原图,换成 SourceOver 就全白了。
QGraphicsItem 场景下做圆角头像(比如聊天列表)
如果头像在 QGraphicsView 里,别用 QLabel 套娃。直接继承 QGraphicsPixmapItem,重写 paint 函数,在里面做圆形绘制逻辑。否则缩放、旋转时 mask 会失效。
容易被忽略的是:必须调用 setFlag(QGraphicsItem::ItemHasNoContents, false),否则 paint() 不会被触发;另外 boundingRect() 要返回原始尺寸,不能只返回圆内接矩形,否则布局计算出错。
- 圆角头像用
drawRoundedRect?不行,那是矩形圆角,不是圆形 - 想支持 8px 圆角而非纯圆?把
drawEllipse换成drawRoundedRect(rect, r, r),但注意r最大不能超width()/2 - 性能敏感场景(如百人列表),提前缓存裁剪后的
QPixmap,别每次 paint 都重绘
QSS 实现圆角(仅限简单 UI,慎用)
用 border-radius 看似简单,但只对 QLabel 且设置了 background-image 有效,对 pixmap 属性无效。而且它只是视觉遮罩,实际像素仍是方形,截图或导出会露馅。
典型写法:
QLabel#avatar_label {
border-radius: 25px;
background-image: url(:/avatar.jpg);
background-repeat: no-repeat;
background-position: center;
}
限制很死:必须用 background-image,不能用 setPixmap;border-radius 值必须是宽高一半才真圆;高 DPI 下 background-position 可能偏移 1px。
真正需要导出头像、做动画、或嵌入 OpenGL 场景时,QSS 这条路根本走不通。











