Finally found a solution. I used a QTextEdit, it does not work with QLineEdit, I don't know why. I still have a small problem, I can't close the completion popup when I hit the return key, so I must hit return key, twice. Not a big problem.
import sys
from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant, pyqtSignal
from PyQt5.QtGui import QTextCursor, QTextOption
from PyQt5.QtWidgets import (QAbstractItemDelegate, QApplication, QCompleter,
QItemDelegate, QMainWindow, QTableView, QTextEdit)
class MyLineEdit(QTextEdit):
returnPressed = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.setAcceptRichText(False)
self.setWordWrapMode(QTextOption.NoWrap)
self.setUndoRedoEnabled(False)
self.setTabChangesFocus(True)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.completer = None
self.textChanged.connect(self.textHasChanged)
def setCompleter(self, completer):
if completer:
completer.setWidget(self)
completer.setCompletionMode(QCompleter.PopupCompletion)
completer.setCaseSensitivity(Qt.CaseInsensitive)
completer.setModelSorting(
QCompleter.CaseSensitivelySortedModel)
completer.setMaxVisibleItems(15)
completer.activated.connect(self.insertCompletion)
self.completer = completer
def insertCompletion(self, completion):
print('>>> insertCompletion')
if self.completer and self.completer.widget() == self:
self.completer.widget().setPlainText(completion)
self.completer.widget().moveCursor(QTextCursor.EndOfLine)
self.completer.widget().ensureCursorVisible()
def focusInEvent(self, event):
print('>>> focusInEvent')
if self.completer:
self.completer.setWidget(self)
super().focusInEvent(event)
def keyPressEvent(self, event):
print('>>> keyPressEvent')
if self.completer and self.completer.popup().isVisible():
if event.key() in (Qt.Key_Return, Qt.Key_Enter,
Qt.Key_Tab, Qt.Key_Backtab, Qt.Key_Escape):
event.ignore()
return
else:
if event.key() in(Qt.Key_Return, Qt.Key_Enter):
self.returnPressed.emit()
return
super().keyPressEvent(event)
if not self.toPlainText():
self.completer.popup().hide()
return
self.completer.setCompletionPrefix(self.toPlainText())
self.completer.popup().setCurrentIndex(
self.completer.completionModel().index(0, 0))
self.completer.complete()
def textHasChanged(self):
# remove new lines and strip left blank characters
self.blockSignals(True)
cursor = self.textCursor()
self.setPlainText(' '.join(self.toPlainText().splitlines()).lstrip())
self.setTextCursor(cursor)
self.ensureCursorVisible()
self.blockSignals(False)
class MyDelegate(QItemDelegate):
def __init__(self, parent):
super().__init__(parent)
def createEditor(self, parent, option, index):
strings = ('tata', 'tada', 'tadam', 'tete', 'titi', 'toto', 'tutu')
completer = QCompleter(strings)
editor = MyLineEdit(parent)
editor.setCompleter(completer)
editor.returnPressed.connect(self.commitAndCloseEditor)
return editor
def commitAndCloseEditor(self):
print('>>> commitAndCloseEditor')
editor = self.sender()
self.commitData.emit(editor)
self.closeEditor.emit(editor)
def setEditorData(self, editor, index):
if editor:
editor.setText(index.model().data[0])
editor.selectAll()
def setModelData(self, editor, model, index):
if editor:
model.setData(index, editor.toPlainText(), Qt.EditRole)
class Model(QAbstractTableModel):
def __init__(self):
super().__init__()
self.data = ['hello']
def rowCount(self, parent=None):
return 1
def columnCount(self, parent=None):
return 1
def data(self, index, role):
if not index.isValid():
return QVariant()
if role in (Qt.DisplayRole, Qt.EditRole):
return self.data[0]
return QVariant()
def setData(self, index, value, role):
if role == Qt.EditRole:
self.data[0] = value
top_left = self.index(0, 0)
bottom_right = self.index(
self.rowCount() + 1, self.columnCount())
self.dataChanged.emit(top_left, bottom_right,
[Qt.DisplayRole])
return True
return False
def flags(self, index):
return Qt.ItemIsEditable | super().flags(index)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.model = Model()
self.table = QTableView()
self.table.setModel(self.model)
delegate = MyDelegate(self.table)
self.table.setItemDelegateForColumn(0, delegate)
def initUI(self):
self.show()
self.setCentralWidget(self.table)
if __name__ == '__main__':
app = QApplication(sys.argv)
mw = MainWindow()
mw.initUI()
sys.exit(app.exec_())