Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【特性】建议遮罩对话框基类增加支持鼠标按住中间的对话框移动的方法 #1058

Open
qianye216 opened this issue Feb 18, 2025 · 0 comments

Comments

@qianye216
Copy link

qianye216 commented Feb 18, 2025

目前的遮罩对话框无法移动中间的对话框组件,可以增加一个方法控制是否支持按住中间的self.widget进行移动
以下代码是通过AI生成的,大佬可以参考下:

# coding:utf-8
from PyQt5.QtCore import QEasingCurve, QPropertyAnimation, Qt, QEvent
from PyQt5.QtGui import QColor, QResizeEvent
from PyQt5.QtWidgets import (QDialog, QGraphicsDropShadowEffect,
                             QGraphicsOpacityEffect, QHBoxLayout, QWidget, QFrame)

from ...common.config import isDarkTheme


class MaskDialogBase(QDialog):
    """ Dialog box base class with a mask """

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self._isClosableOnMaskClicked = False
        self._hBoxLayout = QHBoxLayout(self)
        self.windowMask = QWidget(self)
        self._dragPos = None  # 用于存储拖动时的鼠标位置
        self._isDraggable = False  # 添加可拖动状态标志
        

        # dialog box in the center of mask, all widgets take it as parent
        self.widget = QFrame(self, objectName='centerWidget')
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setGeometry(0, 0, parent.width(), parent.height())

        c = 0 if isDarkTheme() else 255
        self.windowMask.resize(self.size())
        self.windowMask.setStyleSheet(f'background:rgba({c}, {c}, {c}, 0.6)')
        self._hBoxLayout.addWidget(self.widget)
        self.setShadowEffect()

        self.window().installEventFilter(self)
        self.windowMask.installEventFilter(self)
        self.widget.installEventFilter(self)  # 为中心widget安装事件过滤器

    def setShadowEffect(self, blurRadius=60, offset=(0, 10), color=QColor(0, 0, 0, 100)):
        """ add shadow to dialog """
        shadowEffect = QGraphicsDropShadowEffect(self.widget)
        shadowEffect.setBlurRadius(blurRadius)
        shadowEffect.setOffset(*offset)
        shadowEffect.setColor(color)
        self.widget.setGraphicsEffect(None)
        self.widget.setGraphicsEffect(shadowEffect)

    def setMaskColor(self, color: QColor):
        """ set the color of mask """
        self.windowMask.setStyleSheet(f"""
            background: rgba({color.red()}, {color.green()}, {color.blue()}, {color.alpha()})
        """)
    def setDraggable(self, draggable: bool):
        """设置中心组件是否可拖动
        
        Args:
            draggable (bool): 是否允许拖动
        """
        self._isDraggable = draggable

    def isDraggable(self) -> bool:
        """获取中心组件是否可拖动
        
        Returns:
            bool: 是否可拖动
        """
        return self._isDraggable

    def showEvent(self, e):
        """ fade in """
        opacityEffect = QGraphicsOpacityEffect(self)
        self.setGraphicsEffect(opacityEffect)
        opacityAni = QPropertyAnimation(opacityEffect, b'opacity', self)
        opacityAni.setStartValue(0)
        opacityAni.setEndValue(1)
        opacityAni.setDuration(200)
        opacityAni.setEasingCurve(QEasingCurve.InSine)
        opacityAni.finished.connect(lambda: self.setGraphicsEffect(None))
        opacityAni.start()
        super().showEvent(e)

    def done(self, code):
        """ fade out """
        self.widget.setGraphicsEffect(None)
        opacityEffect = QGraphicsOpacityEffect(self)
        self.setGraphicsEffect(opacityEffect)
        opacityAni = QPropertyAnimation(opacityEffect, b'opacity', self)
        opacityAni.setStartValue(1)
        opacityAni.setEndValue(0)
        opacityAni.setDuration(100)
        opacityAni.finished.connect(lambda: self._onDone(code))
        opacityAni.start()

    def _onDone(self, code):
        self.setGraphicsEffect(None)
        QDialog.done(self, code)

    def isClosableOnMaskClicked(self):
        return self._isClosableOnMaskClicked

    def setClosableOnMaskClicked(self, isClosable: bool):
        self._isClosableOnMaskClicked = isClosable

    def resizeEvent(self, e):
        self.windowMask.resize(self.size())

    def eventFilter(self, obj, e: QEvent):
        if obj is self.window():
            if e.type() == QEvent.Resize:
                re = QResizeEvent(e)
                self.resize(re.size())
                
        elif obj is self.windowMask:
            if e.type() == QEvent.MouseButtonRelease and e.button() == Qt.LeftButton \
                    and self.isClosableOnMaskClicked():
                self.reject()
                
        elif obj is self.widget and self._isDraggable:  # 只在允许拖动时处理事件
            if e.type() == QEvent.MouseButtonPress and e.button() == Qt.LeftButton:
                self._dragPos = e.pos()
                return True
                
            elif e.type() == QEvent.MouseMove and self._dragPos is not None:
                # 计算新位置
                delta = e.pos() - self._dragPos
                new_pos = self.widget.pos() + delta
                
                # 获取遮罩和widget的大小
                mask_rect = self.windowMask.rect()
                widget_rect = self.widget.rect()
                
                # 限制x坐标
                new_pos.setX(max(0, min(new_pos.x(), 
                    mask_rect.width() - widget_rect.width())))
                
                # 限制y坐标 
                new_pos.setY(max(0, min(new_pos.y(),
                    mask_rect.height() - widget_rect.height())))
                
                # 更新位置
                self.widget.move(new_pos)
                return True
                
            elif e.type() == QEvent.MouseButtonRelease:
                self._dragPos = None
                return True

        return super().eventFilter(obj, e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant