PyQt5实现无边框窗口的标题拖动和窗口缩放
作者:专注划水 发布时间:2023-01-17 20:09:45
标签:PyQt5,边框,窗口
网上找了半天都找不到好用的PyQt5无边框窗口的实现,借鉴部分前辈的窗口拖放代码,自己实现了一下无边框窗口,问题可能还有一点,慢慢改吧
先做个笔记
py文件
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from PyQt5.QtWidgets import QWidget, QLabel, QPushButton, QVBoxLayout
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtGui import QFont, QCursor
class QTitleLabel(QLabel):
"""
新建标题栏标签类
"""
def __init__(self, *args):
super(QTitleLabel, self).__init__(*args)
self.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self.setFixedHeight(30)
class QTitleButton(QPushButton):
"""
新建标题栏按钮类
"""
def __init__(self, *args):
super(QTitleButton, self).__init__(*args)
self.setFont(QFont("Webdings")) # 特殊字体以不借助图片实现最小化最大化和关闭按钮
self.setFixedWidth(40)
class QUnFrameWindow(QWidget):
"""
无边框窗口类
"""
def __init__(self):
super(QUnFrameWindow, self).__init__(None, Qt.FramelessWindowHint) # 设置为顶级窗口,无边框
self._padding = 5 # 设置边界宽度为5
self.initTitleLabel() # 安放标题栏标签
self.setWindowTitle = self._setTitleText(self.setWindowTitle) # 用装饰器将设置WindowTitle名字函数共享到标题栏标签上
self.setWindowTitle("UnFrameWindow")
self.initLayout() # 设置框架布局
self.setMinimumWidth(250)
self.setMouseTracking(True) # 设置widget鼠标跟踪
self.initDrag() # 设置鼠标跟踪判断默认值
def initDrag(self):
# 设置鼠标跟踪判断扳机默认值
self._move_drag = False
self._corner_drag = False
self._bottom_drag = False
self._right_drag = False
def initTitleLabel(self):
# 安放标题栏标签
self._TitleLabel = QTitleLabel(self)
self._TitleLabel.setMouseTracking(True) # 设置标题栏标签鼠标跟踪(如不设,则标题栏内在widget上层,无法实现跟踪)
self._TitleLabel.setIndent(10) # 设置标题栏文本缩进
self._TitleLabel.move(0, 0) # 标题栏安放到左上角
def initLayout(self):
# 设置框架布局
self._MainLayout = QVBoxLayout()
self._MainLayout.setSpacing(0)
self._MainLayout.addWidget(QLabel(), Qt.AlignLeft) # 顶一个QLabel在竖放框架第一行,以免正常内容挤占到标题范围里
self._MainLayout.addStretch()
self.setLayout(self._MainLayout)
def addLayout(self, QLayout):
# 给widget定义一个addLayout函数,以实现往竖放框架的正确内容区内嵌套Layout框架
self._MainLayout.addLayout(QLayout)
def _setTitleText(self, func):
# 设置标题栏标签的装饰器函数
def wrapper(*args):
self._TitleLabel.setText(*args)
return func(*args)
return wrapper
def setTitleAlignment(self, alignment):
# 给widget定义一个setTitleAlignment函数,以实现标题栏标签的对齐方式设定
self._TitleLabel.setAlignment(alignment | Qt.AlignVCenter)
def setCloseButton(self, bool):
# 给widget定义一个setCloseButton函数,为True时设置一个关闭按钮
if bool == True:
self._CloseButton = QTitleButton(b'\xef\x81\xb2'.decode("utf-8"), self)
self._CloseButton.setObjectName("CloseButton") # 设置按钮的ObjectName以在qss样式表内定义不同的按钮样式
self._CloseButton.setToolTip("关闭窗口")
self._CloseButton.setMouseTracking(True) # 设置按钮鼠标跟踪(如不设,则按钮在widget上层,无法实现跟踪)
self._CloseButton.setFixedHeight(self._TitleLabel.height()) # 设置按钮高度为标题栏高度
self._CloseButton.clicked.connect(self.close) # 按钮信号连接到关闭窗口的槽函数
def setMinMaxButtons(self, bool):
# 给widget定义一个setMinMaxButtons函数,为True时设置一组最小化最大化按钮
if bool == True:
self._MinimumButton = QTitleButton(b'\xef\x80\xb0'.decode("utf-8"), self)
self._MinimumButton.setObjectName("MinMaxButton") # 设置按钮的ObjectName以在qss样式表内定义不同的按钮样式
self._MinimumButton.setToolTip("最小化")
self._MinimumButton.setMouseTracking(True) # 设置按钮鼠标跟踪(如不设,则按钮在widget上层,无法实现跟踪)
self._MinimumButton.setFixedHeight(self._TitleLabel.height()) # 设置按钮高度为标题栏高度
self._MinimumButton.clicked.connect(self.showMinimized) # 按钮信号连接到最小化窗口的槽函数
self._MaximumButton = QTitleButton(b'\xef\x80\xb1'.decode("utf-8"), self)
self._MaximumButton.setObjectName("MinMaxButton") # 设置按钮的ObjectName以在qss样式表内定义不同的按钮样式
self._MaximumButton.setToolTip("最大化")
self._MaximumButton.setMouseTracking(True) # 设置按钮鼠标跟踪(如不设,则按钮在widget上层,无法实现跟踪)
self._MaximumButton.setFixedHeight(self._TitleLabel.height()) # 设置按钮高度为标题栏高度
self._MaximumButton.clicked.connect(self._changeNormalButton) # 按钮信号连接切换到恢复窗口大小按钮函数
def _changeNormalButton(self):
# 切换到恢复窗口大小按钮
try:
self.showMaximized() # 先实现窗口最大化
self._MaximumButton.setText(b'\xef\x80\xb2'.decode("utf-8")) # 更改按钮文本
self._MaximumButton.setToolTip("恢复") # 更改按钮提示
self._MaximumButton.disconnect() # 断开原本的信号槽连接
self._MaximumButton.clicked.connect(self._changeMaxButton) # 重新连接信号和槽
except:
pass
def _changeMaxButton(self):
# 切换到最大化按钮
try:
self.showNormal()
self._MaximumButton.setText(b'\xef\x80\xb1'.decode("utf-8"))
self._MaximumButton.setToolTip("最大化")
self._MaximumButton.disconnect()
self._MaximumButton.clicked.connect(self._changeNormalButton)
except:
pass
def resizeEvent(self, QResizeEvent):
# 自定义窗口调整大小事件
self._TitleLabel.setFixedWidth(self.width()) # 将标题标签始终设为窗口宽度
# 分别移动三个按钮到正确的位置
try:
self._CloseButton.move(self.width() - self._CloseButton.width(), 0)
except:
pass
try:
self._MinimumButton.move(self.width() - (self._CloseButton.width() + 1) * 3 + 1, 0)
except:
pass
try:
self._MaximumButton.move(self.width() - (self._CloseButton.width() + 1) * 2 + 1, 0)
except:
pass
# 重新调整边界范围以备实现鼠标拖放缩放窗口大小,采用三个列表生成式生成三个列表
self._right_rect = [QPoint(x, y) for x in range(self.width() - self._padding, self.width() + 1)
for y in range(1, self.height() - self._padding)]
self._bottom_rect = [QPoint(x, y) for x in range(1, self.width() - self._padding)
for y in range(self.height() - self._padding, self.height() + 1)]
self._corner_rect = [QPoint(x, y) for x in range(self.width() - self._padding, self.width() + 1)
for y in range(self.height() - self._padding, 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._TitleLabel.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:
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)
else:
self.setCursor(Qt.ArrowCursor)
# 当鼠标左键点击不放及满足点击区域的要求后,分别实现不同的窗口调整
# 没有定义左方和上方相关的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
if __name__ == "__main__":
from PyQt5.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)
app.setStyleSheet(open("./UnFrameStyle.qss").read())
window = QUnFrameWindow()
window.setCloseButton(True)
window.setMinMaxButtons(True)
window.show()
sys.exit(app.exec_())
qss文件
/**********Title**********/
QTitleLabel{
background-color: Gainsboro;
font: 100 10pt;
}
/**********Button**********/
QTitleButton{
background-color: rgba(255, 255, 255, 0);
color: black;
border: 0px;
font: 100 10pt;
}
QTitleButton#MinMaxButton:hover{
background-color: #D0D0D1;
border: 0px;
font: 100 10pt;
}
QTitleButton#CloseButton:hover{
background-color: #D32424;
color: white;
border: 0px;
font: 100 10pt;
}
来源:https://blog.csdn.net/qq_38528972/article/details/78573591
![](https://www.aspxhome.com/images/zang.png)
![](https://www.aspxhome.com/images/jiucuo.png)
猜你喜欢
- 背景在vscode刚刚装好的时候,对于开发人员来说可能需要写一些模块的测试,而这个模块可能又引用了其他模块,如果是同级目录的话可能会出现Mo
- 在GitHub上发现一些很有意思的项目,由于本人作为Python的初学者,编程代码能力相对薄弱,为了加强Python的学习,特此利用前辈们的
- 当你连接一个MySQL服务器时,你通常应该使用一个口令。口令不以明文在连接上传输。所有其它信息作为能被任何人读懂的文本被传输。如果你担心这个
- 前几天写了一个ajax的,总感觉代码比较多,今天晚上又得写了一下,感觉代码还是比较多,但还好的是,比较通用。谁有办法优化一下当然好。&nbs
- 不得不说使用python库matplotlib绘图确实比较丑,但使用起来还算是比较方便,做自己的小小研究可以使用。这里记录一些统计作图方法,
- use mysql; u
- 建立完回归模型后,还需要验证咱们建立的模型是否合适,换句话说,就是咱们建立的模型是否真的能代表现有的因变量与自变量关系,这个验证标准一般就选
- 本文实例讲述了Laravel框架文件上传功能实现方法。分享给大家供大家参考,具体如下:以Laravel 5.2.45 框架为主,进行文件上传
- 1. 从键盘输入一个整数,求 100 除以它的商,并显示输出。要求对从键盘输入的数值进行异常处理。try: n=i
- 突然有个想法,不知道是不是首创:用"表情符号"做植入广告. 目前的表情符号 "黄色小圆脸"系列可以说
- 1,七层网络协议应表会传网数物:应用层、表示层、会话层: (这三层又可以合并为应用层,这样就是五层网络协议【osi五层协议】) python
- pytorch中训练完网络后,需要对学习的结果进行测试。官网上例程用的方法统统都是正确率,使用的是torch.eq()这个函数。但是为了更精
- 目录什么是websocket?第一步 准备工作第二步 编写聊天室页面第三步 编写后台websocket路由及处理方法第四步 运行看效果小结C
- 折线图是数据分析的一种手段,但是有时候我们也需要柱状图进行不同数据的可视化量化对比。使用pandas的DataFrame方法进行柱状图的绘制
- 原来字母还可以组合成各种动物图案,真是佩服设计师的奇思妙想,很可爱,超级有趣的组合!Bembo's Zoo 猴子:羊是牛吗,勤劳的水
- 序列是Python中最基本的数据结构。序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推。Pyt
- 现有两个元组(('a'),('b')),(('c'),('d')),请使用p
- 作者:Roland Smart原文链接:http://www.adaptivepath.com/ideas/newsletter/archi
- 分页功能在每个网站都是必要的,对于分页来说,其实就是根据用户的输入计算出应该显示在页面上的数据在数据库表中的起始位置。确定分页需求:1. 每
- 前言Python 字典 update()方法用于更新字典中的键/值对,可以修改存在的键对应的值,也可以添加新的键/值对到字典中。语法格式d.