Home > Net >  How to show My Labels and activate respective Shortcut in Pyqt5?
How to show My Labels and activate respective Shortcut in Pyqt5?

Time:07-05

Here is my code, How to show My Labels and activate respective QShortcut?. Want to show my labels(both instances) and assign respective shortcut keys to labels and activate it.

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *

class Create_workingDict(QWidget):
    def __init__(self,lblname,lblscut):
        super(). __init__()

        self.lblname = lblname
        self.lblscut = lblscut

        lbl_1 = QLabel()
        lbl_1.setText(self.lblname)
        lbl_2 = QLabel()
        lbl_2.setText(self.lblscut)

        vbox = QVBoxLayout()
        vbox.addWidget(lbl_1)
        vbox.addWidget(lbl_2)
        self.setLayout(vbox)

        self.msgSc = QShortcut(QKeySequence(f'{self.lblscut}'), self)
        self.msgSc.activated.connect(lambda: QMessageBox.information(self,'Message', 'Ctrl   M initiated'))


class Mainclass(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Sample Window")
        x = Create_workingDict("Accounts","Alt A")
        y = Create_workingDict("Inventory", "Ctrl B")


def main():
    app = QApplication(sys.argv)
    ex = Mainclass()
    ex.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

CodePudding user response:

Shortcuts work based on the context (and visibility) of the parent widget: as long as the parent is visible and the context is compatible, they will be triggered, otherwise they will not even be considered.

Be aware that the visibility is of the parent widgets (note the plural) is mandatory, no matter of the context: Qt has no API for a "global" shortcut, not only external to the application, but also within it: there is no shortcut that can automatically work anywhere in the app if (any of) its parent(s) is hidden. The context only ensures that the shortcut can only be activated if the active widget is part of the application (ApplicationShortcut), if the current active window is an ancestor of the shortcut parent (WindowShortcut), if any of the grand[...]parent widgets has focus (WidgetWithChildrenShortcut) or the current parent has it (WidgetShortcut).

Long story short: if the shortcut's parent is not visible (at any level), it will not be triggered.

Not only. In your code, both x and y are potentially garbage collected (they are not due to the fact that the lambda scope avoids destruction, but that's just "sheer dumb luck"), so that code would be actually prone to fail anyway if the activated signal would have been connected to an instance method.

If you want them to be available to the visible window, you must add their parent widgets to that window, even if they're not shown. Otherwise, just add the shortcuts to that window.

For instance:

class Mainclass(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Sample Window")
        x = Create_workingDict("Accounts","Alt A")
        y = Create_workingDict("Inventory", "Ctrl B")
        layout = QVBoxLayout(self)
        layout.addWidget(x)
        layout.addWidget(y)

The only and automatic way to access a global application shortcut from any window of the application is to create a QKeySequence that is checked within an event filter installed on the application.

This is a possible, but crude implementation, so, take it as it is and consider its consequences:

class ShortCutFilter(QObject):
    triggered = pyqtSignal(QKeySequence)
    def __init__(self, shortcuts=None):
        super().__init__()
        self.shortcuts = {}

    def addShortcut(self, shortcut, slot=None):
        if isinstance(shortcut, str):
            shortcut = QKeySequence(shortcut)
        slots = self.shortcuts.get(shortcut)
        if not slots:
            self.shortcuts[shortcut] = slots = []
        if slot is not None:
            slots.append(slot)
        return shortcut

    def eventFilter(self, obj, event):
        if event.type() == event.KeyPress:
            keyCode = event.key()
            mods = event.modifiers()
            if mods & Qt.ShiftModifier:
                keyCode  = Qt.SHIFT
            if mods & Qt.MetaModifier:
                keyCode  = Qt.META
            if mods & Qt.ControlModifier:
                keyCode  = Qt.CTRL
            if mods & Qt.ALT:
                keyCode  = Qt.ALT
            for sc, slots in self.shortcuts.items():
                if sc == QKeySequence(keyCode):
                    self.triggered.emit(sc)
                    for slot in slots:
                        try:
                            slot()
                        except Exception as e:
                            print(type(e), e)
                    return True
        return super().eventFilter(obj, event)


def main():
    app = QApplication(sys.argv)
    shortcutFilter = ShortCutFilter()
    app.installEventFilter(shortcutFilter)

    shortcutFilter.addShortcut('alt b', lambda: 
        QMessageBox.information(None, 'Hello', 'How are you'))
    shortcutFilter.triggered.connect(lambda sc:
        print('triggered', sc.toString())

    ex = Mainclass()
    ex.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

This, obviously, means that any key press event will pass through the known python bottleneck. A better solution would be to create global QActions and addAction() to any possible top level window that could accept it.

While this approach might seem more complex, it has its benefits; for instance, you have more control on the context of the shortcut: in the case above, you could trigger Alt B from any window, including the one shown after previously triggering it, which is clearly not a good thing.

CodePudding user response:

Add the layout in main widget.

Then pass the layout to the function where you are creating labels and add them to layout.

Below is the modified code.

Find the details as comments in below code.

Your main window class

class Mainclass(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Sample Window")

        #CREATE THE LAYOUT HERE IN MAIN WINDOW
        vbox = QVBoxLayout()

        x = Create_workingDict("Accounts","Alt A",vbox) #PASS THE LAYOUT OBJECT (vbox) AS AN ARGUMENT
        y = Create_workingDict("Inventory", "Ctrl B",vbox) #PASS THE LAYOUT OBJECT (vbox) AS AN ARGUMENT

        #SET THE LAYOUT TO MAIN WINDOW
        self.setLayout(vbox)

Your function where labels created. Observe that the functions vbox = QVBoxLayout() and self.setLayout(vbox) are moved out of this function to main window.

class Create_workingDict(QWidget):
    def __init__(self,lblname,lblscut,vbox): #RECEIVE LAYOUT (vbox) ARGUMENT
        super(). __init__()

        self.lblname = lblname
        self.lblscut = lblscut

        lbl_1 = QLabel()
        lbl_1.setText(self.lblname)
        lbl_2 = QLabel()
        lbl_2.setText(self.lblscut)
       
        vbox.addWidget(lbl_1)
        vbox.addWidget(lbl_2)        

        self.msgSc = QShortcut(QKeySequence(f'{self.lblscut}'), self)
        self.msgSc.activated.connect(lambda: QMessageBox.information(self,'Message', 'Ctrl   M initiated'))
  • Related