New FrameLess Window
This commit is contained in:
parent
2a54e678ae
commit
0ca3f3c90b
4 changed files with 472 additions and 2 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -24,8 +24,6 @@ dist/
|
|||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
|
|
197
Demo/Data/frameless.ui
Normal file
197
Demo/Data/frameless.ui
Normal file
|
@ -0,0 +1,197 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FormFrameless</class>
|
||||
<widget class="QWidget" name="FormFrameless">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="widgetTitleBar" native="true">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Symbola</family>
|
||||
</font>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>253</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonMinimum">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>36</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>36</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>webdings</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Minimum</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonMaximum">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>36</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>36</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>webdings</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Maximum</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonNormal">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>36</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>36</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>webdings</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Normal</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonClose">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>36</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>36</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>webdings</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>r</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEdit">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">frameless window with move and resize</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
100
Demo/Lib/ui_frameless.py
Normal file
100
Demo/Lib/ui_frameless.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'frameless.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.2
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_FormFrameless(object):
|
||||
def setupUi(self, FormFrameless):
|
||||
FormFrameless.setObjectName("FormFrameless")
|
||||
FormFrameless.resize(400, 300)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(FormFrameless)
|
||||
self.verticalLayout.setContentsMargins(3, 3, 3, 3)
|
||||
self.verticalLayout.setSpacing(0)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.widgetTitleBar = QtWidgets.QWidget(FormFrameless)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Symbola")
|
||||
self.widgetTitleBar.setFont(font)
|
||||
self.widgetTitleBar.setObjectName("widgetTitleBar")
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.widgetTitleBar)
|
||||
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.horizontalLayout.setSpacing(0)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
spacerItem = QtWidgets.QSpacerItem(253, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem)
|
||||
self.buttonMinimum = QtWidgets.QPushButton(self.widgetTitleBar)
|
||||
self.buttonMinimum.setMinimumSize(QtCore.QSize(36, 36))
|
||||
self.buttonMinimum.setMaximumSize(QtCore.QSize(36, 36))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("webdings")
|
||||
self.buttonMinimum.setFont(font)
|
||||
self.buttonMinimum.setObjectName("buttonMinimum")
|
||||
self.horizontalLayout.addWidget(self.buttonMinimum)
|
||||
self.buttonMaximum = QtWidgets.QPushButton(self.widgetTitleBar)
|
||||
self.buttonMaximum.setMinimumSize(QtCore.QSize(36, 36))
|
||||
self.buttonMaximum.setMaximumSize(QtCore.QSize(36, 36))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("webdings")
|
||||
self.buttonMaximum.setFont(font)
|
||||
self.buttonMaximum.setObjectName("buttonMaximum")
|
||||
self.horizontalLayout.addWidget(self.buttonMaximum)
|
||||
self.buttonNormal = QtWidgets.QPushButton(self.widgetTitleBar)
|
||||
self.buttonNormal.setMinimumSize(QtCore.QSize(36, 36))
|
||||
self.buttonNormal.setMaximumSize(QtCore.QSize(36, 36))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("webdings")
|
||||
self.buttonNormal.setFont(font)
|
||||
self.buttonNormal.setObjectName("buttonNormal")
|
||||
self.horizontalLayout.addWidget(self.buttonNormal)
|
||||
self.buttonClose = QtWidgets.QPushButton(self.widgetTitleBar)
|
||||
self.buttonClose.setMinimumSize(QtCore.QSize(36, 36))
|
||||
self.buttonClose.setMaximumSize(QtCore.QSize(36, 36))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("webdings")
|
||||
self.buttonClose.setFont(font)
|
||||
self.buttonClose.setObjectName("buttonClose")
|
||||
self.horizontalLayout.addWidget(self.buttonClose)
|
||||
self.verticalLayout.addWidget(self.widgetTitleBar)
|
||||
self.textEdit = QtWidgets.QTextEdit(FormFrameless)
|
||||
self.textEdit.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||
self.textEdit.setObjectName("textEdit")
|
||||
self.verticalLayout.addWidget(self.textEdit)
|
||||
self.verticalLayout.setStretch(1, 1)
|
||||
|
||||
self.retranslateUi(FormFrameless)
|
||||
QtCore.QMetaObject.connectSlotsByName(FormFrameless)
|
||||
|
||||
def retranslateUi(self, FormFrameless):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
FormFrameless.setWindowTitle(_translate("FormFrameless", "Form"))
|
||||
self.buttonMinimum.setToolTip(_translate("FormFrameless", "Minimum"))
|
||||
self.buttonMinimum.setText(_translate("FormFrameless", "0"))
|
||||
self.buttonMaximum.setToolTip(_translate("FormFrameless", "Maximum"))
|
||||
self.buttonMaximum.setText(_translate("FormFrameless", "1"))
|
||||
self.buttonNormal.setToolTip(_translate("FormFrameless", "Normal"))
|
||||
self.buttonNormal.setText(_translate("FormFrameless", "2"))
|
||||
self.buttonClose.setToolTip(_translate("FormFrameless", "Close"))
|
||||
self.buttonClose.setText(_translate("FormFrameless", "r"))
|
||||
self.textEdit.setHtml(_translate("FormFrameless", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
|
||||
"p, li { white-space: pre-wrap; }\n"
|
||||
"</style></head><body style=\" font-family:\'Sans Serif\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">frameless window with move and resize</p></body></html>"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
FormFrameless = QtWidgets.QWidget()
|
||||
ui = Ui_FormFrameless()
|
||||
ui.setupUi(FormFrameless)
|
||||
FormFrameless.show()
|
||||
sys.exit(app.exec_())
|
175
Demo/NewFramelessWindow.py
Normal file
175
Demo/NewFramelessWindow.py
Normal file
|
@ -0,0 +1,175 @@
|
|||
#!/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, QPen, QColor
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
|
||||
|
||||
|
||||
class FramelessObject(QObject):
|
||||
|
||||
@staticmethod
|
||||
def _get_edges(pos):
|
||||
"""根据坐标获取方向
|
||||
:param pos: QPoint
|
||||
:return: Qt::Edges
|
||||
"""
|
||||
edge = 0
|
||||
x, y = pos.x(), pos.y()
|
||||
widget = QApplication.instance().activeWindow()
|
||||
if not widget:
|
||||
return edge
|
||||
left, top, right, bottom = widget.layout().getContentsMargins()
|
||||
|
||||
if y <= top:
|
||||
edge |= Qt.TopEdge
|
||||
if x <= left:
|
||||
edge |= Qt.LeftEdge
|
||||
if x >= widget.width() - right:
|
||||
edge |= Qt.RightEdge
|
||||
if y >= widget.height() - bottom:
|
||||
edge |= Qt.BottomEdge
|
||||
|
||||
return edge
|
||||
|
||||
@staticmethod
|
||||
def _adjust_cursor(edges):
|
||||
"""调整鼠标样式
|
||||
:param edges: int or None
|
||||
"""
|
||||
# print('edges', edges)
|
||||
widget = QApplication.instance().activeWindow()
|
||||
if not widget:
|
||||
return
|
||||
if edges == Qt.LeftEdge | Qt.TopEdge or edges == Qt.RightEdge | Qt.BottomEdge:
|
||||
widget.setCursor(Qt.SizeFDiagCursor)
|
||||
elif edges == Qt.RightEdge | Qt.TopEdge or edges == Qt.LeftEdge | Qt.BottomEdge:
|
||||
widget.setCursor(Qt.SizeBDiagCursor)
|
||||
elif edges == Qt.LeftEdge or edges == Qt.RightEdge:
|
||||
widget.setCursor(Qt.SizeHorCursor)
|
||||
elif edges == Qt.TopEdge or edges == Qt.BottomEdge:
|
||||
widget.setCursor(Qt.SizeVerCursor)
|
||||
else:
|
||||
widget.setCursor(Qt.ArrowCursor)
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
# 鼠标移动改变光标
|
||||
if event.type() == QEvent.MouseMove:
|
||||
widget = QApplication.instance().activeWindow()
|
||||
if widget and event.buttons() == Qt.NoButton and not (
|
||||
widget.isMaximized() or widget.isFullScreen()):
|
||||
# 鼠标移动变更光标样式
|
||||
# print('MouseMove', event.pos())
|
||||
self._adjust_cursor(self._get_edges(event.pos()))
|
||||
|
||||
if event.type() == QEvent.MouseButtonPress and obj.objectName() == 'FramelessWindow':
|
||||
# 鼠标按下
|
||||
edges = self._get_edges(event.pos())
|
||||
# 标题栏高度
|
||||
try:
|
||||
height = obj.titleHeight()
|
||||
except AttributeError:
|
||||
height = 36
|
||||
# 优先判断边缘
|
||||
widget = QApplication.instance().activeWindow()
|
||||
if edges != 0 and not (widget.isMaximized() or widget.isFullScreen()):
|
||||
# 按下窗口边缘边距位置
|
||||
self._adjust_cursor(edges)
|
||||
# 交给系统去调整大小
|
||||
print('startSystemResize')
|
||||
obj.windowHandle().startSystemResize(edges)
|
||||
elif event.y() <= height:
|
||||
# 标题栏交给系统去移动
|
||||
print('startSystemMove')
|
||||
obj.windowHandle().startSystemMove()
|
||||
elif event.type() == QEvent.MouseButtonRelease:
|
||||
# 鼠标释放
|
||||
self._adjust_cursor(None)
|
||||
return False
|
||||
|
||||
|
||||
class FramelessWindow(QWidget, Ui_FormFrameless):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FramelessWindow, self).__init__(*args, **kwargs)
|
||||
self.setupUi(self)
|
||||
self.setObjectName('FramelessWindow') # 过滤器中的名字一致
|
||||
# 无边框
|
||||
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 titleHeight(self):
|
||||
"""标题栏高度
|
||||
:return: int
|
||||
"""
|
||||
return self.widgetTitleBar.height()
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
"""双击最大化还原
|
||||
:param event: QMouseEvent
|
||||
"""
|
||||
if self.isMaximized():
|
||||
self.showNormal()
|
||||
else:
|
||||
self.showMaximized()
|
||||
event.accept()
|
||||
|
||||
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文件中的布局边距一致
|
||||
self.layout().setContentsMargins(3, 3, 3, 3)
|
||||
|
||||
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)
|
||||
w = FramelessWindow()
|
||||
w.show()
|
||||
sys.exit(app.exec_())
|
Loading…
Reference in a new issue