Search code examples
pythonpyqtpyqt4signals-slotsqlineedit

PyQt4: combine textChanged and editingFinished for QLineEdit


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.


Solution

  • 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)