PyQt5实现无边框窗口的标题拖动和窗口缩放

作者:专注划水 时间:2023-01-17 20:09:45 

网上找了半天都找不到好用的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

标签:PyQt5,边框,窗口
0
投稿

猜你喜欢

  • python打印当前文件的绝对路径并解决打印为空的问题

    2023-10-26 16:24:00
  • SQL Server在AlwaysOn中使用内存表的“踩坑”记录

    2024-01-26 03:49:10
  • MySQL 1303错误的解决方法(navicat)

    2024-01-25 21:24:42
  • Python字符串处理函数简明总结

    2022-07-01 02:03:35
  • Python实现的统计文章单词次数功能示例

    2022-03-15 11:38:07
  • 详解Python为什么不用设计模式

    2022-03-30 16:17:12
  • Python中的列表及其操作方法

    2022-05-24 06:19:41
  • Python连接mysql方法及常用参数

    2024-01-15 00:33:54
  • python 采集中文乱码问题的完美解决方法

    2021-05-13 13:46:58
  • python中return如何写

    2023-11-17 21:44:56
  • MySQL为何不建议使用默认值为null列

    2024-01-22 17:11:10
  • 网页代码更清晰高效的一些经验

    2008-05-19 12:23:00
  • 与 Function 和 Object 相关的有趣代码

    2010-05-07 12:29:00
  • python中lambda函数 list comprehension 和 zip函数使用指南

    2021-08-28 22:16:21
  • 使用Python编写基于DHT协议的BT资源爬虫

    2022-05-21 05:21:54
  • 经测试最好用的mysql密码忘记的解决方法

    2024-01-27 17:12:57
  • 使用Python实现Wake On Lan远程开机功能

    2023-07-26 21:44:06
  • 解决Pycharm界面的子窗口不见了的问题

    2022-03-06 15:21:15
  • 基于python的selenium两种文件上传操作实现详解

    2022-01-31 23:02:17
  • Pyqt+matplotlib 实现实时画图案例

    2022-01-06 12:52:23
  • asp之家 网络编程 m.aspxhome.com