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


Posted in Python onApril 19, 2018

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
利用python编写一个图片主色转换的脚本
Dec 07 Python
python中format()函数的简单使用教程
Mar 14 Python
Python面向对象类继承和组合实例分析
May 28 Python
Python读取系统文件夹内所有文件并统计数量的方法
Oct 23 Python
通过python实现随机交换礼物程序详解
Jul 10 Python
python实现翻转棋游戏(othello)
Jul 29 Python
你还在@微信官方?聊聊Python生成你想要的微信头像
Sep 25 Python
Pycharm最新激活码2019(推荐)
Dec 31 Python
使用Tensorflow-GPU禁用GPU设置(CPU与GPU速度对比)
Jun 30 Python
Python 读取位于包中的数据文件
Aug 07 Python
Python读写锁实现实现代码解析
Nov 28 Python
python机器学习创建基于规则聊天机器人过程示例详解
Nov 02 Python
利用numpy和pandas处理csv文件中的时间方法
Apr 19 #Python
Python处理CSV与List的转换方法
Apr 19 #Python
python3+PyQt5重新实现QT事件处理程序
Apr 19 #Python
python3+PyQt5重新实现自定义数据拖放处理
Apr 19 #Python
python之从文件读取数据到list的实例讲解
Apr 19 #Python
python实现读取大文件并逐行写入另外一个文件
Apr 19 #Python
python按行读取文件,去掉每行的换行符\n的实例
Apr 19 #Python
You might like
无法在发生错误时创建会话,请检查 PHP 或网站服务器日志,并正确配置 PHP 安装最快的解决办法
2010/08/01 PHP
php广告加载类用法实例
2014/09/23 PHP
mod_php、FastCGI、PHP-FPM等PHP运行方式对比
2015/07/02 PHP
用PHP的socket实现客户端到服务端的通信实例详解
2017/02/04 PHP
linux mint下安装phpstorm2020包括JDK部分的教程详解
2020/09/17 PHP
JS获取单击按钮单元格所在行的信息
2014/06/17 Javascript
jQuery实现的登录浮动框效果代码
2015/09/26 Javascript
值得分享的轻量级Bootstrap Table表格插件
2016/05/30 Javascript
基于angularjs实现图片放大镜效果
2016/08/31 Javascript
JavaScript和jQuery获取input框的绝对位置实现方法
2016/10/13 Javascript
微信小程序-消息提示框实例
2016/11/24 Javascript
Vue组件之全局组件与局部组件的使用详解
2017/10/09 Javascript
用node.js写一个jenkins发版脚本
2019/05/21 Javascript
vue实现路由懒加载及组件懒加载的方式
2019/06/11 Javascript
vue中利用three.js实现全景图的完整示例
2020/12/07 Vue.js
Python中正则表达式的详细教程
2015/04/30 Python
python 垃圾收集机制的实例详解
2017/08/20 Python
Python Opencv任意形状目标检测并绘制框图
2019/07/23 Python
在Django中实现添加user到group并查看
2019/11/18 Python
使用python模拟高斯分布例子
2019/12/09 Python
python实现图片,视频人脸识别(opencv版)
2020/11/18 Python
挪威手表购物网站:Klokker
2016/09/19 全球购物
Nike意大利官网:Nike.com IT
2020/01/19 全球购物
大学生简单自荐信
2013/11/10 职场文书
个人自荐书
2013/12/20 职场文书
高中生期末评语大全
2014/01/28 职场文书
护士岗前培训自我评鉴
2014/02/28 职场文书
网络优化专员求职信
2014/05/04 职场文书
初中班主任经验交流材料
2014/05/16 职场文书
趣味运动会策划方案
2014/06/02 职场文书
2014年入党积极分子学习三中全会思想汇报
2014/09/13 职场文书
2014社会治安综合治理工作总结
2014/12/04 职场文书
2014年仓库管理工作总结
2014/12/17 职场文书
大学生在校表现评语
2014/12/31 职场文书
员工担保书范本
2015/09/22 职场文书
详解JavaScript中的执行上下文及调用堆栈
2021/04/29 Javascript