PyQt4: combine textChanged and editingFinished for QLineEdit

Question:

Is there a way to combine textChanged and editingFinished for QLineEdit? The problem is that editingFinished is emitted even if I only move the cursor away from QLineEdit without any changes. Whereas I want to emit a signal only when any changes were performed after I finished editing.

I can imagine only to store somewhere the current text, compare the entered text with it and do something only if it differs. But I wonder if there is any solution purely based on signals handling.

EDIT: At the end I had to store the current text and compare with the new text and not follow the proposed solution. I realized that in my application “1.2345” and “1.23” would be the same text but nevertheless I have to update some other values in this case and so on. I really appreciate detailed answer and comments by @Avaris and @ekhumoro, and will accept it since it seems to solve originally posted problem.

Asked By: Ekaterina Mishina

||

Answers:

Edit

For capturing manual edits:

class MyLineEdit(QtGui.QLineEdit):
    textModified = QtCore.pyqtSignal(str, str) # (before, after)

    def __init__(self, contents='', parent=None):
        super(MyLineEdit, self).__init__(contents, parent)
        self.returnPressed.connect(self.checkText)
        self._before = contents

    def focusInEvent(self, event):
        if event.reason() != QtCore.Qt.PopupFocusReason:
            self._before = self.text()
        super(MyLineEdit, self).focusInEvent(event)

    def focusOutEvent(self, event):
        if event.reason() != QtCore.Qt.PopupFocusReason:
            self.checkText()
        super(MyLineEdit, self).focusOutEvent(event)

    def checkText(self):
        if self._before != self.text():
            self._before = self.text()
            self.textModified.emit(self._before, self.text())

Edit 2

For capturing all edits (programmatic and manual):

class MyLineEdit(QtGui.QLineEdit):
    textModified = QtCore.pyqtSignal(str, str) # (before, after)

    def __init__(self, contents='', parent=None):
        super(MyLineEdit, self).__init__(contents, parent)
        self.editingFinished.connect(self.checkText)
        self.textChanged.connect(lambda: self.checkText())
        self.returnPressed.connect(lambda: self.checkText(True))
        self._before = contents

    def checkText(self, _return=False):
        if (not self.hasFocus() or _return) and self._before != self.text():
            self._before = self.text()
            self.textModified.emit(self._before, self.text())

Edit 3

For capturing only text changes by the user:

class MyLineEdit(QtGui.QLineEdit):
    textModified = QtCore.pyqtSignal(str, str) # (before, after)

    def __init__(self, contents='', parent=None):
        super(MyLineEdit, self).__init__(contents, parent)
        self.editingFinished.connect(self.__handleEditingFinished)
        self.textChanged.connect(self.__handleTextChanged)
        self._before = contents

    def __handleTextChanged(self, text):
        if not self.hasFocus():
            self._before = text

    def __handleEditingFinished(self):
        before, after = self._before, self.text()
        if before != after:
            self._before = after
            self.textModified.emit(before, after)
Answered By: Avaris

If you just want to detect whether any changes have been made (as opposed to whether the text is different from how it started), you can use the modified property of the QLineEdit with the editingFinished signal:

    self.edit = QtGui.QLineEdit(self)
    self.edit.editingFinished.connect(self.handleEditingFinished)
    ...

def handleEditingFinished(self):
    if self.edit.isModified():
        # do interesting stuff ...
        print 'Editing Finished'
    self.edit.setModified(False)
Answered By: ekhumoro