PyQt5 QTextEdit auto completion

kokito picture kokito · Mar 10, 2015 · Viewed 7.4k times · Source

Looking for a way to have an auto completion with a QTextEdit and QCompleter. I have read that it is possible but didn't find any example... I'm using python3.4 and PyQt5

I'm looking for a very basic example thanks for any help

Answer

smitkpatel picture smitkpatel · Mar 11, 2015

an example here...that i've worked on... although it is in python3.3 and pyqt4. I guess it should not make much of a difference..
you will have to change from PyQt4 to from PyQt5

shortcut keys are Ctrl+Space to show suggestions and Ctrl+E to autocomplete the first avialable suggestion

mMyTextEdit.py

from PyQt4 import QtGui,QtCore
from mMyDictionaryCompleter import MyDictionaryCompleter
#===============================================================================
# MyTextEdit  
#===============================================================================
class MyTextEdit(QtGui.QTextEdit):
#|-----------------------------------------------------------------------------|
# class Variables
#|-----------------------------------------------------------------------------| 
    #no classVariables
#     myFocusOutSignal=QtCore.pyqtSignal(str)
#|-----------------------------------------------------------------------------|
# Constructor  
#|-----------------------------------------------------------------------------|
    def __init__(self,*args):
        #*args to set parent
        QtGui.QLineEdit.__init__(self,*args)
        font=QtGui.QFont()
        font.setPointSize(12)
        self.setFont(font)
        self.completer = None

#|--------------------------End of __init__------------------------------------|
#|-----------------------------------------------------------------------------| 
# setCompleter
#|-----------------------------------------------------------------------------|
    def setCompleter(self, completer):
        if self.completer:
            self.disconnect(self.completer, 0, self, 0)
        if not completer:
            return

        completer.setWidget(self)
        completer.setCompletionMode(QtGui.QCompleter.PopupCompletion)
        completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.completer = completer
#        self.connect(self.completer,
#            QtCore.SIGNAL("activated(const QString&)"), self.insertCompletion)
        self.completer.insertText.connect(self.insertCompletion)
#|-----------------------End of setCompleter-------------------------------------|
#|-----------------------------------------------------------------------------| 
# insertCompletion
#|-----------------------------------------------------------------------------|
    def insertCompletion(self, completion):
        tc = self.textCursor()
        extra = (len(completion) -
            len(self.completer.completionPrefix()))
        tc.movePosition(QtGui.QTextCursor.Left)
        tc.movePosition(QtGui.QTextCursor.EndOfWord)
        tc.insertText(completion[-extra:])
        self.setTextCursor(tc)
#|-----------------------End of insertCompletion-------------------------------|
#|-----------------------------------------------------------------------------| 
# textUnderCursor
#|-----------------------------------------------------------------------------|
    def textUnderCursor(self):
        tc = self.textCursor()
        tc.select(QtGui.QTextCursor.WordUnderCursor)
        return tc.selectedText()
#|-----------------------End of textUnderCursor--------------------------------|
#|-----------------------------------------------------------------------------| 
# focusInEvent
#|-----------------------------------------------------------------------------|
    #---override
    def focusInEvent(self, event):
        if self.completer:
            self.completer.setWidget(self);
        QtGui.QTextEdit.focusInEvent(self, event)
#|-----------------------End of focusInEvent-------------------------------------|
#|-----------------------------------------------------------------------------| 
# keyPressEvent
#|-----------------------------------------------------------------------------|
    #---override
    def keyPressEvent(self, event):
        if self.completer and self.completer.popup() and self.completer.popup().isVisible():
            if event.key() in (
            QtCore.Qt.Key_Enter,
            QtCore.Qt.Key_Return,
            QtCore.Qt.Key_Escape,
            QtCore.Qt.Key_Tab,
            QtCore.Qt.Key_Backtab):
                event.ignore()
                return
        ## has ctrl-Space been pressed??
        isShortcut = (event.modifiers() == QtCore.Qt.ControlModifier and\
                      event.key() == QtCore.Qt.Key_Space)
        ## modifier to complete suggestion inline ctrl-e
        inline = (event.modifiers() == QtCore.Qt.ControlModifier and \
                  event.key() == QtCore.Qt.Key_E)
        ## if inline completion has been chosen
        if inline:
            # set completion mode as inline
            self.completer.setCompletionMode(QtGui.QCompleter.InlineCompletion)
            completionPrefix = self.textUnderCursor()
            if (completionPrefix != self.completer.completionPrefix()):
                self.completer.setCompletionPrefix(completionPrefix)
            self.completer.complete()
