效果:
插件:
import PySide6
from PySide6 import QtGui, QtCore, QtWidgets
from PySide6.QtCore import Qt, QSize, QObject
from PySide6.QtGui import QPainter, QColor
from PySide6.QtWidgets import QWidget, QApplication, QPushButton, QLabel, QDialog, QMainWindow
# 标题组件
class QCustomTitleBar(QWidget):
def __init__(self, parent: QObject, windowTitle: str, title_bar_height: int = 40):
super(QCustomTitleBar, self).__init__(parent)
self.parent = parent
self.DEFAULT_TITLE_BAR_HEIGHT = title_bar_height
self.mouseDoubleClickEvent_parent = self.parent.mouseDoubleClickEvent
self.parent.mouseDoubleClickEvent = self.mouseDoubleClickEvent
self.resizeEvent_parent = self.parent.resizeEvent
self.parent.resizeEvent = self.resizeEvent
self.parent.setContentsMargins(0, self.DEFAULT_TITLE_BAR_HEIGHT, 0, 0)
self.parent.setWindowFlags(
Qt.WindowType.Window
| Qt.WindowType.FramelessWindowHint
| Qt.WindowType.WindowSystemMenuHint
| Qt.WindowType.WindowMinimizeButtonHint
| Qt.WindowType.WindowMaximizeButtonHint
)
self.title = QLabel(windowTitle, self.parent)
self.setStyle()
self.close_btn = QPushButton("", self.parent)
self.max_btn = QPushButton("", self.parent)
self.min_btn = QPushButton("", self.parent)
self.setupButtons()
self.window_max_size = None
self.title.setMouseTracking(True)
def setupButtons(self):
self.close_btn.setGeometry(self.parent.width() - 34, 10, 20, 20)
self.max_btn.setGeometry(self.parent.width() - 67, 10, 20, 20)
self.min_btn.setGeometry(self.parent.width() - 100, 10, 20, 20)
self.close_btn.setStyleSheet(
"QPushButton{border-image:url('./images/close.png');background:#ff625f;border-radius:10px;}"
"QPushButton:hover{background:#eb4845;}"
"QPushButton:pressed{background:#d33e3b;}"
)
self.max_btn.setStyleSheet(
"QPushButton{border-image:url('./images/max.png');background:#ffbe2f;border-radius:10px;}"
"QPushButton:hover{background:#ecae27;}"
"QPushButton:pressed{background:#d49d24;}"
)
self.min_btn.setStyleSheet(
"QPushButton{border-image:url('./images/min.png');background:#29c941;border-radius:10px;}"
"QPushButton:hover{background:#1ac033;}"
"QPushButton:pressed{background:#17a82d;}"
)
self.close_btn.clicked.connect(self.parent.close)
self.max_btn.clicked.connect(self.setMaxEvent)
self.min_btn.clicked.connect(self.parent.showMinimized)
# 连接 pressed 和 released 信号到自定义槽函数
self.close_btn.pressed.connect(lambda: self.buttonPressed(self.close_btn))
self.close_btn.released.connect(lambda: self.buttonReleased(self.close_btn))
self.max_btn.pressed.connect(lambda: self.buttonPressed(self.max_btn))
self.max_btn.released.connect(lambda: self.buttonReleased(self.max_btn))
self.min_btn.pressed.connect(lambda: self.buttonPressed(self.min_btn))
self.min_btn.released.connect(lambda: self.buttonReleased(self.min_btn))
def setMaxEvent(self):
if self.parent.isMaximized():
self.parent.showNormal()
else:
self.parent.showMaximized()
def setStyle(self):
DEFAULT_STYLE = """
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #fafafa, stop:1 #d1d1d1);
color: #333333; border:1px solid #c6c6c6;padding:10px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
"""
self.title.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
self.title.setStyleSheet(DEFAULT_STYLE)
self.title.setGeometry(1, 0, self.parent.width() - 2, self.DEFAULT_TITLE_BAR_HEIGHT)
def mouseDoubleClickEvent(self, a0: QtGui.QMouseEvent) -> None:
if a0.button() == Qt.MouseButton.LeftButton and a0.position().y() < self.title.height():
self.setMaxEvent()
return self.mouseDoubleClickEvent_parent(a0)
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
self.close_btn.move(self.parent.width() - 34, 10)
self.max_btn.move(self.parent.width() - 67, 10)
self.min_btn.move(self.parent.width() - 100, 10)
self.title.resize(self.parent.width() - 2, self.DEFAULT_TITLE_BAR_HEIGHT)
return self.resizeEvent_parent(a0)
def buttonPressed(self, button: QPushButton):
button.move(button.x() + 1, button.y() + 2) # 向下移动2个像素
def buttonReleased(self, button: QPushButton):
button.move(button.x() - 1, button.y() - 2) # 向上移动2个像素
# 缩放组件
class WindowResizer:
def __init__(self, window):
self.window = window
self.window.setMouseTracking(True)
self._startPos = None
self._isResizing = False
self._resizeMargins = 10 # 缩放检测区域的大小
self.window.setMinimumSize(160, 80) # 设置最小尺寸
self.window.mousePressEvent = self.mousePressEvent
self.window.mouseMoveEvent = self.mouseMoveEvent
self.window.mouseReleaseEvent = self.mouseReleaseEvent
self.window.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground, True)
self.window.setWindowFlags(Qt.WindowType.FramelessWindowHint)
self.window.paintEvent = self.paintEvent
def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
if event.button() == Qt.MouseButton.LeftButton:
self._resizeDirection = self.getResizeDirection(event.position().toPoint())
if self._resizeDirection:
self._isResizing = True
self._startPos = event.globalPosition().toPoint()
self._startSize = self.window.size()
else:
self._startPos = event.globalPosition().toPoint() - self.window.frameGeometry().topLeft()
def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
if self._isResizing:
delta = event.globalPosition().toPoint() - self._startPos
newSize = QSize(self._startSize.width(), self._startSize.height())
if 'left' in self._resizeDirection:
newSize.setWidth(self._startSize.width() - delta.x())
if 'right' in self._resizeDirection:
newSize.setWidth(self._startSize.width() + delta.x())
if 'top' in self._resizeDirection:
newSize.setHeight(self._startSize.height() - delta.y())
if 'bottom' in self._resizeDirection:
newSize.setHeight(self._startSize.height() + delta.y())
self.window.resize(newSize)
elif event.buttons() == Qt.MouseButton.LeftButton and self._startPos:
self.window.move(event.globalPosition().toPoint() - self._startPos)
else:
self.updateCursorShape(event.position().toPoint())
def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None:
self._isResizing = False
self._startPos = None
self._resizeDirection = None
def updateCursorShape(self, pos):
resizeDirection = self.getResizeDirection(pos)
if resizeDirection:
if resizeDirection in ['left', 'right']:
self.window.setCursor(Qt.CursorShape.SizeHorCursor)
elif resizeDirection in ['top', 'bottom']:
self.window.setCursor(Qt.CursorShape.SizeVerCursor)
elif resizeDirection == 'top-left' or resizeDirection == 'bottom-right':
self.window.setCursor(Qt.CursorShape.SizeFDiagCursor)
elif resizeDirection == 'top-right' or resizeDirection == 'bottom-left':
self.window.setCursor(Qt.CursorShape.SizeBDiagCursor)
else:
self.window.setCursor(Qt.CursorShape.ArrowCursor)
def getResizeDirection(self, pos):
margins = self._resizeMargins
if pos.x() < margins:
if pos.y() < margins:
return 'top-left'
elif pos.y() > self.window.height() - margins:
return 'bottom-left'
else:
return 'left'
elif pos.x() > self.window.width() - margins:
if pos.y() < margins:
return 'top-right'
elif pos.y() > self.window.height() - margins:
return 'bottom-right'
else:
return 'right'
elif pos.y() < margins:
return 'top'
elif pos.y() > self.window.height() - margins:
return 'bottom'
return None
def isAtResizeEdge(self, pos):
return pos.x() >= self.window.width() - self._resizeMargins and pos.y() >= self.window.height() - self._resizeMargins
def paintEvent(self, event: PySide6.QtGui.QPaintEvent) -> None:
painter = QPainter(self.window)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
# 创建更精细的圆角路径
path = QtGui.QPainterPath()
rect = self.window.rect().adjusted(1, 1, -1, -1)
path.addRoundedRect(rect, 10, 10)
# 设置剪辑路径
painter.setClipPath(path)
# 绘制背景
painter.setBrush(QColor(245, 245, 245))
painter.setPen(Qt.PenStyle.NoPen)
painter.drawPath(path)
QWidget.paintEvent(self.window, event)
class ExistingWindow(QMainWindow):
def __init__(self, parent=None):
super(ExistingWindow, self).__init__(parent)
self.setWindowTitle("Existing Window")
self.resize(600, 400)
self.title_bar = QCustomTitleBar(self, windowTitle="配置")
self.resizer = WindowResizer(self)
# 其他初始化代码
if __name__ == '__main__':
app = QApplication([])
main = ExistingWindow()
main.show()
app.exec()
使用:
class ExistingWindow(QWidget):
def __init__(self, parent=None):
super(ExistingWindow, self).__init__(parent)
self.setWindowTitle("Existing Window")
self.resize(600, 400)
self.title_bar = QCustomTitleBar(self)
self.resizer = WindowResizer(self)
# 其他初始化代码
if __name__ == '__main__':
app = QApplication([])
main = ExistingWindow()
main.show()
app.exec()