PyQt 動態 setWindowFlags


問題

在Baidu上搜索 "PyQt setWindowFlags" 會有各種爛大街的講述,但無非都圍繞這樣的寫法:

self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint|QtCore.Qt.WindowStaysOnTopHint)

別的不說,很多人根本不懂這樣的寫法是怎么回事,而許多文章互相抄襲,也不知道為什么這樣寫,所以干脆不寫。

但是我的需求不光在窗口初始化時調用,而且在使用中需要更改setWindowFlags函數set的內容。

這樣的寫法就很有弊端了,我舉個栗子:

self.setWindowFlags(QtCore.Qt.FramelessWindowHint|QtCore.Qt.Tool|QtCore.Qt.WindowStaysOnTopHint)

在初始化時我已經設定了,但是我在中途突然想讓窗口不再置頂

那么:

self.setWindowFlags(QtCore.Qt.FramelessWindowHint|QtCore.Qt.Tool)

如果我要的功能是任意二者組合呢?

有3種組合,那么我如果按照傳統寫法,需要寫3個self.setWindowFlags(...)備用,這顯然是不合理的,如果我的功能需求增加,用來設置的備選項就會指數增加。所有我要詳細說一下setWindowFlags背后的原理。

setWindowFlags 的本質是什么

在Python中參數傳入讓人丈二和尚摸不着頭腦,簡直不知道是干嘛的

    def setWindowFlags(self, Union, Qt_WindowFlags=None, Qt_WindowType=None): # real signature unknown; restored from __doc__
        """ setWindowFlags(self, Union[Qt.WindowFlags, Qt.WindowType]) """
        pass

但是傳入參數上我找到了突破口,不管是什么說法,只要合並Flags項,都用到"xxx|xxx"的寫法。在C++和Python中,"|"的含義都是"按位與"。

"按位與"的運算是基於二進制位的,如果有一個數字,先轉換成二進制,對應位比較,其中任意一位為1則結果為1

那么如果這樣使用,那么|的兩邊的數據類型我盲猜是int,果然被我猜中:

其余的在這里:

WindowStaysOnTopHint = 262144
Tool = 11
FramelessWindowHint = 2048

我們模擬一下合並的過程:

十進制下是:264,203‬

可以看出每一個二進制位都應該充當開關的作用,控制着一些東西。

argument 1 has unexpected type 'int'

這樣想就被打臉了,因為Qt的想法很奇妙:

print(type(QtCore.Qt.WindowStaysOnTopHint))

返回結果:

<class 'PyQt5.QtCore.Qt.WindowType'>

這說明我們傳入的QtCore.Qt.xxx其實在Python里面是被WindowType這個class接管的,那么我們找到它:

C++版本的對應定義:

Qt::Widget               //是一個窗口或部件,有父窗口就是部件,沒有就是窗口
Qt::Window               //是一個窗口,有窗口邊框和標題
Qt::Dialog               //是一個對話框窗口
Qt::Sheet                //是一個窗口或部件Macintosh表單
Qt::Drawer               //是一個窗口或部件Macintosh抽屜
Qt::Popup                //是一個彈出式頂層窗口
Qt::Tool                 //是一個工具窗口
Qt::ToolTip              //是一個提示窗口,沒有標題欄和窗口邊框
Qt::SplashScreen         //是一個歡迎窗口,是QSplashScreen構造函數的默認值
Qt::Desktop              //是一個桌面窗口或部件
Qt::SubWindow            //是一個子窗口

其實是與C++里面的一堆東西一一對應的,同時傳入的int參數也暴露了本質。那么在self.setWindowFlags(QtCore.Qt.FramelessWindowHint|QtCore.Qt.Tool)的寫法下其實是傳入了被Qt包裝好的開關代號(FramelessWindowHint之類常數)按位與后的結果,但是在真正到setWindowFlags之前經過了QtCore.Qt.WindowType一層包裝,所以我們才可以如此"優雅"的使用Qt。

修改代碼

那么找到問題就可以動刀了

self.setWindowFlags(QtCore.Qt.WindowType(Flags))

把原來的調用方式寫成這樣,Flags就可以傳入int值了

WindowFlags = set()

def FramelessWindowHint(Choose=False):
    """ Choose: 是否選擇使用Tool這個選項 """
    if Choose:
        WindowFlags.add(int(QtCore.Qt.FramelessWindowHint))
    else:
        WindowFlags.remove(int(QtCore.Qt.FramelessWindowHint))

def WindowStaysOnTopHint(Choose=False):
    """ Choose: 是否選擇使用Tool這個選項 """
    if Choose:
        WindowFlags.add(int(QtCore.Qt.WindowStaysOnTopHint))
    else:
        WindowFlags.remove(int(QtCore.Qt.WindowStaysOnTopHint))


def Tool(Choose=False):
    """ Choose: 是否選擇使用Tool這個選項 """
    if Choose:
        WindowFlags.add(int(QtCore.Qt.Tool))
    else:
        WindowFlags.remove(int(QtCore.Qt.Tool))

def ChangeWindowFlags(init = True):
    Flags = 0
    for i in Space['WindowFlags']:
        Flags = Flags | i
    self.setWindowFlags(QtCore.Qt.WindowType(Flags))
    if init:
        return
    if not self.isVisible():
        self.setVisible(True)

把需要的功能加入集合,將集合里面的數據(int)按位與操作后傳入self.setWindowFlags(QtCore.Qt.WindowType(Flags))的Flags中,問題迎刃而解。

疑難

def ChangeWindowFlags(self,init = True):

中init的作用是進行執行控制的,在窗口沒有顯示的時候,Flags是沒有意義的,此時窗口的Visible一定為False,強行設置為True會導致Qt的后續操作異常,所以在窗口正常顯示前的ChangeWindowFlags調用都傳入True,窗口顯示后再調用setWindowFlags時會同時執行hide()導致Visible變為False,這時需要把Visible重新設置為True,使得窗口重新顯示,所以ChangeWindowFlags傳入False


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM