pyside6 控件一:自定义窗口(缩放和标题)

目录

一、自定义窗口缩放(使用者可直接Copy)

二、自定义窗口标题(使用者可直接Copy)

三、缩放和标题一起使用案例:


 

一、自定义窗口缩放(使用者可直接Copy)

import enum
from dataclasses import dataclass

from PySide6.QtCore import Qt
from PySide6.QtGui import QPainter, QPaintEvent
from PySide6.QtWidgets import QApplication, QWidget


# 边缘类型枚举
class Edge(enum.Flag):
    NoEdge: Qt.Edge = 0  # 不在边缘地带

    TopEdge: Qt.Edge = 1  # 顶部边缘
    LeftEdge: Qt.Edge = 2  # 左部边缘
    RightEdge: Qt.Edge = 3.  # 右部边缘
    BottomEdge: Qt.Edge = 4  # 底部边缘

    LeftTopEdge: Qt.Edge = 5  # 左顶部边缘
    RightTopEdge: Qt.Edge = 6  # 右顶部边缘
    LeftBottomEdge: Qt.Edge = 7  # 左底部边缘
    RightBottomEdge: Qt.Edge = 8  # 右底部边缘


# 边缘按下区域类型
@dataclass
class EdgePress:
    leftEdgePress: bool = False
    rightEdgePress: bool = False
    topEdgePress: bool = False
    bottomEdgePress: bool = False
    leftTopEdgePress: bool = False
    rightBottomEdgePress: bool = False
    leftBottomEdgePress: bool = False
    rightTopEdgePress: bool = False
    moveEdgePress: bool = False
    # 点击时的窗口初始位置
    movePosition = None


