Pyside | PYQT无边框设计,窗口移动和调整大小的简单写法

原创声明:本文系作者授权CSDN平台发表,未经许可,不得转载。


无边框设计和功能的简单方法

  • 前言
  • 一、去掉系统边框、背景透明
  • 二、边框添加阴影
  • 三、窗口调整大小
    • 1. 窗口边缘改变鼠标样式
    • 2. 调整窗口大小
    • 3. 事件过滤器
  • 四、窗口移动
  • 总结
  • 完整整合
    • 1. 窗口定制模块
    • 2. ui定制模块
  • *如果这篇文章对你有帮助,那就点个赞呗!*


代码已上传到 gitee 仓库,文章浏览完毕后方可获取仓库地址

前言

提示:如有错误或更好的建议请在评论区指出或私信

如果把系统自带边框去掉后,窗口移动和调整大小以及最大化、最小化等功能就消失了。网上有很多无边框设计的模板,但是大多都是通过各种计算来判断位置以及各种计算来改变窗口大小和移动窗口,代码量很长而且略复杂。所以本次对无边框设计做个优化,来简化代码量和代码逻辑。


一、去掉系统边框、背景透明

  • 去掉系统边框的函数为setWindowFlags()
    • 参数需需要填全,否则一些功能丢失,比如点击任务栏无法最小化等
      (Qt.Window | Qt.FramelessWindowHint | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint| Qt.WindowMaximizeButtonHint)
  • 窗口透明函数setAttribute(Qt.WA_TranslucentBackground)

代码如下(示例):

class CustomWindow(QMainWindow):
	def __init__(self):
		super().__init__()
		self.set_window()  # 定制窗口
		
    def set_window(self):
       # 关闭系统标题栏
       self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint
                           | Qt.WindowMaximizeButtonHint)
       # 透明背景
       self.setAttribute(Qt.WA_TranslucentBackground)

二、边框添加阴影

因为背景透明,如若不添加阴影那么定位窗口边缘时无法定位(透明的,相当于窗口没有这一部分了)

代码如下(示例):

class CustomWindow(QMainWindow):
	def __init__(self):
		super().__init__()
		
		# 边界以透明,设置边框阴影效果,否则无法定位边界
		self.shadow = QGraphicsDropShadowEffect(self)
		self.shadow.setBlurRadius(15)
		self.shadow.setColor(QColor(0, 0, 0, 150))
		self.shadow.setOffset(0, 0)
		self.setGraphicsEffect(self.shadow)

三、窗口调整大小

窗口调整大小主要用到QT的鼠标事件功能,结合窗口调整大小内置函数来实现;
窗口需要开启鼠标跟踪self.setMouseTracking(True)
备注:控件的父类也开启跟踪,否则只有在按下鼠标左键的时候才跟踪

1. 窗口边缘改变鼠标样式

  • 利用鼠标移动事件来判断
  • 利用QRect来圈定窗口边界范围,QRect的参数为(左上角x, 左上角y, 右下角x, 右下角y);
  • event.pos()返回当前鼠标位置;
  • 检查鼠标是否在这个矩形范围QRect.contains(event.pos()) 返回bool值;

所以当鼠标在矩形范围内返回True时,更改鼠标的 调整大小的样式,并且给类定义一个方向属性,返回当前所在哪个矩形范围内。

代码如下(示例):

# 窗口边缘设置鼠标手势(右侧、底部、右下角)
    def mouseMoveEvent(self, event):

        # QRect边界范围(左上角x, 左上角y, 右下角x, 右下角y)
        # 右侧边缘(窗口宽度减去边框宽度,0, 窗口宽度, 窗口高度)
        self.right_edge = QRect(self.width() - 10, 0, self.width(), self.height())
        # 底部边缘(不在具体说明)
        self.bottom_edge = QRect(0, self.height() - 10, self.width() - 25, self.height())
        # 右下角边缘(不在具体说明)
        self.right_bottom_edge = QRect(self.width() - 25, self.height() - 10, self.width(), self.height())
        
        # 根据鼠标在窗口的位置 改变鼠标手势
        if not self.isMaximized():
            if self.bottom_edge.contains(event.pos()):
                self.setCursor(Qt.SizeVerCursor)  # 改变鼠标样式为上下调整大小样式
                self.direction = "bottom"  # 方向属性为下
            elif self.right_edge.contains(event.pos()):
                self.setCursor(Qt.SizeHorCursor)
                self.direction = "right"
            elif self.right_bottom_edge.contains(event.pos()):
                self.setCursor(Qt.SizeFDiagCursor)
                self.direction = "right_bottom"

