PyQt4: combine textChanged and editingFinished for QLineEdit

Katya picture Katya · Aug 29, 2012 · Viewed 17.1k times · Source

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.

Answer

Avaris picture Avaris · Aug 29, 2012

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)