前言
近些年,随着智能手机的普及和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`•ω•)ノ
Howdy would you mind letting me know which web host you’re working with? I’ve loaded your blog in 3 completely different browsers and I must say this blog loads a lot faster then most. Can you recommend a good web hosting provider at a honest price? Cheers, I appreciate it!
https://youtu.be/A2pZ5Tfoes8