0

我正在尝试制作音量按钮,单击时应静音/取消静音,悬停时应弹出QSlider,因此用户可以设置他想要的任何级别。现在我试图通过在其中显示滑块窗口enterEvent并将其隐藏来实现这一点leaveEvent

class VolumeButton(QToolButton):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setIcon(volumeicon)

        self.slider = QSlider()
        self.slider.setWindowFlags(Qt.FramelessWindowHint)
        self.slider.setWindowModality(Qt.NonModal)

    def enterEvent(self, event):
        self.slider.move(self.mapToGlobal(self.rect().topLeft()))
        self.slider.show()

    def leaveEvent(self, event):
        self.slider.hide()

问题是它mapToGlobal似乎以某种方式连接enterEvent并且它创建了递归,但是没有mapToGlobal我不能将滑块放在正确的位置。我不确定这是否是实现预期QToolButton结果FramelessWindow的正确小部件,因此请告诉我是否有更好的方法来做到这一点。

4

1 回答 1

1

问题不mapToGlobal在于 ,而leaveEvent在于一旦显示滑块就会触发 :由于滑块位于鼠标的相同坐标中,Qt 认为鼠标已“离开”按钮(并“进入”滑块)。

您不能为此使用简单的 leaveEvent,因为您需要检查按钮滑块的光标位置。

一个可能的解决方案是创建一个包含两个小部件几何形状的 QRegion 并检查光标是否在其中。为了处理滑块的鼠标事件,必须在其上安装事件过滤器:

class VolumeButton(QToolButton):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setIcon(volumeicon)

        self.slider = QSlider()
        self.slider.setWindowFlags(Qt.FramelessWindowHint)
        self.slider.setWindowModality(Qt.NonModal)
        self.slider.installEventFilter(self)

    def isInside(self):
        buttonRect = self.rect().translated(self.mapToGlobal(QPoint()))
        if not self.slider.isVisible():
            return QCursor.pos() in buttonRect
        region = QRegion(buttonRect)
        region |= QRegion(self.slider.geometry())
        return region.contains(QCursor.pos())

    def enterEvent(self, event):
        if not self.slider.isVisible():
            self.slider.move(self.mapToGlobal(QPoint()))
            self.slider.show()

    def leaveEvent(self, event):
        if not self.isInside():
            self.slider.hide()

    def eventFilter(self, source, event):
        if source == self.slider and event.type() == event.Leave:
            if not self.isInside():
                self.slider.hide()
        return super().eventFilter(source, event)

请注意,self.rect()它始终位于坐标(0, 0),因此您可以使用它self.mapToGlobal(QPoint())来获取小部件的全局位置。另一方面,您可能希望在按钮“外部”显示滑块,因此您可以使用self.mapToGlobal(self.rect().bottomLeft()).
请注意,如果用户在按钮和滑块之间的间隙中移动鼠标,则尝试将滑块放置在按钮几何图形“外部”可能会导致问题;在这种情况下,您将需要创建一个有效的区域来覆盖两个小部件以及它们之间的合理空间。

于 2021-02-24T18:16:59.733 回答