Last month I started writing QTodoTxt, a PySide (Python Qt bindings) GUI for the todo.txt concept.
While using it for my own todo list I noticed several features that were missing (I intend to implement them all in due time), one of these features was auto-completion for projects and contexts when editing or creating a new task, something like this:
After some googling I found the built-in QCompleter component that can be easily attached to any QLineEdit control and allow easy auto-completion:
lineEdit = QtGui.QLineEdit()
completer = QtGui.QCompleter(['one', 'two', 'three', 'four'])
lineEdit.setCompleter(lineEdit)
However, this only allows auto-completing for the first word and I wanted to auto-completion for every word in the text. So I went back to google and found a post in the Qt developers forum that shows a simple implementation of this in C++.
Instead of using the QLineEdit's setCompleter
method (which wasn't available
for QTextEdit he was using), his implementation handles opening the completer
manually and just attaches the completer to the QLineEdit using the
QCompleter.setWidget
method.
To create my own auto-complete control I implemented his C++ control in python and added some extra features of my own. At first I created the AutoCompleteEdit class that inherits from QLineEdit and initializes the QCompleter:
from PySide import QtCore, QtGui
class AutoCompleteEdit(QtGui.QLineEdit):
def __init__(self, model, separator = ' ', \
addSpaceAfterCompleting = True):
super(AutoCompleteEdit, self).__init__()
self._separator = separator
self._addSpaceAfterCompleting = \
addSpaceAfterCompleting
self._completer = QtGui.QCompleter(model)
self._completer.setWidget(self)
self.connect(
self._completer,
QtCore.SIGNAL('activated(QString)'),
self._insertCompletion)
self._keysToIgnore = [QtCore.Qt.Key_Enter,
QtCore.Qt.Key_Return,
QtCore.Qt.Key_Escape,
QtCore.Qt.Key_Tab]</pre>
I overrode the keyPressEvent method of QLineEdit to handle the completion manually:
def keyPressEvent(self, event):
if self._completer.popup().isVisible():
if event.key() in self._keysToIgnore:
event.ignore()
return
super(AutoCompleteEdit, self).keyPressEvent(event)
completionPrefix = self.textUnderCursor()
if completionPrefix != self._completer.completionPrefix():
self._updateCompleterPopupItems(completionPrefix)
if len(event.text()) > 0 and len(completionPrefix) > 0:
self._completer.complete()
if len(completionPrefix) == 0:
self._completer.popup().hide()
This method performs the following tasks:
Thank you for reading,
You can see the full code in my Bitbucket repository
Please feel free to leave comments,
David.