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)

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

PyQt 动态 setWindowFlags

有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,果然被我猜中:

PyQt 动态 setWindowFlags

其余的在这里:

WindowStaysOnTopHint = 262144
Tool = 11
FramelessWindowHint = 2048

我们模拟一下合并的过程:

PyQt 动态 setWindowFlags

十进制下是:264,203?

可以看出每一个二进制位都应该充当开关的作用,控制着一些东西。

PyQt 动态 setWindowFlags

argument 1 has unexpected type ‘int‘

这样想就被打脸了,因为Qt的想法很奇妙:

print(type(QtCore.Qt.WindowStaysOnTopHint))

返回结果:

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

这说明我们传入的QtCore.Qt.xxx其实在Python里面是被WindowType这个class接管的,那么我们找到它:

PyQt 动态 setWindowFlags

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

PyQt 动态 setWindowFlags

上一篇:WebApi


下一篇:.NET Core(C#) 使用IHttpClientFactory实现爬虫执行GET和POST请求