Question

I'm doing an app with in GUI written with PySide. I set a QMenu on a QPushButton, added several QActions via QMenu.addAction. To further explain these actions to the user I added QToolTip's to these with QAction.setToolTip.

When I run the GUI now my QToolTip won't show. The example posted below reproduces the same issue, any ideas?

Thanks in advance

import sys
from PySide import QtGui

class Example(QtGui.QPushButton):

    def __init__(self, parent = None):
        super(Example, self).__init__(parent)

        self.setText('TestMenu')
        self.setToolTip('This is a Test Button')

        menu = QtGui.QMenu(self)
        action_1 = menu.addAction('Action1')
        action_1.setToolTip('This is action 1')
        action_2 = menu.addAction('Action2')
        action_2.setToolTip('This is action 2')
        action_3 = menu.addAction('Action3')
        action_3.setToolTip('This is action 3')
        action_4 = menu.addAction('Action4')
        action_4.setToolTip('This is action 4')

        self.setMenu(menu)
        self.show()

def main():
    app = QtGui.QApplication(sys.argv)
    ex = Example()

    app.exec_()

if __name__ == '__main__':
    main()
Was it helpful?

Solution

In Qt-5.1 or later, you can simply use QMenu.setToolTipsVisible, and the menu items will show their tooltips as expected (see QTBUG-13663):

    menu.setToolTipsVisible(True)

However, for Qt-4.* and Qt-5.0, the situation is different. If an action is added to a toolbar, its tooltip will be shown; but if the same action is added to a QMenu, it won't be, and there is no built-in API to change that. There are a couple of ways to work around this. One is to use status tips instead, which will show the menu-item information in the status-bar. The other is to implement the menu-item tooltip functionality yourself using the QMenu.hovered signal and QToolTip.showText:

        self.menu = QtGui.QMenu(self)
        ...
        self.menu.hovered.connect(self.handleMenuHovered)

    def handleMenuHovered(self, action):
        QtGui.QToolTip.showText(
            QtGui.QCursor.pos(), action.toolTip(),
            self.menu, self.menu.actionGeometry(action))

OTHER TIPS

Actually you don't have to do any workaround to display your tooltip, since Qt 5.1, you can use QMenu's property toolTipsVisible, which is by default set to false.

See the related Qt suggestion.

With ekhumoro helping me on the way got to this solution. It's probably not the most beautiful thing, and the code below positions the menu and the tool tips somewhat arkward, but in my actual programm it looks quite neat.

import sys
from PySide import QtGui, QtCore

class Example(QtGui.QPushButton):

    def __init__(self, parent = None):
        super(Example, self).__init__(parent)

        self.setText('TestMenu')
        self.setToolTip('This is a Test Button')

        menu = QtGui.QMenu(self)
        action_1 = menu.addAction('Action1')
        action_1.setToolTip('This is action 1')
        action_2 = menu.addAction('Action2')
        action_2.setToolTip('This is action 2')
        action_3 = menu.addAction('Action3')
        action_3.setToolTip('This is action 3')
        action_4 = menu.addAction('Action4')
        action_4.setToolTip('This is action 4')

        action_1.hovered.connect(lambda pos = [self], parent = action_1, index = 0: show_toolTip(pos, parent, index))
        action_2.hovered.connect(lambda pos = [self], parent = action_2, index = 1: show_toolTip(pos, parent, index))
        action_3.hovered.connect(lambda pos = [self], parent = action_3, index = 2: show_toolTip(pos, parent, index))
        action_4.hovered.connect(lambda pos = [self], parent = action_4, index = 3: show_toolTip(pos, parent, index))

        self.setMenu(menu)
        self.show()

def show_toolTip(pos, parent, index):
    '''
    **Parameters**
        pos:    list
            list of all parent widget up to the upmost

        parent: PySide.QtGui.QAction
            the parent QAction

        index:  int
            place within the QMenu, beginning with zero
    '''
    position_x = 0
    position_y = 0
    for widget in pos:
        position_x += widget.pos().x()
        position_y += widget.pos().y()

    point = QtCore.QPoint()
    point.setX(position_x)
    point.setY(position_y + index * 22) # set y Position of QToolTip

    QtGui.QToolTip.showText(point, parent.toolTip())

def main():
    app = QtGui.QApplication(sys.argv)
    ex = Example()

    app.exec_()

if __name__ == '__main__':
    main()

I have to say I'm not perfectly happy with this, mainly because the show_toolTip function has to be global, for the lambda operator didn't recognize it when I had it in the class (self.show_toolTip). I'm still open for suggesetions if someone has a suggestion.

Instead of showing tooltip immediately one can just update the tooltip of the parent (menu) when hovering and wait for the tooltip to be shown! Therefore:

    menu = QtGui.QMenu(self)
    action_1 = menu.addAction('Action1')
    action_1.setToolTip('This is action 1')
    ...
    menu.hovered.connect(self.handleMenuHovered)

def handleMenuHovered(self, action):
    action.parent().setToolTip(action.toolTip())
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top