PyQt5 自定义标题栏,实现无边框,最小化最大化关闭事件,窗口拖动移动,窗口改变大小,仿百度网盘色调美化
文章目录
1.实现效果
文件框架
|
|-TitleTest.py
ui代码
|
|-CallTitleTest.py
逻辑代码
|
2. Qtdesigner设计(ui设计代码)
设计图:
布局
具体控件设置请看 ui的py文件
请留意 3 个pushButton
和 2 个lable
的大小设计
TitleTest.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'TitleTest.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(648, 432)
self.verticalLayout = QtWidgets.QVBoxLayout(Form)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName("verticalLayout")
self.widget = QtWidgets.QWidget(Form)
self.widget.setMaximumSize(QtCore.QSize(16777215, 35))
self.widget.setObjectName("widget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setSpacing(0)
self.horizontalLayout.setObjectName("horizontalLayout")
spacerItem = QtWidgets.QSpacerItem(5, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.label = QtWidgets.QLabel(self.widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(20)
sizePolicy.setVerticalStretch(20)
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy)
self.label.setMaximumSize(QtCore.QSize(20, 20))
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
spacerItem1 = QtWidgets.QSpacerItem(5, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.label_2 = QtWidgets.QLabel(self.widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(30)
sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
self.label_2.setSizePolicy(sizePolicy)
self.label_2.setObjectName("label_2")
self.horizontalLayout.addWidget(self.label_2)
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem2)
self.pushButton = QtWidgets.QPushButton(self.widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(20)
sizePolicy.setVerticalStretch(20)
sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth())
self.pushButton.setSizePolicy(sizePolicy)
self.pushButton.setMinimumSize(QtCore.QSize(0, 0))
self.pushButton.setMaximumSize(QtCore.QSize(20, 20))
self.pushButton.setObjectName("pushButton")
self.horizontalLayout.addWidget(self.pushButton)
spacerItem3 = QtWidgets.QSpacerItem(8, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem3)
self.pushButton_2 = QtWidgets.QPushButton(self.widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(20)
sizePolicy.setVerticalStretch(20)
sizePolicy.setHeightForWidth(self.pushButton_2.sizePolicy().hasHeightForWidth())
self.pushButton_2.setSizePolicy(sizePolicy)
self.pushButton_2.setMaximumSize(QtCore.QSize(20, 20))
self.pushButton_2.setObjectName("pushButton_2")
self.horizontalLayout.addWidget(self.pushButton_2)
spacerItem4 = QtWidgets.QSpacerItem(8, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem4)
self.pushButton_3 = QtWidgets.QPushButton(self.widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(20)
sizePolicy.setVerticalStretch(20)
sizePolicy.setHeightForWidth(self.pushButton_3.sizePolicy().hasHeightForWidth())
self.pushButton_3.setSizePolicy(sizePolicy)
self.pushButton_3.setMaximumSize(QtCore.QSize(20, 20))
self.pushButton_3.setObjectName("pushButton_3")
self.horizontalLayout.addWidget(self.pushButton_3)
spacerItem5 = QtWidgets.QSpacerItem(5, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem5)
self.verticalLayout.addWidget(self.widget)
self.widget_2 = QtWidgets.QWidget(Form)
self.widget_2.setObjectName("widget_2")
self.verticalLayout.addWidget(self.widget_2)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.label.setText(_translate("Form", "图标"))
self.label_2.setText(_translate("Form", "TextLabel"))
self.pushButton.setToolTip(_translate("Form", "<html><head/><body><p>最小化</p></body></html>"))
self.pushButton.setText(_translate("Form", "-"))
self.pushButton_2.setToolTip(_translate("Form", "<html><head/><body><p>最大化</p></body></html>"))
self.pushButton_2.setText(_translate("Form", "O"))
self.pushButton_3.setToolTip(_translate("Form", "<html><head/><body><p>关闭</p></body></html>"))
self.pushButton_3.setText(_translate("Form", "X"))
3. 设置无边框,标题和图标
注:完整的逻辑代码请看最下面的CallTitleTest.py
文件代码
单独设置一个函数来整理无边框,标题和图标设置_init_main_window
,如果全部放在__init__
初始化函数里,会很混乱的
def _init_main_window(self):
# 设置窗体无边框
self.setWindowFlags(Qt.FramelessWindowHint)
# 设置背景透明
self.setAttribute(Qt.WA_TranslucentBackground)
########在测试时记得设置自己的图标地址
# 设置图标
w = self.label.width()
h = self.label.height()
self.pix = QPixmap(r"D:\code_python\CalPx_test\images\481ee7d90dfcc5c5418eaa8bf94370f2_cr.jpg") # 注意修改Windows路径问题
self.label.setPixmap(self.pix)
self.label.setScaledContents(True)
# 设置标题
self.label_2.setText('我的APP')
# 设置标题字体,大小
self.label_2.setStyleSheet('''
font-family:"方正胖娃_GBK";
font-size:11px;
''')
########在测试时记得设置自己的图标地址
我是用两个 QLable 标签控件来放置图标和标题的,用QSS进行一定美化
要在__init__
初始化函数里初始化函数
self. _init_main_window()
4.最大化最小化关闭事件
最大化最小化关闭事件是通过三个按钮来实现
编写三个按钮的信号与槽函数
def _close_max_min_icon(self):
self.pushButton_3.setText('r')
self.pushButton_2.setText('1')
self.pushButton.setText('0')
@pyqtSlot()
def on_pushButton_clicked(self):
# 最小化
self.showMinimized()
@pyqtSlot()
def on_pushButton_2_clicked(self):
# 最大化与复原
if self.isMaximized():
self.showNormal()
self.pushButton_2.setText('1') # 切换放大按钮图标字体
self.pushButton_2.setToolTip("<html><head/><body><p>最大化</p></body></html>")
else:
self.showMaximized()
self.pushButton_2.setText('2')
self.pushButton_2.setToolTip("<html><head/><body><p>恢复</p></body></html>")
@pyqtSlot()
def on_pushButton_3_clicked(self):
# 关闭程序
self.close()
def _close_max_min_icon(self):
函数不是按钮的信号与槽函数,在这里说是因为它是设置三个按钮的图标关键字,使用了webdings
字体,需要在__init__
初始化
5.窗口拖动移动,窗口改变大小
在__init__
初始化函数里声明代码并初始化一些函数
self._initDrag() # 设置鼠标跟踪判断扳机默认值
self.setMouseTracking(True) # 设置widget鼠标跟踪
self.widget.installEventFilter(self) # 初始化事件过滤器
self.widget_2.installEventFilter(self)
事件过滤器函数
def eventFilter(self, obj, event):
# 事件过滤器,用于解决鼠标进入其它控件后还原为标准鼠标样式
if isinstance(event, QEnterEvent):
self.setCursor(Qt.ArrowCursor)
return super(MyWindow, self).eventFilter(obj, event) # 注意 ,MyWindow是所在类的名称
# return QWidget.eventFilter(self, obj, event) # 用这个也行,但要注意修改窗口类型
因为设置了主窗口透明化,所以要专门用这个过滤器来恢复鼠标形状,两个子窗口
widget
和widget_2
都要经过这个过滤器
鼠标事件
def resizeEvent(self, QResizeEvent):
# 自定义窗口调整大小事件
# 改变窗口大小的三个坐标范围
self._right_rect = [QPoint(x, y) for x in range(self.width() - 5, self.width() + 5)
for y in range(self.widget.height() + 20, self.height() - 5)]
self._bottom_rect = [QPoint(x, y) for x in range(1, self.width() - 5)
for y in range(self.height() - 5, self.height() + 1)]
self._corner_rect = [QPoint(x, y) for x in range(self.width() - 5, self.width() + 1)
for y in range(self.height() - 5, self.height() + 1)]
def mousePressEvent(self, event):
# 重写鼠标点击的事件
if (event.button() == Qt.LeftButton) and (event.pos() in self._corner_rect):
# 鼠标左键点击右下角边界区域
self._corner_drag = True
event.accept()
elif (event.button() == Qt.LeftButton) and (event.pos() in self._right_rect):
# 鼠标左键点击右侧边界区域
self._right_drag = True
event.accept()
elif (event.button() == Qt.LeftButton) and (event.pos() in self._bottom_rect):
# 鼠标左键点击下侧边界区域
self._bottom_drag = True
event.accept()
elif (event.button() == Qt.LeftButton) and (event.y() < self.widget.height()):
# 鼠标左键点击标题栏区域
self._move_drag = True
self.move_DragPosition = event.globalPos() - self.pos()
event.accept()
def mouseMoveEvent(self, QMouseEvent):
# 判断鼠标位置切换鼠标手势
if QMouseEvent.pos() in self._corner_rect: # QMouseEvent.pos()获取相对位置
self.setCursor(Qt.SizeFDiagCursor)
elif QMouseEvent.pos() in self._bottom_rect:
self.setCursor(Qt.SizeVerCursor)
elif QMouseEvent.pos() in self._right_rect:
self.setCursor(Qt.SizeHorCursor)
# 当鼠标左键点击不放及满足点击区域的要求后,分别实现不同的窗口调整
# 没有定义左方和上方相关的5个方向,主要是因为实现起来不难,但是效果很差,拖放的时候窗口闪烁,再研究研究是否有更好的实现
if Qt.LeftButton and self._right_drag:
# 右侧调整窗口宽度
self.resize(QMouseEvent.pos().x(), self.height())
QMouseEvent.accept()
elif Qt.LeftButton and self._bottom_drag:
# 下侧调整窗口高度
self.resize(self.width(), QMouseEvent.pos().y())
QMouseEvent.accept()
elif Qt.LeftButton and self._corner_drag:
# 由于我窗口设置了圆角,这个调整大小相当于没有用了
# 右下角同时调整高度和宽度
self.resize(QMouseEvent.pos().x(), QMouseEvent.pos().y())
QMouseEvent.accept()
elif Qt.LeftButton and self._move_drag:
# 标题栏拖放窗口位置
self.move(QMouseEvent.globalPos() - self.move_DragPosition)
QMouseEvent.accept()
def mouseReleaseEvent(self, QMouseEvent):
# 鼠标释放后,各扳机复位
self._move_drag = False
self._corner_drag = False
self._bottom_drag = False
self._right_drag = False
def resizeEvent(self, QResizeEvent)
通过这个函数获取改变大小的坐标范围,只有鼠标处于这个范围,就可以改变程序窗口大小。我只设置了三个方向可以改变窗口大小,其他的可以自行修改。
坐标问题:
self.width()
获取主窗口的宽,self.height()
获取主窗口的高相对应的
self.widget.width()
是获取子窗口widget
的宽
event.pos()
和QMouseEvent.pos()
获取相对于软件窗口的像素坐标
event.globalPos()
和QMouseEvent.globalPos()
获取相对于屏幕的像素坐标后面的x,y分别是其的x坐标和y坐标
6.仿百度网盘色调QSS美化
设置背景色
百度网盘程序的标题栏的颜色便灰,内容窗口便白色,使用屏幕拾色器获取具体颜色编码。
QSS代码
QWidget#widget{background-color:#eef0f6;
border-left:0.5px solid lightgray;
border-right:0.5px solid lightgray;
border-top:0.5px solid lightgray;
border-bottom:0.5px solid #e5e5e5;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
QWidget#widget_2{background-color:#ffffff;
border-left:0.5px solid lightgray;
border-right:0.5px solid lightgray;
border-bottom:0.5px solid #e5e5e5;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
padding:5px 5px 5px 5px
}
美化三个按钮
QSS代码
QPushButton#pushButton
{
font-family:"Webdings";
text-align:top;
background:#6DDF6D;border-radius:5px;
border:none;
font-size:13px;
}
QPushButton#pushButton:hover{background:green;}
QPushButton#pushButton_2
{
font-family:"Webdings";
background:#F7D674;border-radius:5px;
border:none;
font-size:13px;
}
QPushButton#pushButton_2:hover{background:yellow;}
QPushButton#pushButton_3
{
font-family:"Webdings";
background:#F76677;border-radius:5px;
border:none;
font-size:13px;
}
QPushButton#pushButton_3:hover{background:red;}
7.完整逻辑代码 CallTitleTest.py
CallTitleTest.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# author:mgboy time:2020/8/5
import sys
from PyQt5.QtCore import pyqtSlot, Qt, QPoint
from PyQt5.QtGui import QFont, QEnterEvent, QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QDialog
from PyQt5 import QtCore
from TitleTest import Ui_Form
class MyWindow(QWidget, Ui_Form):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.setupUi(self)
self._init_main_window() # 主窗口初始化设置
self._initDrag() # 设置鼠标跟踪判断扳机默认值
self.setMouseTracking(True) # 设置widget鼠标跟踪
self._close_max_min_icon() # 设置 3 个按钮的图标字体
print(self.width(),self.height())
self.my_Qss() # 美化
self.widget.installEventFilter(self) # 初始化事件过滤器
self.widget_2.installEventFilter(self)
def _init_main_window(self):
# 设置窗体无边框
self.setWindowFlags(Qt.FramelessWindowHint)
# 设置背景透明
self.setAttribute(Qt.WA_TranslucentBackground)
########在测试时记得设置自己的图标地址
# 设置图标
w = self.label.width()
h = self.label.height()
self.pix = QPixmap(r"D:\code_python\CalPx_test\images\481ee7d90dfcc5c5418eaa8bf94370f2_cr.jpg") # 注意修改Windows路径问题
self.label.setPixmap(self.pix)
self.label.setScaledContents(True)
# 设置标题
self.label_2.setText('我的APP')
# 设置标题字体,大小
self.label_2.setStyleSheet('''
font-family:"方正胖娃_GBK";
font-size:11px;
''')
def _initDrag(self):
# 设置鼠标跟踪判断扳机默认值
self._move_drag = False
self._corner_drag = False
self._bottom_drag = False
self._right_drag = False
def _close_max_min_icon(self):
# 设置按钮图标使用webdings特殊字体
self.pushButton_3.setText('r')
self.pushButton_2.setText('1')
self.pushButton.setText('0')
@pyqtSlot()
def on_pushButton_clicked(self):
# 最小化
self.showMinimized()
@pyqtSlot()
def on_pushButton_2_clicked(self):
# 最大化与复原
if self.isMaximized():
self.showNormal()
self.pushButton_2.setText('1') # 切换放大按钮图标
self.pushButton_2.setToolTip("<html><head/><body><p>最大化</p></body></html>")
else:
self.showMaximized()
self.pushButton_2.setText('2')
self.pushButton_2.setToolTip("<html><head/><body><p>恢复</p></body></html>")
@pyqtSlot()
def on_pushButton_3_clicked(self):
# 关闭程序
self.close()
def eventFilter(self, obj, event):
# 事件过滤器,用于解决鼠标进入其它控件后还原为标准鼠标样式
if isinstance(event, QEnterEvent):
self.setCursor(Qt.ArrowCursor)
return super(MyWindow, self).eventFilter(obj, event) # 注意 ,MyWindow是所在类的名称
# return QWidget.eventFilter(self, obj, event) # 用这个也行,但要注意修改窗口类型
def resizeEvent(self, QResizeEvent):
# 自定义窗口调整大小事件
# 改变窗口大小的三个坐标范围
self._right_rect = [QPoint(x, y) for x in range(self.width() - 5, self.width() + 5)
for y in range(self.widget.height() + 20, self.height() - 5)]
self._bottom_rect = [QPoint(x, y) for x in range(1, self.width() - 5)
for y in range(self.height() - 5, self.height() + 1)]
self._corner_rect = [QPoint(x, y) for x in range(self.width() - 5, self.width() + 1)
for y in range(self.height() - 5, self.height() + 1)]
def mousePressEvent(self, event):
# 重写鼠标点击的事件
if (event.button() == Qt.LeftButton) and (event.pos() in self._corner_rect):
# 鼠标左键点击右下角边界区域
self._corner_drag = True
event.accept()
elif (event.button() == Qt.LeftButton) and (event.pos() in self._right_rect):
# 鼠标左键点击右侧边界区域
self._right_drag = True
event.accept()
elif (event.button() == Qt.LeftButton) and (event.pos() in self._bottom_rect):
# 鼠标左键点击下侧边界区域
self._bottom_drag = True
event.accept()
elif (event.button() == Qt.LeftButton) and (event.y() < self.widget.height()):
# 鼠标左键点击标题栏区域
self._move_drag = True
self.move_DragPosition = event.globalPos() - self.pos()
event.accept()
def mouseMoveEvent(self, QMouseEvent):
# 判断鼠标位置切换鼠标手势
if QMouseEvent.pos() in self._corner_rect: # QMouseEvent.pos()获取相对位置
self.setCursor(Qt.SizeFDiagCursor)
elif QMouseEvent.pos() in self._bottom_rect:
self.setCursor(Qt.SizeVerCursor)
elif QMouseEvent.pos() in self._right_rect:
self.setCursor(Qt.SizeHorCursor)
# 当鼠标左键点击不放及满足点击区域的要求后,分别实现不同的窗口调整
# 没有定义左方和上方相关的5个方向,主要是因为实现起来不难,但是效果很差,拖放的时候窗口闪烁,再研究研究是否有更好的实现
if Qt.LeftButton and self._right_drag:
# 右侧调整窗口宽度
self.resize(QMouseEvent.pos().x(), self.height())
QMouseEvent.accept()
elif Qt.LeftButton and self._bottom_drag:
# 下侧调整窗口高度
self.resize(self.width(), QMouseEvent.pos().y())
QMouseEvent.accept()
elif Qt.LeftButton and self._corner_drag:
# 由于我窗口设置了圆角,这个调整大小相当于没有用了
# 右下角同时调整高度和宽度
self.resize(QMouseEvent.pos().x(), QMouseEvent.pos().y())
QMouseEvent.accept()
elif Qt.LeftButton and self._move_drag:
# 标题栏拖放窗口位置
self.move(QMouseEvent.globalPos() - self.move_DragPosition)
QMouseEvent.accept()
def mouseReleaseEvent(self, QMouseEvent):
# 鼠标释放后,各扳机复位
self._move_drag = False
self._corner_drag = False
self._bottom_drag = False
self._right_drag = False
def my_Qss(self):
# Qss美化
qssStyle = '''
QWidget#widget{
background-color:#eef0f6;
border-left:0.5px solid lightgray;
border-right:0.5px solid lightgray;
border-top:0.5px solid lightgray;
border-bottom:0.5px solid #e5e5e5;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
QWidget#widget_2{
background-color:#ffffff;
border-left:0.5px solid lightgray;
border-right:0.5px solid lightgray;
border-bottom:0.5px solid #e5e5e5;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
padding:5px 5px 5px 5px
}
QPushButton#pushButton
{
font-family:"Webdings";
text-align:top;
background:#6DDF6D;border-radius:5px;
border:none;
font-size:13px;
}
QPushButton#pushButton:hover{background:green;}
QPushButton#pushButton_2
{
font-family:"Webdings";
background:#F7D674;border-radius:5px;
border:none;
font-size:13px;
}
QPushButton#pushButton_2:hover{background:yellow;}
QPushButton#pushButton_3
{
font-family:"Webdings";
background:#F76677;border-radius:5px;
border:none;
font-size:13px;
}
QPushButton#pushButton_3:hover{background:red;}
'''
self.setStyleSheet(qssStyle)
if __name__ == "__main__":
# 适配2k等高分辨率屏幕,低分辨率屏幕可以缺省
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
myWin = MyWindow()
myWin.show()
sys.exit(app.exec_())
.
要加入其它控件,可以先使用designer
设计一个QWidget
类窗口,再加入widget_2
子窗口即可。最近会写一下怎么加。设计时要注意widget_2
子窗口的大小是648*397
,最好按照这个大小再来设计。也可以把标题类设置为一个接口文件,调用即可,不过这要修改逻辑代码。
.
.
.
.