QShortcut on QDesktopWidget disfunctional in PySide2



  • As I'm currently porting a gui application from Python 2.7.14/PySide 1.2.4 to Python 3.7.1/PySide2 5.11.2 I encountered a problem with the shortcut machinery. Basically:

    • Setting QShortcuts with Qt.ApplicationShortcut context on the QDesktopWidget seems disfunctional in PySide2 while working well in PySide.

    It seems like other people encountered the same issue too.

    • Is this a known bug?
    • Is there an elegant workaround for placing application wide shortcuts somewhere in the application without creating an invisible widget for this purpose?

    Python 3.7.1/PySide2 5.11.2 (not working)


    # -*- coding: utf-8 -*-
    """Test application wide shortcut on desktop widget fails in PySide2."""
    import logging
    from datetime import datetime
    
    from PySide2.QtCore import Qt
    from PySide2.QtGui import QKeySequence
    from PySide2.QtWidgets import QShortcut, QWidget, QApplication
    
    
    logging.basicConfig(level=logging.DEBUG)
    logger = logging.getLogger(__name__)
    
    
    class Widget(QWidget):
        """Widget with application wide shortcut."""
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            shortcut = QShortcut(QKeySequence(Qt.Key_H), QApplication.desktop())
            shortcut.setContext(Qt.ApplicationShortcut)
            shortcut.activated.connect(self.handler)
    
        def handler(self):
            """Shortcut does not trigger."""
            msg = f"{datetime.now()}"
            logger.info(msg)
    
    
    # run
    app = QApplication()
    widget = Widget()
    widget.show()
    app.exec_()
    

    Python 2.7.14/PySide 1.2.4 (working)


    # -*- coding: utf-8 -*-
    """Test application wide shortcut on desktop widget succeeds in PySide."""
    import logging
    from datetime import datetime
    
    from PySide.QtCore import Qt
    from PySide.QtGui import QKeySequence, QShortcut, QWidget, QApplication
    
    
    logging.basicConfig(level=logging.DEBUG)
    logger = logging.getLogger(__name__)
    
    
    class Widget(QWidget):
        """Widget with application wide shortcut."""
    
        def __init__(self, *args, **kwargs):
            super(Widget, self).__init__(*args, **kwargs)
    
            shortcut = QShortcut(QKeySequence(Qt.Key_H), QApplication.desktop())
            shortcut.setContext(Qt.ApplicationShortcut)
            shortcut.activated.connect(self.handler)
    
        def handler(self):
            """Shortcut does trigger."""
            msg = "{}".format(datetime.now())
            logger.info(msg)
    
    
    # run
    app = QApplication(tuple())
    widget = Widget()
    widget.show()
    app.exec_()
    

    Real use-case


    • I have an ApplicationHotkeyHandler which is a QObject that defines all the application wide shortcuts.
    • The handler functions on that instance are registered as application wide shortcuts on the QDesktopWidget, which comes as part of the Qt infrastructure and seems like an elegant and safe way around creating an invisible, artificial widget just for the purpose of holding application shortcuts.

    Here's the gist of what it looks like in the application:


    class ApplicationHotkeyHandler(QObject):
        """Define application wide hotkeys and their handlers here. Shortcuts are
        placed on QDesktopWidget which is an always available part of the Qt
        infrastructure.
        
        Notes:
            This is a QObject for signals and parenting but not a QWidget
            as we don't have to display anything, so the handler instance cannot 
            serve as parent for QShortcuts.
        """
    
        @hotkey(QKeySequence(Qt.CTRL + Qt.Key_Q), context=Qt.ApplicationShortcut,)
        def quit(self):
            QApplication.instance().quit()
    
        ...


Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.