class ElWindow(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setMouseTracking(True)
        self.resize(100, 100)
        self.setWindowFlags(
            Qt.Window
            | Qt.FramelessWindowHint
            | Qt.WindowSystemMenuHint
            | Qt.WindowMinimizeButtonHint
            | Qt.WindowMaximizeButtonHint
        )
        # 设置窗体透明
        self.setAttribute(Qt.WA_TranslucentBackground)
        # 窗体边缘尺寸(出现缩放标记的范围)
        self.edge_size = 5
        # 窗体圆角X半径
        self.xRadius = 5
        # 窗体圆角Y半径
        self.yRadius = 5
        # 窗体的最小宽度
        self.min_width = 50
        # 窗体的最小高度
        self.min_height = 50
        # 拖放标记
        self.edge_press = EdgePress()
        # 顶部可移动窗口高度
        self.move_event_height = 40

    # 设置边框圆角
    def paintEvent(self, event: QPaintEvent) -> None:
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)  # 设置抗锯齿,不然边框会有明显锯齿
        painter.setBrush(Qt.white)  # 设置窗体颜色
        painter.drawRoundedRect(self.rect(), self.xRadius, self.yRadius)
        super().paintEvent(event)

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.MouseButton.NoButton:
            # 没有鼠标按钮按下,检查鼠标位置并更新窗口光标
            pos = event.globalPosition().toPoint() - self.pos()
            edges = self._get_edges(pos)

            if edges == Edge.LeftEdge or edges == Edge.RightEdge:
                self.setCursor(Qt.SizeHorCursor)
            elif edges == Edge.TopEdge or edges == Edge.BottomEdge:
                self.setCursor(Qt.SizeVerCursor)
            elif edges == Edge.LeftTopEdge or edges == Edge.RightBottomEdge:
                self.setCursor(Qt.CursorShape.SizeFDiagCursor)
            elif edges == Edge.LeftBottomEdge or edges == Edge.RightTopEdge:
                self.setCursor(Qt.CursorShape.SizeBDiagCursor)
            else:
                self.setCursor(Qt.CursorShape.ArrowCursor)
        elif event.buttons() == Qt.MouseButton.LeftButton:
            if self.edge_press.moveEdgePress:
                """移动窗口"""
                self.move(event.globalPosition().toPoint() - self.edge_press.movePosition)  # 更改窗口位置
            elif self._get_edge_press() is not Edge.NoEdge:
                """缩放窗口"""
                self._resize_window(event.globalPosition().toPoint() - self.pos())

    def mousePressEvent(self, event) -> None:

        pos = event.globalPosition().toPoint() - self.pos()
        edges = self._get_edges(pos)
        if edges == Edge.LeftEdge:
            self.edge_press.leftEdgePress = True
        elif edges == Edge.RightEdge:
            self.edge_press.rightEdgePress = True
        elif edges == Edge.TopEdge:
            self.edge_press.topEdgePress = True
        elif edges == Edge.BottomEdge:
            self.edge_press.bottomEdgePress = True
        elif edges == Edge.LeftTopEdge:
            self.edge_press.leftTopEdgePress = True
        elif edges == Edge.RightBottomEdge:
            self.edge_press.rightBottomEdgePress = True
        elif edges == Edge.LeftBottomEdge:
            self.edge_press.leftBottomEdgePress = True
        elif edges == Edge.RightTopEdge:
            self.edge_press.rightTopEdgePress = True
        else:
            # 移动事件
            if self._get_move_edges(pos):
                self.edge_press.moveEdgePress = True
                self.edge_press.movePosition = event.globalPosition().toPoint() - self.pos()
                self.setCursor(Qt.CursorShape.OpenHandCursor)

    def mouseReleaseEvent(self, event) -> None:
        self.edge_press.leftEdgePress = False
        self.edge_press.rightEdgePress = False
        self.edge_press.topEdgePress = False
        self.edge_press.bottomEdgePress = False
        self.edge_press.leftTopEdgePress = False
        self.edge_press.rightBottomEdgePress = False
        self.edge_press.leftBottomEdgePress = False
        self.edge_press.rightTopEdgePress = False
        self.edge_press.moveEdgePress = False
        self.setCursor(Qt.CursorShape.ArrowCursor)

    def _get_move_edges(self, pos):
        """获取移动窗口事件标记"""
        in_move_edge: bool = pos.y() <= self.move_event_height  # 是否在可移动区域内
        not_in_edges: bool = self._get_edges(pos) == Edge.NoEdge  # 是否在非缩放区域内
        return in_move_edge and not_in_edges

    def _get_edges(self, pos):
        """获取边缘类型"""
        edges = Edge.NoEdge

        in_left_edge: bool = pos.x() <= self.edge_size  # 左
        in_right_edge: bool = pos.x() >= (self.width() - self.edge_size)  # 右
        in_top_edge: bool = pos.y() <= self.edge_size  # 上
        in_bottom_edge: bool = pos.y() >= (self.height() - self.edge_size)  # 下

        size = len([i for i in [in_left_edge, in_right_edge, in_top_edge, in_bottom_edge] if i])

        if size == 0:
            return edges
        if size == 1:
            if in_left_edge:
                return Edge.LeftEdge
            if in_right_edge:
                return Edge.RightEdge
            if in_top_edge:
                return Edge.TopEdge
            if in_bottom_edge:
                return Edge.BottomEdge
        if size == 2:
            if in_left_edge and in_top_edge:
                return Edge.LeftTopEdge
            if in_left_edge and in_bottom_edge:
                return Edge.LeftBottomEdge
            if in_right_edge and in_top_edge:
                return Edge.RightTopEdge
            if in_right_edge and in_bottom_edge:
                return Edge.RightBottomEdge

    def _get_edge_press(self):
        """
         边缘按下区域类型
        """
        if self.edge_press.leftEdgePress:
            return Edge.LeftEdge
        elif self.edge_press.rightEdgePress:
            return Edge.RightEdge
        elif self.edge_press.topEdgePress:
            return Edge.TopEdge
        elif self.edge_press.bottomEdgePress:
            return Edge.BottomEdge
        elif self.edge_press.leftTopEdgePress:
            return Edge.LeftTopEdge
        elif self.edge_press.rightBottomEdgePress:
            return Edge.RightBottomEdge
        elif self.edge_press.leftBottomEdgePress:
            return Edge.LeftBottomEdge
        elif self.edge_press.rightTopEdgePress:
            return Edge.RightTopEdge
        else:
            return Edge.NoEdge

    def _resize_window(self, pos):

        """缩放窗口"""
        # 判断按下时的区域是哪一块
        edges = self._get_edge_press()
        # 获取窗口的初始大小和位置
        geo = self.frameGeometry()
        x, y, width, height = geo.x(), geo.y(), geo.width(), geo.height()
        if edges is Edge.LeftEdge:
            width -= pos.x()
            if width <= self.min_width:
                return
            else:
                x += pos.x()
        elif edges is Edge.RightEdge:
            width = pos.x()
            if width <= self.min_width:
                return
        elif edges is Edge.TopEdge:
            height -= pos.y()
            if height <= self.min_height:
                return
            else:
                y += pos.y()
        elif edges is Edge.BottomEdge:
            height = pos.y()
            if height <= self.min_height:
                return
        elif edges is Edge.LeftTopEdge:
            width -= pos.x()
            if width <= self.min_width:
                width = geo.width()
            else:
                x += pos.x()
            height -= pos.y()
            if height <= self.min_height:
                height = geo.height()
            else:
                y += pos.y()
        elif edges is Edge.LeftBottomEdge:
            width -= pos.x()
            if width <= self.min_width:
                width = geo.width()
            else:
                x += pos.x()
            height = pos.y()
            if height <= self.min_height:
                height = geo.height()
        elif edges is Edge.RightTopEdge:
            width = pos.x()
            if width <= self.min_width:
                width = geo.width()
            height -= pos.y()
            if height <= self.min_height:
                height = geo.height()
            else:
                y += pos.y()
        elif edges is Edge.RightBottomEdge:
            width = pos.x()
            if width <= self.min_width:
                width = geo.width()
            height = pos.y()
            if height <= self.min_height:
                height = geo.height()
        self.setGeometry(x, y, width, height)

    def resizeEvent(self, event):
        # 处理窗口大小更改事件
        self.setCursor(Qt.CursorShape.ArrowCursor)