#            self.completer.setCurrentRow(0)
#            self.completer.activated.emit(self.completer.currentCompletion())
            # set the current suggestion in the text box
            self.completer.insertText.emit(self.completer.currentCompletion())
            # reset the completion mode
            self.completer.setCompletionMode(QtGui.QCompleter.PopupCompletion)
            return
        if (not self.completer or not isShortcut):
            pass
            QtGui.QTextEdit.keyPressEvent(self, event)
        # debug
#        print("After controlspace")
#        print("isShortcut is: {}".format(isShortcut))
        # debug over
        ## ctrl or shift key on it's own??
        ctrlOrShift = event.modifiers() in (QtCore.Qt.ControlModifier ,\
                QtCore.Qt.ShiftModifier)
        if ctrlOrShift and event.text()== '':
#             ctrl or shift key on it's own
            return
        # debug
#        print("After on its own")
#        print("isShortcut is: {}".format(isShortcut))
        # debug over
#         eow = QtCore.QString("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=") #end of word
#        eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=" #end of word
        eow = "~!@#$%^&*+{}|:\"<>?,./;'[]\\-=" #end of word

        hasModifier = ((event.modifiers() != QtCore.Qt.NoModifier) and\
                        not ctrlOrShift)

        completionPrefix = self.textUnderCursor()
#         print('event . text = {}'.format(event.text().right(1)))
#         if (not isShortcut and (hasModifier or event.text()=='' or\
#                                 len(completionPrefix) < 3 or \
#                                 eow.contains(event.text().right(1)))):
        if not isShortcut :
            if self.completer.popup():
                self.completer.popup().hide()
            return
#        print("complPref: {}".format(completionPrefix))
#        print("completer.complPref: {}".format(self.completer.completionPrefix()))
#        print("mode: {}".format(self.completer.completionMode()))
#        if (completionPrefix != self.completer.completionPrefix()):
        self.completer.setCompletionPrefix(completionPrefix)
        popup = self.completer.popup()
        popup.setCurrentIndex(
            self.completer.completionModel().index(0,0))
        cr = self.cursorRect()
        cr.setWidth(self.completer.popup().sizeHintForColumn(0)
            + self.completer.popup().verticalScrollBar().sizeHint().width())
        self.completer.complete(cr) ## popup it up!
#|-----------------------End of keyPressEvent----------------------------------|

if __name__ == "__main__":

    app = QtGui.QApplication([])
    completer = MyDictionaryCompleter()
    te = MyTextEdit()
    te.setCompleter(completer)
    te.show()
    app.exec_()

mMyDictionaryCompleter.py

#===============================================================================
# MyDictionaryCompleter
#===============================================================================
from PyQt4 import QtGui, QtCore
class MyDictionaryCompleter(QtGui.QCompleter):
#|-----------------------------------------------------------------------------|
# class Variables
#|-----------------------------------------------------------------------------| 
    insertText = QtCore.pyqtSignal(str)
    #no classVariables
#|-----------------------------------------------------------------------------|
# Constructor  
#|-----------------------------------------------------------------------------|
    def __init__(self, myKeywords=None,parent=None):


        myKeywords =['apple','aggresive','ball','bat','cat','cycle','dog','dumb',\
                     'elephant','engineer','food','file','good','great',\
                     'hippopotamus','hyper','india','ireland','just','just',\
                     'key','kid','lemon','lead','mute','magic',\
                     'news','newyork','orange','oval','parrot','patriot',\
                     'question','queue','right','rest','smile','simple',\
                     'tree','urban','very','wood','xylophone','yellow',\
                     'zebra']
        QtGui.QCompleter.__init__(self, myKeywords, parent)
        self.connect(self,
            QtCore.SIGNAL("activated(const QString&)"), self.changeCompletion)
#|--------------------------End of Constructor---------------------------------| 
#|-----------------------------------------------------------------------------| 
# changeCompletion
#|-----------------------------------------------------------------------------|
    def changeCompletion(self, completion):
        if completion.find("(") != -1:
            completion = completion[:completion.find("(")]
        print(completion)
        self.insertText.emit(completion)
#|-----------------------End of changeCompletion-------------------------------|

EDIT

attached screenshots.
Character2nd Suggestion Selected2nd Suggestion Completed