Qt第三十七章:自定义标题+自由缩放(插件)

效果:

插件:

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()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文子阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值