QWidget獨立窗口抗鋸齒圓角窗口的一個實現(xiàn)方案
由于 QWidget::setMask 接口設置圓角不支持抗鋸齒,所以通常會使用透明窗口加圓角背景,但圓角背景不能滿足對子控件的裁剪,子控件與圓角區(qū)域重疊的部分還是能顯示出來。當然對于大多數(shù)窗口,留出足夠的邊距也是可以接受。
對一些特殊場景,比如QComboBox的列表框,UI設計師強烈要求圓角,列表與它的容器不能有邊距,常規(guī)辦法就很難做到。筆者在經(jīng)過長時間的研究,有了一個可能的方案。
最終實現(xiàn)效果如下圖,可以看到,列表項區(qū)域,滾動條區(qū)域也能夠正常顯示圓角。
注意,該方案可能不適用一些場景:
- 特殊的平臺或Qt配置
- 復雜窗口且有性能要求
- 窗口尺寸較大(可以優(yōu)化)
- 有嵌入式窗口、OpenGL、QWindow等
方案
基本原理
Qt的每個獨立窗口,默認都是在一張圖片上,層疊繪制所有子控件。通常我們自繪控件時,幾乎不會使用QPainter::setCompositionMode設置其他混合模式,會出現(xiàn)比較奇怪的效果。但如果使用透明背景窗口,使用混合模式其實跟在QPixmap或QImage上繪制一樣。
另外一點,當一個控件重繪時,由于底層的繪制會影響到上層透明合成,所以Qt會從下到上按順序繪各個控件的臟區(qū)域。
所以理論上,如果在一個窗口上增加一個全尺寸的遮罩,重繪時使用混合模式就可以實現(xiàn)對一些像素的清除,且支持抗鋸齒。
代碼步驟
-
創(chuàng)建一個QWidget作為遮罩
遮罩置于頂層,鼠標設置透傳(WA_TransparentForMouseEvents)
遮罩跟隨窗口尺寸大小同步變化,保持一致。安裝事件過濾器即可(installEventFilter)
-
重寫paintEvent,利用混合模式清除圓角像素
以下繪制邏輯比較直接,建議優(yōu)化
//創(chuàng)建一個圖片,填充透明色
QPixmap pix(this->size());
pix.fill(QColor(0,0,0,0));
// 在改圖片上填充一個圓角區(qū)域,需要設置抗鋸齒
QPainter painter(&pix);
painter.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
//這里圓角區(qū)域需要根據(jù)dpi、size調(diào)整
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);
// 該混合模式會根據(jù)source像素的透明度,調(diào)整目標的透明度
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);
優(yōu)化方向
-
性能優(yōu)化
上面的示例,使用了一整張圖片對窗口像素進行混合模式的運算,且每次子控件重繪都會引起遮罩的重繪,性能比較差??梢钥紤]僅在四周設置圓角的遮罩。
-
圓角繪制優(yōu)化
本文使用了一個不透明的圓角區(qū)域?qū)Υ翱谠O置裁剪,圓角的參數(shù)是固定在代碼里的。實際QSS是可以設置窗口的圓角,因此可以借助QSS來生成一張圓角圖片,就避免代碼里包含固定數(shù)值。文章來源:http://www.zghlxwxcb.cn/news/detail-423736.html
具體實現(xiàn)原理可以參考之前的文章,這里不具體展示了:
QComboBox文字居中的一種解決辦法
Qt實現(xiàn)一個支持QSS的Switch Button
Qt借助隱藏控件和QSS繪制重復元素文章來源地址http://www.zghlxwxcb.cn/news/detail-423736.html
到了這里,關(guān)于Qt QWidget 抗鋸齒圓角窗口的一個實現(xiàn)方案(支持子控件)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!