From cdd03cea6de19cb75292789df269040fc5a86ac4 Mon Sep 17 00:00:00 2001 From: zhiyiYo <1319158137@qq.com> Date: Tue, 2 May 2023 22:32:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=B7=A5=E5=85=B7=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E4=BD=8D=E7=BD=AE=E6=9E=9A=E4=B8=BE=20`ToolTipPositio?= =?UTF-8?q?n`=20=E5=92=8C=E5=AF=BC=E8=88=AA=E8=8F=9C=E5=8D=95=E9=A1=B9?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E6=8F=90=E7=A4=BA=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?#159?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/navigation/demo.py | 57 ++--- examples/navigation2/demo.py | 55 ++--- examples/tool_tip/demo.py | 28 +-- .../navigation/navigation_interface.py | 21 +- .../components/navigation/navigation_panel.py | 31 ++- qfluentwidgets/components/widgets/__init__.py | 2 +- qfluentwidgets/components/widgets/tool_tip.py | 195 +++++++++++++++--- 7 files changed, 255 insertions(+), 134 deletions(-) diff --git a/examples/navigation/demo.py b/examples/navigation/demo.py index 2fbc6ba..c733dc1 100644 --- a/examples/navigation/demo.py +++ b/examples/navigation/demo.py @@ -66,7 +66,7 @@ class Window(FramelessWindow): self.setTitleBar(StandardTitleBar(self)) # use dark theme mode - setTheme(Theme.DARK) + # setTheme(Theme.DARK) # change the theme color # setThemeColor('#0078d4') @@ -82,12 +82,6 @@ class Window(FramelessWindow): self.folderInterface = Widget('Folder Interface', self) self.settingInterface = Widget('Setting Interface', self) - self.stackWidget.addWidget(self.searchInterface) - self.stackWidget.addWidget(self.musicInterface) - self.stackWidget.addWidget(self.videoInterface) - self.stackWidget.addWidget(self.folderInterface) - self.stackWidget.addWidget(self.settingInterface) - # initialize layout self.initLayout() @@ -104,35 +98,14 @@ class Window(FramelessWindow): self.hBoxLayout.setStretchFactor(self.stackWidget, 1) def initNavigation(self): - self.navigationInterface.addItem( - routeKey=self.searchInterface.objectName(), - icon=FIF.SEARCH, - text='Search', - onClick=lambda: self.switchTo(self.searchInterface) - ) - self.navigationInterface.addItem( - routeKey=self.musicInterface.objectName(), - icon=FIF.MUSIC, - text='Music library', - onClick=lambda: self.switchTo(self.musicInterface) - ) - self.navigationInterface.addItem( - routeKey=self.videoInterface.objectName(), - icon=FIF.VIDEO, - text='Video library', - onClick=lambda: self.switchTo(self.videoInterface) - ) + self.addSubInterface(self.searchInterface, FIF.SEARCH, 'Search') + self.addSubInterface(self.musicInterface, FIF.MUSIC, 'Music library') + self.addSubInterface(self.videoInterface, FIF.VIDEO, 'Video library') self.navigationInterface.addSeparator() # add navigation items to scroll area - self.navigationInterface.addItem( - routeKey=self.folderInterface.objectName(), - icon=FIF.FOLDER, - text='Folder library', - onClick=lambda: self.switchTo(self.folderInterface), - position=NavigationItemPosition.SCROLL - ) + self.addSubInterface(self.folderInterface, FIF.FOLDER, 'Folder library', NavigationItemPosition.SCROLL) # for i in range(1, 21): # self.navigationInterface.addItem( # f'folder{i}', @@ -150,13 +123,7 @@ class Window(FramelessWindow): position=NavigationItemPosition.BOTTOM ) - self.navigationInterface.addItem( - routeKey=self.settingInterface.objectName(), - icon=FIF.SETTING, - text='Settings', - onClick=lambda: self.switchTo(self.settingInterface), - position=NavigationItemPosition.BOTTOM - ) + self.addSubInterface(self.settingInterface, FIF.SETTING, 'Settings', NavigationItemPosition.BOTTOM) #!IMPORTANT: don't forget to set the default route key if you enable the return button # self.navigationInterface.setDefaultRouteKey(self.musicInterface.objectName()) @@ -179,6 +146,18 @@ class Window(FramelessWindow): self.setQss() + def addSubInterface(self, interface, icon, text: str, position=NavigationItemPosition.TOP): + """ add sub interface """ + self.stackWidget.addWidget(interface) + self.navigationInterface.addItem( + routeKey=interface.objectName(), + icon=icon, + text=text, + onClick=lambda: self.switchTo(interface), + position=position, + tooltip=text + ) + def setQss(self): color = 'dark' if isDarkTheme() else 'light' with open(f'resource/{color}/demo.qss', encoding='utf-8') as f: diff --git a/examples/navigation2/demo.py b/examples/navigation2/demo.py index 29f4ca5..ade3c47 100644 --- a/examples/navigation2/demo.py +++ b/examples/navigation2/demo.py @@ -109,12 +109,6 @@ class Window(FramelessWindow): self.folderInterface = Widget('Folder Interface', self) self.settingInterface = Widget('Setting Interface', self) - self.stackWidget.addWidget(self.searchInterface) - self.stackWidget.addWidget(self.musicInterface) - self.stackWidget.addWidget(self.videoInterface) - self.stackWidget.addWidget(self.folderInterface) - self.stackWidget.addWidget(self.settingInterface) - # initialize layout self.initLayout() @@ -134,35 +128,14 @@ class Window(FramelessWindow): self.navigationInterface.displayModeChanged.connect(self.titleBar.raise_) def initNavigation(self): - self.navigationInterface.addItem( - routeKey=self.searchInterface.objectName(), - icon=FIF.SEARCH, - text='Search', - onClick=lambda: self.switchTo(self.searchInterface) - ) - self.navigationInterface.addItem( - routeKey=self.musicInterface.objectName(), - icon=FIF.MUSIC, - text='Music library', - onClick=lambda: self.switchTo(self.musicInterface) - ) - self.navigationInterface.addItem( - routeKey=self.videoInterface.objectName(), - icon=FIF.VIDEO, - text='Video library', - onClick=lambda: self.switchTo(self.videoInterface) - ) + self.addSubInterface(self.searchInterface, FIF.SEARCH, 'Search') + self.addSubInterface(self.musicInterface, FIF.MUSIC, 'Music library') + self.addSubInterface(self.videoInterface, FIF.VIDEO, 'Video library') self.navigationInterface.addSeparator() # add navigation items to scroll area - self.navigationInterface.addItem( - routeKey=self.folderInterface.objectName(), - icon=FIF.FOLDER, - text='Folder library', - onClick=lambda: self.switchTo(self.folderInterface), - position=NavigationItemPosition.SCROLL - ) + self.addSubInterface(self.folderInterface, FIF.FOLDER, 'Folder library', NavigationItemPosition.SCROLL) # for i in range(1, 21): # self.navigationInterface.addItem( # f'folder{i}', @@ -180,13 +153,7 @@ class Window(FramelessWindow): position=NavigationItemPosition.BOTTOM ) - self.navigationInterface.addItem( - routeKey=self.settingInterface.objectName(), - icon=FIF.SETTING, - text='Settings', - onClick=lambda: self.switchTo(self.settingInterface), - position=NavigationItemPosition.BOTTOM - ) + self.addSubInterface(self.settingInterface, FIF.SETTING, 'Settings', NavigationItemPosition.BOTTOM) #!IMPORTANT: don't forget to set the default route key self.navigationInterface.setDefaultRouteKey(self.musicInterface.objectName()) @@ -209,6 +176,18 @@ class Window(FramelessWindow): self.setQss() + def addSubInterface(self, interface, icon, text: str, position=NavigationItemPosition.TOP): + """ add sub interface """ + self.stackWidget.addWidget(interface) + self.navigationInterface.addItem( + routeKey=interface.objectName(), + icon=icon, + text=text, + onClick=lambda: self.switchTo(interface), + position=position, + tooltip=text + ) + def setQss(self): color = 'dark' if isDarkTheme() else 'light' with open(f'resource/{color}/demo.qss', encoding='utf-8') as f: diff --git a/examples/tool_tip/demo.py b/examples/tool_tip/demo.py index 7690e48..8e950cf 100644 --- a/examples/tool_tip/demo.py +++ b/examples/tool_tip/demo.py @@ -4,7 +4,7 @@ from PyQt5.QtCore import QEvent, QPoint, Qt, QUrl from PyQt5.QtGui import QDesktopServices from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout -from qfluentwidgets import ToolTip, ToolTipFilter, setTheme, Theme, PushButton +from qfluentwidgets import ToolTip, ToolTipFilter, setTheme, Theme, PushButton, ToolTipPosition class Demo(QWidget): @@ -15,7 +15,6 @@ class Demo(QWidget): self.button1 = PushButton('キラキラ', self) self.button2 = PushButton('食べた愛', self) self.button3 = PushButton('シアワセ', self) - self._toolTip = ToolTip(parent=self) # use dark theme # setTheme(Theme.DARK) @@ -24,11 +23,11 @@ class Demo(QWidget): self.button2.setToolTip('aiko - 食べた愛 🥰') self.button3.setToolTip('aiko - シアワセ 😊') self.button1.setToolTipDuration(1000) - self.button2.setToolTipDuration(5000) + # self.button2.setToolTipDuration(-1) # won't disappear - self.button1.installEventFilter(self) - self.button2.installEventFilter(self) - self.button3.installEventFilter(ToolTipFilter(self.button3)) + self.button1.installEventFilter(ToolTipFilter(self.button1, 0, ToolTipPosition.TOP)) + self.button2.installEventFilter(ToolTipFilter(self.button2, 0, ToolTipPosition.BOTTOM)) + self.button3.installEventFilter(ToolTipFilter(self.button3, 300, ToolTipPosition.RIGHT)) self.button1.clicked.connect(lambda: QDesktopServices.openUrl(QUrl( 'https://www.youtube.com/watch?v=S0bXDRY1DGM&list=RDMM&index=1'))) @@ -44,26 +43,9 @@ class Demo(QWidget): self.hBox.addWidget(self.button3) self.resize(480, 240) - self._toolTip.hide() self.setStyleSheet('Demo{background:white}') - def eventFilter(self, obj, e: QEvent): - if obj is self: - return super().eventFilter(obj, e) - - tip = self._toolTip - if e.type() == QEvent.Enter: - tip.setText(obj.toolTip()) - tip.setDuration(obj.toolTipDuration()) - tip.adjustPos(obj.mapToGlobal(QPoint()), obj.size()) - tip.show() - elif e.type() == QEvent.Leave: - tip.hide() - elif e.type() == QEvent.ToolTip: - return True - - return super().eventFilter(obj, e) if __name__ == '__main__': diff --git a/qfluentwidgets/components/navigation/navigation_interface.py b/qfluentwidgets/components/navigation/navigation_interface.py index 9ae5d04..462d3bc 100644 --- a/qfluentwidgets/components/navigation/navigation_interface.py +++ b/qfluentwidgets/components/navigation/navigation_interface.py @@ -6,6 +6,7 @@ from PyQt5.QtGui import QResizeEvent, QIcon from PyQt5.QtWidgets import QWidget from .navigation_panel import NavigationPanel, NavigationItemPosition, NavigationWidget, NavigationDisplayMode +from .navigation_widget import NavigationPushButton from ...common.style_sheet import FluentStyleSheet from ...common.icon import FluentIconBase @@ -41,7 +42,7 @@ class NavigationInterface(QWidget): FluentStyleSheet.NAVIGATION_INTERFACE.apply(self) def addItem(self, routeKey: str, icon: Union[str, QIcon, FluentIconBase], text: str, onClick, selectable=True, - position=NavigationItemPosition.TOP): + position=NavigationItemPosition.TOP, tooltip: str = None) -> NavigationPushButton: """ add navigation item Parameters @@ -58,16 +59,21 @@ class NavigationInterface(QWidget): onClick: callable the slot connected to item clicked signal + selectable: bool + whether the item is selectable + position: NavigationItemPosition where the button is added - selectable: bool - whether the item is selectable + tooltip: str + the tooltip of item """ - self.panel.addItem(routeKey, icon, text, onClick, selectable, position) + button = self.panel.addItem(routeKey, icon, text, onClick, selectable, position, tooltip) self.setMinimumHeight(self.panel.layoutMinHeight()) + return button - def addWidget(self, routeKey: str, widget: NavigationWidget, onClick, position=NavigationItemPosition.TOP): + def addWidget(self, routeKey: str, widget: NavigationWidget, onClick, position=NavigationItemPosition.TOP, + tooltip: str = None): """ add custom widget Parameters @@ -83,8 +89,11 @@ class NavigationInterface(QWidget): position: NavigationItemPosition where the button is added + + tooltip: str + the tooltip of widget """ - self.panel.addWidget(routeKey, widget, onClick, position) + self.panel.addWidget(routeKey, widget, onClick, position, tooltip) self.setMinimumHeight(self.panel.layoutMinHeight()) def addSeparator(self, position=NavigationItemPosition.TOP): diff --git a/qfluentwidgets/components/navigation/navigation_panel.py b/qfluentwidgets/components/navigation/navigation_panel.py index a8a3b98..08cfa60 100644 --- a/qfluentwidgets/components/navigation/navigation_panel.py +++ b/qfluentwidgets/components/navigation/navigation_panel.py @@ -8,6 +8,7 @@ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QFrame, QApplication from .navigation_widget import NavigationPushButton, NavigationToolButton, NavigationWidget, NavigationSeparator from ..widgets.scroll_area import SingleDirectionScrollArea +from ..widgets.tool_tip import ToolTipFilter from ...common.style_sheet import FluentStyleSheet from ...common.icon import FluentIconBase from ...common.icon import FluentIcon as FIF @@ -85,6 +86,13 @@ class NavigationPanel(QFrame): self.history.emptyChanged.connect(self.returnButton.setDisabled) self.returnButton.clicked.connect(self.history.pop) + # add tool tip + self.returnButton.installEventFilter(ToolTipFilter(self.returnButton, 1000)) + self.returnButton.setToolTip(self.tr('Back')) + + self.menuButton.installEventFilter(ToolTipFilter(self.menuButton, 1000)) + self.menuButton.setToolTip(self.tr('Open Navigation')) + self.setProperty('menu', False) self.scrollWidget.setObjectName('scrollWidget') FluentStyleSheet.NAVIGATION_INTERFACE.apply(self) @@ -112,7 +120,8 @@ class NavigationPanel(QFrame): self.topLayout.addWidget(self.returnButton, 0, Qt.AlignTop) self.topLayout.addWidget(self.menuButton, 0, Qt.AlignTop) - def addItem(self, routeKey: str, icon: Union[str, QIcon, FluentIconBase], text: str, onClick, selectable=True, position=NavigationItemPosition.TOP): + def addItem(self, routeKey: str, icon: Union[str, QIcon, FluentIconBase], text: str, onClick, selectable=True, + position=NavigationItemPosition.TOP, tooltip: str = None): """ add navigation item Parameters @@ -134,14 +143,20 @@ class NavigationPanel(QFrame): selectable: bool whether the item is selectable + + tooltip: str + the tooltip of item """ if routeKey in self.items: return button = NavigationPushButton(icon, text, selectable, self) - self.addWidget(routeKey, button, onClick, position) + self.addWidget(routeKey, button, onClick, position, tooltip) + + return button - def addWidget(self, routeKey: str, widget: NavigationWidget, onClick, position=NavigationItemPosition.TOP): + def addWidget(self, routeKey: str, widget: NavigationWidget, onClick, position=NavigationItemPosition.TOP, + tooltip: str = None): """ add custom widget Parameters @@ -157,6 +172,9 @@ class NavigationPanel(QFrame): position: NavigationItemPosition where the button is added + + tooltip: str + the tooltip of widget """ if routeKey in self.items: return @@ -169,6 +187,10 @@ class NavigationPanel(QFrame): if self.displayMode in [NavigationDisplayMode.EXPAND, NavigationDisplayMode.MENU]: widget.setCompacted(False) + if tooltip: + widget.setToolTip(tooltip) + widget.installEventFilter(ToolTipFilter(widget, 1000)) + self._addWidgetToLayout(widget, position) def addSeparator(self, position=NavigationItemPosition.TOP): @@ -233,6 +255,7 @@ class NavigationPanel(QFrame): """ expand navigation panel """ self._setWidgetCompacted(False) self.expandAni.setProperty('expand', True) + self.menuButton.setToolTip(self.tr('Close Navigation')) # determine the display mode according to the width of window # https://learn.microsoft.com/en-us/windows/apps/design/controls/navigationview#default @@ -269,6 +292,8 @@ class NavigationPanel(QFrame): self.expandAni.setProperty('expand', False) self.expandAni.start() + self.menuButton.setToolTip(self.tr('Open Navigation')) + def toggle(self): """ toggle navigation panel """ if self.displayMode in [NavigationDisplayMode.COMPACT, NavigationDisplayMode.MINIMAL]: diff --git a/qfluentwidgets/components/widgets/__init__.py b/qfluentwidgets/components/widgets/__init__.py index a4d3958..2b26f63 100644 --- a/qfluentwidgets/components/widgets/__init__.py +++ b/qfluentwidgets/components/widgets/__init__.py @@ -15,7 +15,7 @@ from .stacked_widget import PopUpAniStackedWidget, OpacityAniStackedWidget from .state_tool_tip import StateToolTip from .switch_button import SwitchButton, IndicatorPosition from .table_view import TableView, TableWidget, TableItemDelegate -from .tool_tip import ToolTip, ToolTipFilter +from .tool_tip import ToolTip, ToolTipFilter, ToolTipPosition from .tree_view import TreeWidget, TreeView from .cycle_list_widget import CycleListWidget from .scroll_bar import ScrollBar, SmoothScrollBar, SmoothScrollDelegate \ No newline at end of file diff --git a/qfluentwidgets/components/widgets/tool_tip.py b/qfluentwidgets/components/widgets/tool_tip.py index 642f3e5..7a72c2e 100644 --- a/qfluentwidgets/components/widgets/tool_tip.py +++ b/qfluentwidgets/components/widgets/tool_tip.py @@ -1,5 +1,7 @@ # coding:utf-8 -from PyQt5.QtCore import QEvent, QObject, QPoint, QTimer, Qt +from enum import Enum + +from PyQt5.QtCore import QEvent, QObject, QPoint, QTimer, Qt, QPropertyAnimation, QSize from PyQt5.QtGui import QColor, QCursor from PyQt5.QtWidgets import (QApplication, QFrame, QGraphicsDropShadowEffect, QHBoxLayout, QLabel, QWidget) @@ -7,13 +9,36 @@ from PyQt5.QtWidgets import (QApplication, QFrame, QGraphicsDropShadowEffect, from ...common import FluentStyleSheet +class ToolTipPosition(Enum): + """ Info bar position """ + + TOP = 0 + BOTTOM = 1 + LEFT = 2 + RIGHT = 3 + TOP_LEFT = 4 + TOP_RIGHT = 5 + BOTTOM_LEFT = 6 + BOTTOM_RIGHT = 7 + + class ToolTip(QFrame): """ Tool tip """ def __init__(self, text='', parent=None): + """ + Parameters + ---------- + text: str + the text of tool tip + + parent: QWidget + parent widget + """ super().__init__(parent=parent) self.__text = text self.__duration = 1000 + self.container = QFrame(self) self.timer = QTimer(self) @@ -27,6 +52,10 @@ class ToolTip(QFrame): self.containerLayout.addWidget(self.label) self.containerLayout.setContentsMargins(8, 6, 8, 6) + # add opacity effect + self.opacityAni = QPropertyAnimation(self, b'windowOpacity', self) + self.opacityAni.setDuration(150) + # add shadow self.shadowEffect = QGraphicsDropShadowEffect(self) self.shadowEffect.setBlurRadius(25) @@ -56,9 +85,15 @@ class ToolTip(QFrame): def duration(self): return self.__duration - def setDuration(self, duration): - """ set tooltip duration in milliseconds """ - self.__duration = abs(duration) + def setDuration(self, duration: int): + """ set tooltip duration in milliseconds + + Parameters + ---------- + duration: int + display duration in milliseconds, if `duration <= 0`, tooltip won't disappear automatically + """ + self.__duration = duration def __setQss(self): """ set style sheet """ @@ -69,40 +104,148 @@ class ToolTip(QFrame): self.adjustSize() def showEvent(self, e): + self.opacityAni.setStartValue(0) + self.opacityAni.setEndValue(1) + self.opacityAni.start() + self.timer.stop() - self.timer.start(self.__duration) + if self.duration() > 0: + self.timer.start(self.__duration + self.opacityAni.duration()) + super().showEvent(e) def hideEvent(self, e): self.timer.stop() super().hideEvent(e) - def adjustPos(self, pos, size): - """ adjust the position of tooltip relative to widget + def adjustPos(self, widget, position: ToolTipPosition): + """ adjust the position of tooltip relative to widget """ + manager = ToolTipPositionManager.make(position) + self.move(manager.position(self, widget)) - Parameters - ---------- - pos: QPoint - global position of widget - size: QSize - size of widget - """ - x = pos.x() + size.width()//2 - self.width()//2 - y = pos.y() - self.height() +class ToolTipPositionManager: + """ Tooltip position manager """ + + def position(self, tooltip: ToolTip, parent: QWidget) -> QPoint: + pos = self._pos(tooltip, parent) + x, y = pos.x(), pos.y() - # adjust postion to prevent tooltips from appearing outside the screen rect = QApplication.screenAt(QCursor.pos()).availableGeometry() - x = min(max(0, x) if QCursor().pos().x() >= 0 else x, rect.width() - self.width() - 4) - y = min(max(0, y), rect.height() - self.height() - 4) + x = min(max(-2, x) if QCursor().pos().x() >= 0 else x, rect.width() - tooltip.width() - 4) + y = min(max(-2, y), rect.height() - tooltip.height() - 4) + + return QPoint(x, y) + + def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint: + raise NotImplementedError + + @staticmethod + def make(position: ToolTipPosition): + """ mask info bar manager according to the display position """ + managers = { + ToolTipPosition.TOP: TopToolTipManager, + ToolTipPosition.BOTTOM: BottomToolTipManager, + ToolTipPosition.LEFT: LeftToolTipManager, + ToolTipPosition.RIGHT: RightToolTipManager, + ToolTipPosition.TOP_RIGHT: TopRightToolTipManager, + ToolTipPosition.BOTTOM_RIGHT: BottomRightToolTipManager, + ToolTipPosition.TOP_LEFT: TopLeftToolTipManager, + ToolTipPosition.BOTTOM_LEFT: BottomLeftToolTipManager, + } + + if position not in managers: + raise ValueError(f'`{position}` is an invalid info bar position.') - self.move(x, y) + return managers[position]() + + +class TopToolTipManager(ToolTipPositionManager): + """ Top tooltip position manager """ + + def _pos(self, tooltip: ToolTip, parent: QWidget): + pos = parent.mapToGlobal(QPoint()) + x = pos.x() + parent.width()//2 - tooltip.width()//2 + y = pos.y() - tooltip.height() + return QPoint(x, y) + + +class BottomToolTipManager(ToolTipPositionManager): + """ Bottom tooltip position manager """ + + def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint: + pos = parent.mapToGlobal(QPoint()) + x = pos.x() + parent.width()//2 - tooltip.width()//2 + y = pos.y() + parent.height() + return QPoint(x, y) + + +class LeftToolTipManager(ToolTipPositionManager): + """ Left tooltip position manager """ + + def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint: + pos = parent.mapToGlobal(QPoint()) + x = pos.x() - tooltip.width() + y = pos.y() + (parent.height() - tooltip.height()) // 2 + return QPoint(x, y) + + +class RightToolTipManager(ToolTipPositionManager): + """ Right tooltip position manager """ + + def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint: + pos = parent.mapToGlobal(QPoint()) + x = pos.x() + parent.width() + y = pos.y() + (parent.height() - tooltip.height()) // 2 + return QPoint(x, y) + + +class TopRightToolTipManager(ToolTipPositionManager): + """ Top right tooltip position manager """ + + def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint: + pos = parent.mapToGlobal(QPoint()) + x = pos.x() + parent.width() - tooltip.width() + \ + tooltip.layout().contentsMargins().right() + y = pos.y() - tooltip.height() + return QPoint(x, y) + + +class TopLeftToolTipManager(ToolTipPositionManager): + """ Top left tooltip position manager """ + + def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint: + pos = parent.mapToGlobal(QPoint()) + x = pos.x() - tooltip.layout().contentsMargins().left() + y = pos.y() - tooltip.height() + return QPoint(x, y) + + +class BottomRightToolTipManager(ToolTipPositionManager): + """ Bottom right tooltip position manager """ + + def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint: + pos = parent.mapToGlobal(QPoint()) + x = pos.x() + parent.width() - tooltip.width() + \ + tooltip.layout().contentsMargins().right() + y = pos.y() + parent.height() + return QPoint(x, y) + + +class BottomLeftToolTipManager(ToolTipPositionManager): + """ Bottom left tooltip position manager """ + + def _pos(self, tooltip: ToolTip, parent: QWidget) -> QPoint: + pos = parent.mapToGlobal(QPoint()) + x = pos.x() - tooltip.layout().contentsMargins().left() + y = pos.y() + parent.height() + return QPoint(x, y) class ToolTipFilter(QObject): """ Tool tip filter """ - def __init__(self, parent: QWidget, showDelay=300): + def __init__(self, parent: QWidget, showDelay=300, position=ToolTipPosition.TOP): """ Parameters ---------- @@ -111,11 +254,15 @@ class ToolTipFilter(QObject): showDelay: int show tool tip after how long the mouse hovers in milliseconds + + position: TooltipPosition + where to show the tooltip """ super().__init__(parent=parent) self.isEnter = False self._tooltip = None self._tooltipDelay = showDelay + self.position = position self.timer = QTimer(self) def eventFilter(self, obj: QObject, e: QEvent) -> bool: @@ -126,11 +273,11 @@ class ToolTipFilter(QObject): elif e.type() == QEvent.Enter: self.isEnter = True parent = self.parent() # type: QWidget - if parent.isWidgetType() and parent.toolTip(): + if parent.isWidgetType() and parent.toolTip() and parent.isEnabled(): if self._tooltip is None: self._tooltip = ToolTip(parent.toolTip(), parent.window()) - t = parent.toolTipDuration() if parent.toolTipDuration() > 0 else 1000 + t = parent.toolTipDuration() if parent.toolTipDuration() > 0 else -1 self._tooltip.setDuration(t) # show the tool tip after delay @@ -151,7 +298,7 @@ class ToolTipFilter(QObject): parent = self.parent() # type: QWidget self._tooltip.setText(parent.toolTip()) - self._tooltip.adjustPos(parent.mapToGlobal(QPoint()), parent.size()) + self._tooltip.adjustPos(parent, self.position) self._tooltip.show() def setToolTipDelay(self, delay: int): -- GitLab