Why won't QToolTips appear on QActions within a QMenu
Asked Answered
L

4

14

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()
Ladd answered 12/2, 2014 at 10:25 Comment(0)
P
26

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))
Piazza answered 12/2, 2014 at 20:49 Comment(4)
Thanks, probably not the answer I was looking for ;) - but it surely helped me getting on the way!! I'll post my solution belowLadd
The next answer down (using setToolTipsVisible on the QMenu) is actually the correct answer for Qt 5.1+.Shu
@Wingware. The original question was about pyside, and hence qt4 only. However, I've now updated my answer to cover both qt4 and qt5.Piazza
setToolTipsVisible does not appear to help on the Mac. Perhaps the Mac native menus don't support that?Hula
P
9

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.

Predicative answered 24/11, 2016 at 15:51 Comment(1)
This really works and should be the accepted solution, you will have to make sure that this property is checked for every menu and sub menu that have in your application or the tooltips won't be shown for those menusSoot
L
2

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.

Ladd answered 13/2, 2014 at 9:33 Comment(1)
Not sure if you're still interested, but I've added a simpler solution to my answer.Piazza
D
2

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())
Doyen answered 17/11, 2016 at 9:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.