Qt實現半透明遮罩效果

2022-01-10 08:55:13 字數 4039 閱讀 3977

本文索引
我們在顯示一些模態對話方塊的時候,往往需要將對話方塊的背景顏色調暗以達到突出當前對話方塊的效果,例如:

對話方塊的父視窗除了標題欄以外的部分都變暗了,在父視窗的對比下對話方塊的顯示效果就得到了強調。

這種設計多見於web頁面,當使用者點選諸如購買之類的按鈕後頁面會彈出乙個購物清單確認對話方塊,並將對話方塊以外的內容用類似圖中的效果處理,使使用者可以將注意力集中在對話方塊本身。

今天我們也將使用qt來實現這一效果。

在介紹具體做法前我想先介紹一點預備知識——「亮盒效果」。這是乙個攝影技術的名詞,大意是指將背景暗化以便突出**的主體,因為往往使用乙個黑色的「盒子」來罩住需要拍攝的主體,所以被稱為亮盒。而這與我們想實現的效果不謀而合。

所以想要實現讓對話方塊的父視窗變暗的效果,最常見的手段就是使用乙個半透明遮罩控制項將父視窗元件整個遮住。

可能有人會問,既然只需要將背景暗化,那為何不直接修改父視窗的qss,而要使用乙個遮罩元件呢?原因也很簡單,因為父控制項的background屬性是少數幾個能被子控制項繼承的屬性,當我們修改了父視窗的qss那麼我們的對話方塊也將不可避免的遭受影響,雖然可以使用setstylesheet('')去除這些額外的影響,但是這樣做將會引入許多不必要的複雜性,顯然是與我們的設計初衷相違背的。

所以我們選擇使用遮罩控制項。回顧一下qwidget的特性,當除了qdialog以外的控制項設定了非none的parent時,該控制項就會繪製在parent控制項上。布局管理器只是幫助我們設定了parent並自動指定了乙個合適的位置和尺寸來繪製控制項,所以我們完全可以自己指定控制項的大小和需要繪製的區域。

繪製區域使用的是qwidget的邏輯座標。與painter使用的座標系統一致。所以我們只需要設定遮罩元件的parent為父視窗,然後獲取父視窗的高度和寬度,並設定遮罩元件的大小與父視窗一致,最後從父視窗邏輯座標系的(0, 0)出開始繪製控制項即可保證遮罩控制項可以完整的遮蓋住父視窗實現遮罩效果。

注意,如果子控制項的繪製區域或者大小超過了父控制項,超過的部分將會被截斷,也就是說不會顯示出來。不過不用擔心,qt為我們提供了geometrysetgeometry介面,通過它們就可以方便的控制widgets的形狀和位置而不用擔心出錯。

下面就讓我們看一下python3實現的遮罩控制項。

先看**:

class maskwidget(qwidget):

def __init__(self, parent=none):

super().__init__(parent)

self.setwindowflag(qt.framelesswindowhint, true)

self.setattribute(qt.wa_styledbackground)

self.setstylesheet('background:rgba(0,0,0,102);')

self.setattribute(qt.wa_deleteonclose)

def show(self):

"""重寫show,設定遮罩大小與parent一致

"""if self.parent() is none:

return

parent_rect = self.parent().geometry()

self.setgeometry(0, 0, parent_rect.width(), parent_rect.height())

super().show()

遮罩控制項的實現相當簡單,只需要注意一些細節。

遮罩控制項的初始化和普通的自定義控制項的過程一樣,不過需要注意的是self.setattribute(qt.wa_styledbackground)這一行,自定義控制項只有設定該屬性後才能正常設定背景。

隨後我們還設定了無邊框視窗和deleteonclose,遮罩不需要顯示任何邊框,不過這裡的deleteonclose可以不用設定,因為python使用的pyqt可以完美地配合gc,當控制項不在被使用時可以自動釋放資源,不過我還是養成了顯示釋放的習慣,明確對資源的處理永遠都不是壞事。

第乙個重點在於那句qss。qss中也可以設定rgba顏色,不過與css相比有一些區別。最後的alpha引數,css中通常是0-1的實數或者乙個百分數,而在qss中它是乙個0-255的整數值,而我們想要實現半透明的黑色遮罩,就需要指定控制項背景色透明度為40%,也就是255 * 0.4 = 102,最終的結果就是rgba(255, 0, 0, 102),設定完成後控制項就擁有了半透明效果。

第二個重點在重寫的show方法上。光設定了顏色和透明度還不夠,我們還要讓控制項正確地遮蓋住parent。為了達到這一目的,我們先獲取parent的geometry,然後使用self.setgeometry(0, 0, parent_rect.width(), parent_rect.height())將控制項設定到與parent重合(原理參考上一節內容)。而如果我們沒有給控制項設定parent,那麼控制項什麼也不會做,因為控制項本身需要依賴於parent,如果沒有的話也就沒法正常顯示了。之後再使用qwidget.show()就可以顯示我們的遮罩效果了。

使用遮罩也相當簡單:

class mywidget(qwidget):

"""測試遮罩的顯示效果

"""def __init__(self):

super().__init__()

# 設定白色背景,方便顯示出遮罩

self.setstylesheet('background:white;')

main_layout = qvboxlayout()

button = qpushbutton('點選顯示對話方塊')

button.clicked.connect(self.show_dialog)

main_layout.addstretch(5)

main_layout.addwidget(button, 1, qt.aligncenter)

self.setlayout(main_layout)

self.show()

def show_dialog(self):

dialog = qdialog(self)

dialog.setmodal(true)

dialog_layout = qvboxlayout()

dialog_layout.addwidget(qlabel('mask test'))

dialog.setlayout(dialog_layout)

mask = maskwidget(self)

mask.show()

dialog.exec()

mask.close()

if __name__ == '__main__':

w = mywidget()

w.show()

遮罩的使用分為如下個步驟:

根據需要遮蓋的控制項建立maskwidget

顯示遮罩

在模態對話方塊關閉後呼叫close()清除遮罩

之所以要在對話方塊顯示之前先顯示遮罩,是因為顯示模態對話方塊後父視窗的事件迴圈被阻塞,這時所有對父視窗的操作都是被阻塞的,而對話方塊關閉後遮罩就被close了,父視窗的事件迴圈會將多次繪製事件智慧型的合併,所以遮罩可能根本不會被顯示出來,因此我們必須在對話方塊前顯示遮罩。(如果你好奇的話可以把兩行**的順序對調,看看是否能正常顯示遮罩控制項)

這樣我們的遮罩控制項就完成了,執行程式:

QT 透明 半透明 效果

objwindow setwindowopacity 只對視窗有效,setattribute qt wa translucentbackground,true 對於視窗widget必須設定為qt framelesswindowhint include qgraphicsopacityeffect o...

div css彈出層半透明遮罩層效果實現

背景半透明,覆蓋整個可視區域的遮罩層效果在工作中經常會遇到,這篇文章主要介紹了當內容超過一屏時如何做到多瀏覽器的相容性。下面我們通過乙個簡單的例子看看如何實現,高手請繞道。html 很簡單 1 半透明效果可以使用 css3 中的 opacity 屬性,在低版本的ie瀏覽器中使用ie的alpha 濾鏡...

Unity Shader 實現半透明效果

想要實現如下效果 主角可以遮擋背景,背景遮擋主角時候半透明混合 研究了一周,shader知識了解從無到稍稍入門,終於解決了,記錄一下實現方案。使用blend one oneminussrcalpha指令,也就是srccolor dstcolor 1 srcalpha 假設a和b重疊,先渲染a,然後渲...