案例:

if __name__ == '__main__':

    # 只需继承ElWindow即可
    class MindWindow(ElWindow):
        def __init__(self):
            super(MindWindow, self).__init__()
            pass


    app = QApplication([])
    window = MindWindow()
    window.show()
    app.exec()

 

二、自定义窗口标题(使用者可直接Copy)

from PySide6 import QtGui, QtWidgets
from PySide6.QtCore import Qt, QSize
from PySide6.QtGui import QIcon, QFont
from PySide6.QtWidgets import QPushButton, QLabel, QWidget, QApplication
from components.el_title_bar.el_window import ElWindow


# 自定义标题栏
class ElTitleBar:
    def __init__(self, window: QtWidgets, window_title: str = ""):
        self.window = window
        # 默认标题栏高度 必须设
        self.DEFAULT_TITILE_BAR_HEIGHT = 40
        # 存储父类的双击事件
        self.mouseDoubleClickEvent_parent = self.window.mouseDoubleClickEvent
        # 将本类的双击事件赋值给将父类的双击事件
        self.window.mouseDoubleClickEvent = self.mouseDoubleClickEvent

        # 存储父类的窗口大小改变事件
        self.resizeEvent_parent = self.window.resizeEvent
        # 将本类的窗口大小改变事件赋值给将父类的窗口大小改变事件
        self.window.resizeEvent = self.resizeEvent

        # 设置ui文件里main_layout上边距,以免遮挡标题栏
        self.window.setContentsMargins(0, self.DEFAULT_TITILE_BAR_HEIGHT, 0, 0)

        # 1.设置无边框 和 透明背景 无边框必须设置全,不然会导致点击任务栏不能最小化窗口
        self.window.setWindowFlags(
            Qt.Window
            | Qt.FramelessWindowHint
            | Qt.WindowSystemMenuHint
            | Qt.WindowMinimizeButtonHint
            | Qt.WindowMaximizeButtonHint
        )
        # self.window.setAttribute(Qt.WA_TranslucentBackground)
        # 2.添加自定义的标题栏到最顶部
        self.title = QLabel(window_title, self.window)
        self.title.setFont(QFont("STKaiti", 14))
        # 3.设置标题栏样式
        self.setStyle()
        # 4.添加按钮
        # 添加关闭按钮
        self.close_btn = QPushButton("", self.window)
        self.close_btn.setGeometry(self.window.width() - 33, 10, 20, 20)
        # 添加最大化按钮
        self.max_btn = QPushButton("", self.window)
        self.max_btn.setGeometry(self.window.width() - 66, 10, 20, 20)
        # 添加最小化按钮
        self.min_btn = QPushButton("", self.window)
        self.min_btn.setGeometry(self.window.width() - 99, 10, 20, 20)
        # 设置三个按钮的鼠标样式
        self.close_btn.setCursor(Qt.PointingHandCursor)
        self.max_btn.setCursor(Qt.PointingHandCursor)
        self.min_btn.setCursor(Qt.PointingHandCursor)
        # 设置三个按钮的样式
        self.close_btn.setStyleSheet(
            "QPushButton{border-image:url('./images/close.png');background:#ff625f;border-radius:10px;}"
            "QPushButton:hover{background:#eb4845;}"
        )
        self.max_btn.setStyleSheet(
            "QPushButton{border-image:url('./images/max.png');background:#ffbe2f;border-radius:10px;}"
            "QPushButton:hover{background:#ecae27;}"
        )
        self.min_btn.setStyleSheet(
            "QPushButton{border-image:url('./images/min.png');background:#29c941;border-radius:10px;}"
            "QPushButton:hover{background:#1ac033;}"
        )

        # 5.添加工具栏按钮事件
        # 关闭按钮点击绑定窗口关闭事件
        self.close_btn.clicked.connect(self.window.close)
        # 最大化按钮绑定窗口最大化事件
        self.max_btn.clicked.connect(self.setMaxEvent)
        # 最小化按钮绑定窗口最小化事件
        self.min_btn.clicked.connect(self.window.showMinimized)
        # 6.记录全屏窗口的大小-ps非常有用
        self.window_max_size = None
        # 7.设置标题栏鼠标跟踪 鼠标移入触发,不设置,移入标题栏不触发
        self.title.setMouseTracking(True)

    def setMaxEvent(self, flag=False):
        """
        @description  最大化按钮绑定窗口最大化事件和事件 拿出来是因为拖动标题栏时需要恢复界面大小
        @param flag 是否是拖动标题栏 bool
        @return
        """
        if flag:
            if self.window.isMaximized():
                self.window.showNormal()
                self.max_btn.setStyleSheet(
                    "QPushButton{border-image:url('./images/max.png');background:#ffbe2f;border-radius:10px;}"
                    "QPushButton:hover{background:#ecae27;}"
                )
                return self.window_max_size
            return None
        else:
            if self.window.isMaximized():
                self.window.showNormal()
                self.max_btn.setStyleSheet(
                    "QPushButton{border-image:url('./images/max.png');background:#ffbe2f;border-radius:10px;}"
                    "QPushButton:hover{background:#ecae27;}"
                )
            else:
                self.window.showMaximized()
                self.max_btn.setStyleSheet(
                    "QPushButton{border-image:url('./images/restore.png');background:#ffbe2f;border-radius:10px;}"
                    "QPushButton:hover{background:#ecae27;}"
                )
                # 记录最大化窗口的大小  用于返回最大化时拖动窗口恢复前的大小 这个程序循环帧会取不到恢复前的宽度
                self.window_max_size = QSize(self.window.width(), self.window.height())

    def setStyle(self, style: str = ""):
        """
        @description 设置自定义标题栏样式
        @param
        @return
        """
        # 想要边框 加上border:1px solid #cccccc;
        DEFAULT_STYLE = """
                            background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #fafafa,stop:1 #d1d1d1);
                            color:#333333;padding:10px;border:1px solid #c6c6c6;
                            border-top-left-radius:4px;
                            border-top-right-radius:4px;
                        """
        self.title.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        # 设置样式
        self.title.setStyleSheet(DEFAULT_STYLE if not style else DEFAULT_STYLE + style)
        # 设置大小
        self.title.setGeometry(0, 0, self.window.width(), self.DEFAULT_TITILE_BAR_HEIGHT)

    def mouseDoubleClickEvent(self, a0: QtGui.QMouseEvent) -> None:
        """
        @description 鼠标双击事件
        @param
        @return
        """
        # 如果双击的是鼠标左键 且在标题栏范围内 则放大缩小窗口
        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:
        """
        @description  窗口缩放事件
        @param
        @return
        """
        # 最大化最小化的时候,需要去改变按钮组位置
        self.close_btn.move(self.window.width() - 33, 10)
        self.max_btn.move(self.window.width() - 66, 10)
        self.min_btn.move(self.window.width() - 99, 10)
        self.title.resize(self.window.width(), self.DEFAULT_TITILE_BAR_HEIGHT)
        return self.resizeEvent_parent(a0)

 案例:(此时还不能移动和缩放

​
if __name__ == '__main__':

    class MindWindow(QWidget):
        def __init__(self):
            super(MindWindow, self).__init__()
            # 挂载标题栏即可
            ElTitleBar(self, window_title="地址管理")


    app = QApplication([])
    window = MindWindow()
    window.show()
    app.exec()

​

三、缩放和标题一起使用案例:

if __name__ == '__main__':


    # 继承ElWindow
    class MindWindow(ElWindow):
        def __init__(self):
            super(MindWindow, self).__init__()
            self.resize(400,200)
            # 挂载标题栏
            ElTitleBar(self, window_title="地址管理")


    app = QApplication([])
    window = MindWindow()
    window.show()
    app.exec()

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

文子阳

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

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

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

打赏作者

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

抵扣说明:

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

余额充值