[PyQt] Suppress departure from cell in QTableView

John F Sturtz john at sturtz.org
Sat Feb 23 18:01:40 GMT 2019

Thanks again Maurizio!  This is very cool.

I played around with using QLineEdit, and that works quite well for most of the columns in my view.  The nice thing about that is that I can use a QRegExpValidator for validation, which is pretty slick.

Actually, the reason I was wanting to do special text formatting in some of the columns is to implement auto-completion (basically, what the user has typed in so far shows in one style, and the auto-completed portion in another).  It turns out using a QLineEdit, I can use a QCompleter for this.  I played around with that too, and it works pretty well.

On the other hand, I could implement my own auto-completion and use QTextEdit as you have done.  It so happens that the columns in my view that will have auto-completion don't have much validation, so I wouldn't really miss the QRegExpValidator for those cells.

Thanks again!  You've given me lots of great ideas.


Since you need inline text formatting, implementing QLineEdit can be an issue as you'd probably need to do all the painting by yourself.

I'd suggest another approach instead: use a QTextEdit. It's implementation is a bit harder to get, but allows a better way to do what you need, while giving the user a standard and much more comfortable text editing UX.

Here's a small example I made which also uses QSyntaxHighlighter to do the formatting (in this case, it makes all occurrences of the word "red" in italic red).
It doesn't count in the "unallowed" text input you wrote about, but that can be done inside the keyPressEvent() method (but the release could need some checking too); you could also validate the input by connecting the textChanged() signal, to inhibit invalid clipboard pasting (like new line characters).

class MyHighlighter(QtGui.QSyntaxHighlighter):
    def __init__(self, *args, **kwargs):
        QtGui.QSyntaxHighlighter.__init__(self, *args, **kwargs)
        self.myFormat = QtGui.QTextCharFormat()
        self.re [http://self.re] = re.compile(r'(red)+')

    def highlightBlock(self, text):
        for match in self.re.finditer(text):
            self.setFormat(match.start(), match.end() - match.start(), self.myFormat)

class MyEditor(QtWidgets.QTextEdit):
    submit = QtCore.pyqtSignal()

    def __init__(self, *args, **kwargs):
        QtWidgets.QTextEdit.__init__(self, *args, **kwargs)

        self.highlighter = MyHighlighter(self.document())

    def keyPressEvent(self, event):
        if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
        elif event.key() == QtCore.Qt.Key_Tab:
            # Ignore tab by letting the parent(s) handle it, as you are 
            # probably not interested in the tab character.
            # Since you also want to prevent leaving the cell editing 
            # if the content is not valid, here you can also choose to
            # accept the event, inhibiting cell departure.
            # Please note that the shift-tab is intercepted by the view.
            QtWidgets.QTextEdit.keyPressEvent(self, event)

class MyDelegate(QtWidgets.QStyledItemDelegate):
    def createEditor(self, parent, option, index):
        editor = MyEditor(parent)
        editor.submit.connect(lambda: self.submit(editor, index))
        return editor

    def submit(self, editor, index):
        # You can also check for contents before proceeding with this
        self.setModelData(editor, index.model(), index)
        self.closeEditor.emit(editor, self.EditNextItem)

    def setModelData(self, editor, model, index):
        # This is required, otherwise you'll get the rich text contents as XHTML
        model.setData(index, editor.toPlainText(), QtCore.Qt.DisplayRole)

Now you only have to setItemDelegate() on your view, and eventually set the highlighting [validation] by checking the index.column() argument in the createEditor() method according to your needs (or just create different delegates for each column).


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20190223/0c5cb8fd/attachment.html>

More information about the PyQt mailing list