5

This is most likely a duplicate question, but I have to ask it because other answers aren't helping in my case, since I am new to pyqt (switched from tkinter few days ago).

I am wondering if is it possible to connect to an event of a widget like this:

 self.lineEdit = QtGui.QLineEdit(self.frame)

 self.lineEdit.keyReleaseEvent(lambda: someFunction(QtCore.Qt.Key_A ))

 self.lineEdit.setObjectName(_fromUtf8("lineEdit"))

 self.horizontalLayout.addWidget(self.lineEdit)

and then...

def someFunction(event):
    print(event)
    ...

My question is how to bind to a specific event from another widget, and connect that event with a function - like btn.clicked.connect(function_goes_here).

In tkinter it's something be like this:

self.Entry.bind("<KeyRelease-a>", lambda event: someFunction(event))
ekhumoro
  • 107,367
  • 18
  • 208
  • 308

2 Answers2

6

There are a number of different ways to achieve this. A generic way to listen to all events for a given widget, is to install an event-filter on it. All protected functions have a corresponding event type that can be accessed in this way:

class MainmWindow(QMainWindow):
    def __init__(self):
        ...
        self.lineEdit = QLineEdit(self.frame)
        self.lineEdit.installEventFilter(self)

    def eventFilter(self, source, event):
        if source is self.lineEdit:
            if event.type() == QEvent.KeyRelease:
                print('key release:', event.key())
                # the following line will eat the key event
                # return True
        return super(MainmWindow, self).eventFilter(source, event)

Alternatively, you can sub-class the widget, re-implement the relevant event handler, and emit a custom signal:

class LineEdit(QLineEdit):
    keyReleased = pyqtSignal(int)

    def keyReleaseEvent(self, event):
        self.keyReleased.emit(event.key())
        super(LineEdit, self).keyReleaseEvent(event)

class MainmWindow(QMainWindow):
    def __init__(self):
        ...
        self.lineEdit = LineEdit(self.frame)
        self.lineEdit.keyReleased.connect(self.handleKeyRelease)

    def handleKeyRelease(self, key):
        print('key release:' key)

A more hackish variation on this is to overwrite the method directly:

class MainmWindow(QMainWindow):
    def __init__(self):
        ...
        self.lineEdit = QLineEdit(self.frame)
        self.lineEdit.keyReleaseEvent = self.handleKeyRelease

    def handleKeyRelease(self, event):
        print('key release:', event.key())
        QLineEdit.keyReleaseEvent(self.lineEdit, event)

Note that if you don't want to invoke the default event handling, you can omit the call to the base-class method.

ekhumoro
  • 107,367
  • 18
  • 208
  • 308
  • i like this hackish way :D , but i have one more question. Is it possible to bind two keys like " Ctrl + a ", or some other combination, because this key release event returns two key codes ,one for 'Ctrl' and other for 'a' ? Or there is another event intended to use instead of 'keyRelase', for this purpose – Dušan Atanacković Jul 29 '17 at 07:24
  • 1
    @DušanAtanacković. All you need is a simple if statement to check for any key combination you like: `if event.modifiers() == QtCore.Qt.ControlModifier and event.key() == QtCore.Qt.Key_A:`. – ekhumoro Jul 29 '17 at 13:44
  • I know that this question is not related with previous but i am wondering what OS you are using, since this questions that i am asking are related with program i am currently developing (for fun).The program i want to make is something like quick installer for Linux so i am wondering if you are interesting in that project, you can check it here if you want https://github.com/Dusan92Atanackovic/luqi.git regards – Dušan Atanacković Jul 30 '17 at 08:28
  • @DušanAtanacković. I use arch linux, where GUI installers aren't used very much. Good luck with your project. If you need any further coding help, just ask on SO. – ekhumoro Jul 30 '17 at 11:53
  • Sorry for asking to many questions, but i have found one strange behavior of QLineEdits (QLE). I have one QLE and a button next to it which calls some function, and when i press ENTER inside QLE that function is called even i have removed that KeyRelease handling, furthermore i have dynamically made inputs (QLE) bellow that one, and they also call that same function. – Dušan Atanacković Aug 08 '17 at 09:27
  • @DušanAtanacković. Please [ask a new question](https://stackoverflow.com/questions/ask), and make sure you show all the relevant code. – ekhumoro Aug 08 '17 at 13:54
  • I did, here's the link if you have time to take a look https://stackoverflow.com/questions/45584519/strange-behavior-of-qlineedit-widets-binding-button-function Thanks. – Dušan Atanacković Aug 09 '17 at 07:46
  • For me only the "hackish" way worked! But it's good. Thanks!! – ioaniatr Sep 03 '18 at 21:29
  • In the "hackish" way, if I have more than one QLineEdits that call's the same function, how can I know which one called the `handleKeyRelease()` function? (within the function) – ioaniatr Sep 03 '18 at 22:01
  • @ioaniatr Same as you would with any other method: `if self is whatever:`. But really, the other solutions are far superior to resorting to such crude hackery. – ekhumoro Sep 03 '18 at 22:43
  • With `self` you get the `QMainWindow`, not the lineEdit that triggered the event. – ioaniatr Sep 03 '18 at 22:52
  • @ioaniatr Of course you do - that's why it's a hack. You obviously need to create a [proper instance method](https://stackoverflow.com/q/972/984421) to make it work. But I don't really see the point when there are two better solutions available. – ekhumoro Sep 04 '18 at 17:28
0

override using python lambda magic

def on_key(line_edit : QLineEdit, key):
    print(key)

...

line_edit = self._lineedit = QLineEdit()
line_edit.keyReleaseEvent = lambda ev, self=line_edit, super=line_edit.keyReleaseEvent: \
                            (super(ev), on_key(self, ev.key()), None)[-1]
        

We save current keyReleaseEvent function in lambda kwarg super.

Also we need to save line_edit in lambda kwarg self, because line_edit will be removed from locals during lambda execution.

Finally we need to return None, thus get it from last element from our Tuple.

iperov
  • 337
  • 4
  • 8