前言

近些年,随着智能手机的普及和UI设计的扁平化,桌面客户端程序也流行起了无边框界面的设计,究其原因,一方面是去除掉难看的系统默认标题栏、状态栏和边框,另一方面是可以将菜单以及最小化、关闭等按钮与界面元素统一进行布局设计,让整个界面功能精简、风格统一。

例如下图网易云音乐的客户端标题栏,将搜索、账户以及一些功能按钮整合在一起,这是使用默认的系统标题栏所难以实现的。

因此,今天要介绍的就是在美化PyQt5图形界面时设置无边框的方法。

 

步骤

那么问题来了,把大象放冰箱,不好意思串台了(噗)。

PyQt5实现无边框,总共分几步?

第一步,先把标题栏和边框去掉,状态栏以及菜单栏最好也都去掉,如果需要可以自定义。

第二步,去掉标题栏后,默认的最小化、最大化、关闭按钮也就没了,需要自行创建按钮代替。

第三步,去掉标题栏后,鼠标拖拽移动窗口也就不能实现了,因此要实现鼠标拖拽以及双击最大化、还原等功能。

不过开始第一步之前,先要准备好界面,在设计界面时,我一般使用QtDesigner绘制,然后再将保存的.ui文件转换成.py文件,开发时将UI类进行继承再进行修改。

 

去除边框

设置无边框的代码很简单,如下:

self.setWindowFlag(QtCore.Qt.FramelessWindowHint)

如果想连窗口的背景都去除的话,可以再加上下面的代码。

self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

 

实现最小化、最大化及关闭按钮

界面右上角已经准备好了三个按钮,只是目前没有任何功能,接下来就要将他们的功能实现。

self.pushButton.clicked.connect(self.showMinimized)
self.pushButton_2.clicked.connect(self.maxOrNormal)
self.pushButton_3.clicked.connect(self.queryExit)

# 切换最大化与正常大小
def maxOrNormal(self):
    if self.isMaximized():
        self.showNormal()
    else:
        self.showMaximized()

# 弹出警告提示窗口确认是否要关闭
def queryExit(self):
    res = QtWidgets.QMessageBox.question(self,"Warning","Quit?",QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel)
    if res == QtWidgets.QMessageBox.Yes:
        QtCore.QCoreApplication.instance().exit()

这样这三个按钮功能就与原来的标题栏按钮一致了。

 

实现鼠标拖拽功能

实现鼠标拖拽功能可以通过重写鼠标事件来实现,在鼠标按下后记录当前位置,鼠标移动时让窗口跟随鼠标移动距离的变化而移动。

同时为避免鼠标拖拽的作用范围太大,在设计窗口时我在自定义的标题栏下放了一个QFrame部件,只有在这个范围内拖拽窗口才会移动。代码如下:

_startPos = None
_endPos = None
_isTracking = None

# 鼠标移动事件
def mouseMoveEvent(self, a0: QtGui.QMouseEvent):
    if self._startPos:
        self._endPos = a0.pos() - self._startPos
        # 移动窗口
        self.move(self.pos() + self._endPos)

# 鼠标按下事件
def mousePressEvent(self, a0: QtGui.QMouseEvent):
    # 根据鼠标按下时的位置判断是否在QFrame范围内
    if self.childAt(a0.pos().x(),a0.pos().y()).objectName() == "frame":
        # 判断鼠标按下的是左键
        if a0.button() == QtCore.Qt.LeftButton:
            self._isTracking = True
            # 记录初始位置
            self._startPos = QtCore.QPoint(a0.x(), a0.y())

# 鼠标松开事件
def mouseReleaseEvent(self, a0: QtGui.QMouseEvent):
    if a0.button() == QtCore.Qt.LeftButton:
        self._isTracking = False
        self._startPos = None
        self._endPos = None

# 鼠标双击事件
def mouseDoubleClickEvent(self, a0: QtGui.QMouseEvent):
    if self.childAt(a0.pos().x(),a0.pos().y()).objectName() == "frame":
        if a0.button() == QtCore.Qt.LeftButton:
            self.maxOrNormal()

大致效果如下:

这样,一个无边框的UI界面就完成了,后面的QSS美化就不详述了。

 


亲眼所见,亦非真实;

一切恐惧,源于未知。

——《第五人格》