QToolButton 与弹出 QSlider
QToolButton with popup QSlider
我正在尝试制作音量按钮,点击它应该 mute/unmute 并且在悬停时它应该弹出 QSlider
,因此用户可以设置他想要的任何级别。现在我试图通过在 enterEvent
中显示滑块 window 并将其隐藏在 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
是否是实现预期结果的正确小部件,所以如果有更好的方法,请告诉我。
问题不在于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())
.
请注意,如果用户将鼠标移动到按钮和滑块之间的间隙中,则尝试将滑块放置在按钮几何结构“外部”可能会导致问题;在这种情况下,您将需要创建一个有效区域来覆盖小部件 和 ,它们之间的合理 space。
我正在尝试制作音量按钮,点击它应该 mute/unmute 并且在悬停时它应该弹出 QSlider
,因此用户可以设置他想要的任何级别。现在我试图通过在 enterEvent
中显示滑块 window 并将其隐藏在 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
是否是实现预期结果的正确小部件,所以如果有更好的方法,请告诉我。
问题不在于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())
.
请注意,如果用户将鼠标移动到按钮和滑块之间的间隙中,则尝试将滑块放置在按钮几何结构“外部”可能会导致问题;在这种情况下,您将需要创建一个有效区域来覆盖小部件 和 ,它们之间的合理 space。