2. 调整窗口大小

  • 利用鼠标点击事件来执行
  • 结合鼠标移动事件的方向属性来判断调整策略
  • 使用startSystemResize()内置函数来调整窗口大小
    代码如下(示例):
# 窗口边缘调整窗口大小(右侧、底部、右下角)
    def mousePressEvent(self, event):

        if event.button() == Qt.LeftButton:
            # 鼠标左键点击下边界区域 调整大小
            if self.direction == 'bottom':
                self.windowHandle().startSystemResize(Qt.BottomEdge)
            # 鼠标左键点击右边界区域 调整大小
            elif self.direction == 'right':
                self.windowHandle().startSystemResize(Qt.RightEdge)
            elif self.direction == 'right_bottom':
                self.windowHandle().startSystemResize(Qt.RightEdge | Qt.BottomEdge)

3. 事件过滤器

  • 事件过滤器, 鼠标进入其它控件后还原为标准鼠标样式, 方向属性变为None
  • 还需要应用该过滤器self.ui.xxxx.installEventFilter(self)xxxx建议是整个窗口的QFrame控件
    def eventFilter(self, obj, event):

        if isinstance(event, QEnterEvent):
            self.setCursor(Qt.ArrowCursor)
            self.direction = None
        return super().eventFilter(obj, event)

四、窗口移动

使用startSystemMove()内置函数来完成功能
结合控件的内置鼠标事件绑定即可

# 自定义标题栏
    def custom_titlebar(self):

        # 窗口移动
        def move_titlebar(_event):
        	self.windowHandle().startSystemMove()

        self.ui.AppDes.mouseMoveEvent = move_titlebar  # 标题栏移动信号绑定窗口移动事件

        # 标题栏双击最大化|还原
        def doubleclick_titlebar(event):
            if event.button() == Qt.LeftButton:
                self.max_restore()

        self.ui.AppDes.mouseDoubleClickEvent = doubleclick_titlebar  # 标题栏双击信号绑定窗口最大化|还原事件

        # 按钮[最小化, 最大化|还原, 关闭程序]
        self.ui.BtnMin.clicked.connect(self.showMinimized)  # 最小化
        self.ui.BtnMaxRestore.clicked.connect(self.max_restore)  # 最大化|还原
        self.ui.BtnClose.clicked.connect(self.close)  # 关闭程序

总结

主要使用了startSystemMove()startSystemResize()这两个内置方法来完成复杂的计算和功能的实现。
这两个内置函数是QT在5.15版本在QWindow中提供了两个新的方法。

完整整合

1. 窗口定制模块

"""
取消系统标题栏, 定制窗口.
    1. 关闭系统标题栏, 窗口透明
    2. 调整窗口大小(右下角, 右|下边缘), 边缘鼠标样式
    3. 事件过滤器
    4. 窗体添加边缘阴影
"""

from PySide2.QtCore import Qt, QRect
from PySide2.QtGui import QColor, QEnterEvent
from PySide2.QtWidgets import QMainWindow, QDialog, QGraphicsDropShadowEffect


