181 lines
5.9 KiB
Python
181 lines
5.9 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Created on 2018年4月30日
|
|
@author: Irony
|
|
@site: https://pyqt.site , https://github.com/PyQt5
|
|
@email: 892768447@qq.com
|
|
@file: NewFramelessWindow
|
|
@description:
|
|
"""
|
|
|
|
from Lib.ui_frameless import Ui_FormFrameless
|
|
from PyQt5.QtCore import QTimer, Qt, QEvent, QObject
|
|
from PyQt5.QtGui import QWindow, QPainter, QColor, QMouseEvent
|
|
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
|
|
|
|
|
|
class FramelessObject(QObject):
|
|
Margins = 3 # 边缘边距
|
|
TitleHeight = 36 # 标题栏高度
|
|
Widgets = set() # 无边框窗口集合
|
|
|
|
@classmethod
|
|
def set_margins(cls, margins):
|
|
cls.Margins = margins
|
|
|
|
@classmethod
|
|
def set_title_height(cls, height):
|
|
cls.TitleHeight = height
|
|
|
|
@classmethod
|
|
def add_widget(cls, widget):
|
|
cls.Widgets.add(widget)
|
|
|
|
@classmethod
|
|
def del_widget(cls, widget):
|
|
if widget in cls.Widgets:
|
|
cls.Widgets.remove(widget)
|
|
|
|
def _get_edges(self, pos, width, height):
|
|
"""根据坐标获取方向
|
|
:param pos: QPoint
|
|
:param width: int
|
|
:param height: int
|
|
:return: Qt.Edges
|
|
"""
|
|
edge = 0
|
|
x, y = pos.x(), pos.y()
|
|
|
|
if y <= self.Margins:
|
|
edge |= Qt.TopEdge
|
|
if x <= self.Margins:
|
|
edge |= Qt.LeftEdge
|
|
if x >= width - self.Margins:
|
|
edge |= Qt.RightEdge
|
|
if y >= height - self.Margins:
|
|
edge |= Qt.BottomEdge
|
|
|
|
return edge
|
|
|
|
def _get_cursor(self, edges):
|
|
"""调整鼠标样式
|
|
:param edges: int or None
|
|
:return: Qt.CursorShape
|
|
"""
|
|
if edges == Qt.LeftEdge | Qt.TopEdge or edges == Qt.RightEdge | Qt.BottomEdge:
|
|
return Qt.SizeFDiagCursor
|
|
elif edges == Qt.RightEdge | Qt.TopEdge or edges == Qt.LeftEdge | Qt.BottomEdge:
|
|
return Qt.SizeBDiagCursor
|
|
elif edges == Qt.LeftEdge or edges == Qt.RightEdge:
|
|
return Qt.SizeHorCursor
|
|
elif edges == Qt.TopEdge or edges == Qt.BottomEdge:
|
|
return Qt.SizeVerCursor
|
|
|
|
return Qt.ArrowCursor
|
|
|
|
def is_titlebar(self, pos):
|
|
"""判断是否是标题栏
|
|
:param pos: QPoint
|
|
:return: bool
|
|
"""
|
|
return pos.y() <= self.TitleHeight
|
|
|
|
def moveOrResize(self, window, pos, width, height):
|
|
edges = self._get_edges(pos, width, height)
|
|
if edges:
|
|
if window.windowState() == Qt.WindowNoState:
|
|
window.startSystemResize(edges)
|
|
else:
|
|
if self.is_titlebar(pos):
|
|
window.startSystemMove()
|
|
|
|
def eventFilter(self, obj, event):
|
|
if obj.isWindowType():
|
|
# top window 处理光标样式
|
|
if event.type() == QEvent.MouseMove and obj.windowState() == Qt.WindowNoState:
|
|
obj.setCursor(self._get_cursor(self._get_edges(event.pos(), obj.width(), obj.height())))
|
|
elif event.type() == QEvent.TouchUpdate:
|
|
self.moveOrResize(obj, event.pos(), obj.width(), obj.height())
|
|
elif obj in self.Widgets and isinstance(event, QMouseEvent) and event.button() == Qt.LeftButton:
|
|
if event.type() == QEvent.MouseButtonDblClick:
|
|
# 双击最大化还原
|
|
if self.is_titlebar(event.pos()):
|
|
if obj.windowState() == Qt.WindowFullScreen:
|
|
pass
|
|
elif obj.windowState() == Qt.WindowMaximized:
|
|
obj.showNormal()
|
|
else:
|
|
obj.showMaximized()
|
|
elif event.type() == QEvent.MouseButtonPress:
|
|
self.moveOrResize(obj.windowHandle(), event.pos(), obj.width(), obj.height())
|
|
|
|
return False
|
|
|
|
|
|
class FramelessWindow(QWidget, Ui_FormFrameless):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(FramelessWindow, self).__init__(*args, **kwargs)
|
|
self.setupUi(self)
|
|
# 无边框
|
|
self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)
|
|
self.setAttribute(Qt.WA_TranslucentBackground, True)
|
|
self.setMouseTracking(True)
|
|
# 隐藏还原按钮
|
|
self.buttonNormal.setVisible(False)
|
|
# 标题栏按钮信号
|
|
self.buttonMinimum.clicked.connect(self.showMinimized)
|
|
self.buttonMaximum.clicked.connect(self.showMaximized)
|
|
self.buttonNormal.clicked.connect(self.showNormal)
|
|
self.buttonClose.clicked.connect(self.close)
|
|
self.setStyleSheet('#widgetTitleBar{background: rgb(232, 232, 232);}')
|
|
|
|
def changeEvent(self, event):
|
|
"""窗口状态改变
|
|
:param event:
|
|
"""
|
|
super(FramelessWindow, self).changeEvent(event)
|
|
# 窗口状态改变时修改标题栏控制按钮
|
|
visible = self.isMaximized()
|
|
self.buttonMaximum.setVisible(not visible)
|
|
self.buttonNormal.setVisible(visible)
|
|
if visible:
|
|
self.layout().setContentsMargins(0, 0, 0, 0)
|
|
else:
|
|
# TODO 与UI文件中的布局边距一致
|
|
m = FramelessObject.Margins
|
|
self.layout().setContentsMargins(m, m, m, m)
|
|
|
|
def paintEvent(self, event):
|
|
# 透明背景但是需要留下一个透明度用于鼠标捕获
|
|
painter = QPainter(self)
|
|
painter.fillRect(self.rect(), QColor(255, 255, 255, 1))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
import cgitb
|
|
|
|
cgitb.enable(format='text')
|
|
|
|
app = QApplication(sys.argv)
|
|
if not hasattr(QWindow, 'startSystemMove'):
|
|
QWindow.startSystemResize()
|
|
# 不支持
|
|
QMessageBox.critical(None, '错误', '当前Qt版本不支持该例子')
|
|
QTimer.singleShot(100, app.quit)
|
|
else:
|
|
# 安装全局事件过滤器
|
|
fo = FramelessObject()
|
|
app.installEventFilter(fo)
|
|
|
|
w1 = FramelessWindow()
|
|
fo.add_widget(w1)
|
|
w1.show()
|
|
|
|
w2 = FramelessWindow()
|
|
fo.add_widget(w2)
|
|
w2.show()
|
|
sys.exit(app.exec_())
|