前言
近些年,随着智能手机的普及和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美化就不详述了。
亲眼所见,亦非真实;
一切恐惧,源于未知。
——《第五人格》
评论
那个frame的定位我不是很懂,我改了frame的名字,但是点击还是没有效果
龙哥 那个QFrame部件只是为了生成一个区域,用于判断在点击左键时鼠标的位置是否在区域内,目的是让整个窗口中只有那个区域内可以拖动窗口。
点击没反应是正常的,拖拽如果没反应,那你可以打一下日志看看在哪里出bug了。
yumefx 拖拽没有反应,我设置了如果有鼠标点击行为就输出值,结果没有值输出,弄一整天了,搞不懂
突然发现你也是两年前接触的python,真巧
我知道哪里出问题了,我用的动态获得ui文件,把ui文件转化成py文件就行了
龙哥 解决了就好(・∀・)。
大哥呀我是个小白,那个ui继承要怎么结合那个去除边框的类呀
joe 去除边框这个功能只是设置一个属性而已,不是单独的类,因此按照正常的方式自定义一个类继承ui文件生成的类,在自定义类里设置该属性即可。
yumefx 谢谢大哥的解惑୧(๑•̀⌄•́๑)૭👍
设置了为啥子不行啊
匿名 talk is cheap,show me code.(o`•ω•)ノ
150516 750597I like the valuable information you provide inside your articles. Ill bookmark your weblog and check again here regularly. Im quite certain Ill learn plenty of new stuff proper here! Greatest of luck for the next! 424899
884246 152631This design is spectacular! You clearly know how to keep a reader amused. Between your wit and your videos, I was almost moved to start my own weblog (effectively, almostHaHa!) Great job. I really enjoyed what you had to say, and more than that, how you presented it. Too cool! 525736
394352 662309Precisely what I was searching for, thankyou for putting up. 754145