class CustomWindow(QMainWindow, QDialog):

    # 初始化
    def __init__(self):
        super().__init__()

        self.shadow = None  # 阴影
        self.direction = None  # 调整方向
        self.right_edge = None  # 右边缘
        self.bottom_edge = None  # 下边缘
        self.right_bottom_edge = None  # 右下边缘

        self.set_window()  # 定制窗口

    # 取消系统标题栏, 给窗口添加阴影
    def set_window(self):

        # 关闭系统标题栏
        self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint
                            | Qt.WindowMaximizeButtonHint)

        # 透明背景
        self.setAttribute(Qt.WA_TranslucentBackground)

        # 边界以透明,设置边框阴影效果,否则无法定位边界
        self.shadow = QGraphicsDropShadowEffect(self)
        self.shadow.setBlurRadius(15)
        self.shadow.setColor(QColor(0, 0, 0, 150))
        self.shadow.setOffset(0, 0)
        self.setGraphicsEffect(self.shadow)

    # 窗口边缘调整窗口大小(右侧和底部)
    def mousePressEvent(self, event):

        if event.button() == Qt.LeftButton:
            # 鼠标左键点击下边界区域 调整大小
            if self.direction == 'bottom':
                self.windowHandle().startSystemResize(Qt.BottomEdge)
            # 鼠标左键点击右边界区域 调整大小
            elif self.direction == 'right':
                self.windowHandle().startSystemResize(Qt.RightEdge)
            elif self.direction == 'right_bottom':
                self.windowHandle().startSystemResize(Qt.RightEdge | Qt.BottomEdge)

    # 窗口边缘设置鼠标手势(右侧和底部)
    def mouseMoveEvent(self, event):

        # 边界范围(x, y, x1, y1)
        self.right_edge = QRect(self.width() - 10, 0, self.width(), self.height())
        self.bottom_edge = QRect(0, self.height() - 10, self.width() - 25, self.height())
        self.right_bottom_edge = QRect(self.width() - 25, self.height() - 10, self.width(), self.height())

        # 根据鼠标在窗口的位置 改变鼠标手势
        if not self.isMaximized():

            if self.bottom_edge.contains(event.pos()):
                self.setCursor(Qt.SizeVerCursor)
                self.direction = "bottom"
            elif self.right_edge.contains(event.pos()):
                self.setCursor(Qt.SizeHorCursor)
                self.direction = "right"
            elif self.right_bottom_edge.contains(event.pos()):
                self.setCursor(Qt.SizeFDiagCursor)
                self.direction = "right_bottom"

    # 事件过滤器, 鼠标进入其它控件后还原为标准鼠标样式, 方向属性变为None
    def eventFilter(self, obj, event):

        if isinstance(event, QEnterEvent):
            self.setCursor(Qt.ArrowCursor)
            self.direction = None
        return super().eventFilter(obj, event)

2. ui定制模块

"""
加载UI文、定制UI界面.
备注:
    需要给主窗口和主Widget窗口开启鼠标跟踪, 用来定位边缘位置
    需要给主Frame添加事件过滤器, 过滤鼠标样式
模块功能:
    1. 标题栏窗口移动,标题栏双击最大化|还原
    2. 自定义按钮(最大化|还原, 最小化, 关闭窗口)

"""

from PySide2.QtGui import QIcon, Qt

import ui_1.qtui.res_rc  # type: ignore
from lib.custom_window import CustomWindow
from ui_1.qtui.main_ui import Ui_MainWindow


class CustomUI(CustomWindow):

    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.setMouseTracking(True)  # 父窗口也开启跟踪,否则只有在按下鼠标左键的时候才跟踪
        self.ui.AppWindow.setMouseTracking(True)  # 设置子窗口鼠标跟踪
        self.ui.AppBg.installEventFilter(self)  # 事件过滤器, 鼠标进入AppBg控件 过滤事件

        self.custom_titlebar()  # 定制标题栏

    # 最大化|还原
    def max_restore(self):

        if self.isMaximized():
            self.showNormal()  # 取消最大化, 正常展示
            self.ui.AppMargins.setContentsMargins(10, 10, 10, 10)  # 添加外边框
            self.ui.BtnMaxRestore.setIcon(QIcon(u":/icons/pictures/icons/最大化.png"))  # 更换最大化图标
        else:
            self.showMaximized()  # 窗口最大化
            self.ui.AppMargins.setContentsMargins(0, 0, 0, 0)  # 取消外边框
            self.ui.BtnMaxRestore.setIcon(QIcon(u":/icons/pictures/icons/还原.png"))  # 更换还原图标

    # 自定义标题栏
    def custom_titlebar(self):

        # 窗口移动
        def move_titlebar(_event):
            if self.isMaximized():
                self.max_restore()
            else:
                self.windowHandle().startSystemMove()

        self.ui.AppDes.mouseMoveEvent = move_titlebar  # 标题栏移动信号绑定窗口移动事件

        # 标题栏双击最大化|还原
        def doubleclick_titlebar(event):
            if event.button() == Qt.LeftButton:
                self.max_restore()

        self.ui.AppDes.mouseDoubleClickEvent = doubleclick_titlebar  # 标题栏双击信号绑定窗口最大化|还原事件

        # 按钮[最小化, 最大化|还原, 关闭程序]
        self.ui.BtnMin.clicked.connect(self.showMinimized)  # 最小化
        self.ui.BtnMaxRestore.clicked.connect(self.max_restore)  # 最大化|还原
        self.ui.BtnClose.clicked.connect(self.close)  # 关闭程序

如果这篇文章对你有帮助,那就点个赞呗!

项目gitee地址
项目刚开始建立,更新完善中…

你可能感兴趣的:(PythonGUI,pyqt,qt5,ui,python,qt)