Blair Zajac
Blair Zajac

Reputation: 4625

Modeless popup Qt ToolButton

We have a PyQt4/PySide Qt4 video player app that has QToolButton object in the GUI with an associated QMenu, added with QToolButton#setMenu(). Currently, when the user interacts with the menu, it takes over the event loop and video playback stops. The event loop takeover occurs in the private QToolButtonPrivate::popupTimerDone().

We'd like to rewrite/modify QToolButton to not be modeless, but there doesn't appear to be any easy choices:

  1. Copy and modify QToolButton in C++ and then use PyQt4 or PySide's C++ wrapping to wrap the class for Python. Easier, but now our build system needs to compile a C++ class and know which environment it's running in, PySide or PyQt4.
  2. Completely rewrite QToolButton in Python, so it would subclass QAbstractButton, and make the modifications. A decent amount of code to rewrite and maintain.
  3. In Python, subclass QToolButton and override where necessary. This seems nice, but looking at the internal state that QToolButtonPrivate::popupTimerDone() references, we would be mostly rewriting the entire thing anyway.

Are there any other ideas?

Upvotes: 2

Views: 887

Answers (1)

Eric Hulser
Eric Hulser

Reputation: 4022

I would recommend looking into using the QToolButton.clicked signal with the QMenu.popup method vs. the setMenu - that may break the modality.

I tried setting up an example for you - but it doesn't block the QMovie...so maybe you can use this example to test the different options for a video player vs. a Qmovie and see if it still blocks your event loop:

from PyQt4 import QtGui, QtCore

MOVIE_FILE = '/path/to/ajax_loader.gif'

class MyDialog(QtGui.QDialog):
    def __init__( self, parent = None ):
        super(MyDialog, self).__init__(parent)

        self._menu = QtGui.QMenu(self)
        self._menu.addAction('Action A')
        self._menu.addAction('Action B')

        self._menuButton     = QtGui.QToolButton(self)
        self._modalButton    = QtGui.QToolButton(self)
        self._nonModalButton = QtGui.QToolButton(self)
        self._feedbackLabel  = QtGui.QLabel(self)
        self._startTime      = QtCore.QDateTime.currentDateTime()

        self._menuButton.setPopupMode(self._menuButton.InstantPopup)

        movie = QtGui.QMovie(self)
        movie.setFileName(MOVIE_FILE)
        movie.start()

        self._feedbackLabel.setMovie(movie)

        hlayout = QtGui.QHBoxLayout()
        hlayout.addWidget(self._menuButton)
        hlayout.addWidget(self._modalButton)
        hlayout.addWidget(self._nonModalButton)
        hlayout.addStretch()

        vlayout = QtGui.QVBoxLayout()
        vlayout.addLayout(hlayout)
        vlayout.addWidget(self._feedbackLabel)

        self.setLayout(vlayout)
        self.adjustSize()

        # setup different menu examples
        self._menuButton.setMenu(self._menu)
        self._modalButton.clicked.connect(self.showModalMenu)
        self._nonModalButton.clicked.connect(self.showNonModalMenu)

        self._menu.triggered.connect(self.showAction)

    def showModalMenu( self ):
        point = self._modalButton.rect().bottomLeft()
        global_point = self._modalButton.mapToGlobal(point)
        self._menu.exec_(global_point)

    def showNonModalMenu( self ):
        point = self._nonModalButton.rect().bottomLeft()
        global_point = self._nonModalButton.mapToGlobal(point)
        self._menu.popup(global_point)

    def showAction( self, action ):
        print action.text()

if ( __name__ == '__main__' ):
    app = QtGui.QApplication([])
    dlg = MyDialog()
    dlg.show()
    app.exec_()

Upvotes: 1

Related Questions