Qt QWidget 独立窗口抗锯齿圆角的一个实现方案(支持子控件) | 您所在的位置:网站首页 › qwidget设置模态不起作用 › Qt QWidget 独立窗口抗锯齿圆角的一个实现方案(支持子控件) |
QWidget独立窗口抗锯齿圆角窗口的一个实现方案
由于 QWidget::setMask 接口设置圆角不支持抗锯齿,所以通常会使用透明窗口加圆角背景,但圆角背景不能满足对子控件的裁剪,子控件与圆角区域重叠的部分还是能显示出来。当然对于大多数窗口,留出足够的边距也是可以接受。 对一些特殊场景,比如QComboBox的列表框,UI设计师强烈要求圆角,列表与它的容器不能有边距,常规办法就很难做到。笔者在经过长时间的研究,有了一个可能的方案。 最终实现效果如下图,可以看到,列表项区域,滚动条区域也能够正常显示圆角。 注意,该方案可能不适用一些场景: 特殊的平台或Qt配置复杂窗口且有性能要求窗口尺寸较大(可以优化)有嵌入式窗口、OpenGL、QWindow等 方案 基本原理Qt的每个独立窗口,默认都是在一张图片上,层叠绘制所有子控件。通常我们自绘控件时,几乎不会使用QPainter::setCompositionMode设置其他混合模式,会出现比较奇怪的效果。但如果使用透明背景窗口,使用混合模式其实跟在QPixmap或QImage上绘制一样。 另外一点,当一个控件重绘时,由于底层的绘制会影响到上层透明合成,所以Qt会从下到上按顺序绘各个控件的脏区域。 所以理论上,如果在一个窗口上增加一个全尺寸的遮罩,重绘时使用混合模式就可以实现对一些像素的清除,且支持抗锯齿。 代码步骤创建一个QWidget作为遮罩 遮罩置于顶层,鼠标设置透传(WA_TransparentForMouseEvents) 遮罩跟随窗口尺寸大小同步变化,保持一致。安装事件过滤器即可(installEventFilter) 重写paintEvent,利用混合模式清除圆角像素 以下绘制逻辑比较直接,建议优化 //创建一个图片,填充透明色 QPixmap pix(this->size()); pix.fill(QColor(0,0,0,0)); // 在改图片上填充一个圆角区域,需要设置抗锯齿 QPainter painter(&pix); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; //这里圆角区域需要根据dpi、size调整 path.addRoundedRect(QRectF(pix.rect()).adjusted(0.5, 0.5, -0.5, -0.5), 10, 10); painter.fillPath(path, Qt::white); painter.end(); // 在窗口上绘制该圆角图片 painter.begin(this); painter.setRenderHint(QPainter::Antialiasing); // 该混合模式会根据source像素的透明度,调整目标的透明度 painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); painter.drawPixmap(0, 0, pix); // 恢复默认混合模式,绘制边框,如果没有则不用 painter.setCompositionMode(QPainter::CompositionMode_SourceOver); painter.setPen(QPen(QColor(0xCA64EA), 1.0)); painter.drawPath(path); 优化方向性能优化 上面的示例,使用了一整张图片对窗口像素进行混合模式的运算,且每次子控件重绘都会引起遮罩的重绘,性能比较差。可以考虑仅在四周设置圆角的遮罩。 圆角绘制优化 本文使用了一个不透明的圆角区域对窗口设置裁剪,圆角的参数是固定在代码里的。实际QSS是可以设置窗口的圆角,因此可以借助QSS来生成一张圆角图片,就避免代码里包含固定数值。 具体实现原理可以参考之前的文章,这里不具体展示了: QComboBox文字居中的一种解决办法 Qt实现一个支持QSS的Switch Button Qt借助隐藏控件和QSS绘制重复元素 |
CopyRight 2018-2019 实验室设备网 版权所有 |