Skip to content

Instantly share code, notes, and snippets.

@ian-weisser
Created April 7, 2014 00:33
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ian-weisser/10013110 to your computer and use it in GitHub Desktop.
Save ian-weisser/10013110 to your computer and use it in GitHub Desktop.
Python3 tkinter text entry widget with built-in autocompletion class,, with working example
#!/usr/bin/python3
"""
tkentrycomplete.py
A tkinter widget that features autocompletion.
Created by Mitja Martini on 2008-11-29.
Converted to Python3 by Ian weisser on 2014-04-06.
"""
import sys
import os
import tkinter
__version__ = "1.0"
tkinter_umlauts=['odiaeresis', 'adiaeresis', 'udiaeresis', 'Odiaeresis', 'Adiaeresis', 'Udiaeresis', 'ssharp']
class AutocompleteEntry(tkinter.Entry):
"""
Subclass of tkinter.Entry that features autocompletion.
To enable autocompletion use set_completion_list(list) to define
a list of possible strings to hit.
To cycle through hits use down and up arrow keys.
"""
def set_completion_list(self, completion_list):
self._completion_list = completion_list
self._hits = []
self._hit_index = 0
self.position = 0
self.bind('<KeyRelease>', self.handle_keyrelease)
def autocomplete(self, delta=0):
"""autocomplete the Entry, delta may be 0/1/-1 to cycle through possible hits"""
if delta: # need to delete selection otherwise we would fix the current position
self.delete(self.position, tkinter.END)
else: # set position to end so selection starts where textentry ended
self.position = len(self.get())
# collect hits
_hits = []
for element in self._completion_list:
if element.startswith(self.get().lower()):
_hits.append(element)
# if we have a new hit list, keep this in mind
if _hits != self._hits:
self._hit_index = 0
self._hits=_hits
# only allow cycling if we are in a known hit list
if _hits == self._hits and self._hits:
self._hit_index = (self._hit_index + delta) % len(self._hits)
# now finally perform the auto completion
if self._hits:
self.delete(0,tkinter.END)
self.insert(0,self._hits[self._hit_index])
self.select_range(self.position,tkinter.END)
def handle_keyrelease(self, event):
"""event handler for the keyrelease event on this widget"""
if event.keysym == "BackSpace":
self.delete(self.index(tkinter.INSERT), tkinter.END)
self.position = self.index(tkinter.END)
if event.keysym == "Left":
if self.position < self.index(tkinter.END): # delete the selection
self.delete(self.position, tkinter.END)
else:
self.position = self.position-1 # delete one character
self.delete(self.position, tkinter.END)
if event.keysym == "Right":
self.position = self.index(tkinter.END) # go to end (no selection)
if event.keysym == "Down":
self.autocomplete(1) # cycle to next hit
if event.keysym == "Up":
self.autocomplete(-1) # cycle to previous hit
# perform normal autocomplete if event is a single key or an umlaut
if len(event.keysym) == 1 or event.keysym in tkinter_umlauts:
self.autocomplete()
def test(test_list):
"""Run a mini application to test the AutocompleteEntry Widget."""
root = tkinter.Tk(className=' AutocompleteEntry demo')
entry = AutocompleteEntry(root)
entry.set_completion_list(test_list)
entry.pack()
entry.focus_set()
root.mainloop()
if __name__ == '__main__':
test_list = (u'test', u'type', u'true', u'tree', u'tölz')
print("Type a 't' to test the AutocompleteEntry widget.")
print("Will use AutocompleteEntry.set_completion_list({})".format(test_list))
print("Try also the backspace key and the arrow keys.")
test(test_list)
@victordomingos
Copy link

Hi. I was looking for a tkinter widget like this and got really excited when I discovered this page. However, I am getting a strange behavior: if the list includes items that start with uppercase characters, or it won't work for those items. Only words that start by lowercase or numeric characters will be shown on the widget autocompletion. I am still trying to understand why this happens. Not sure if it was intentional, but it it was, it would be great to include a way to opt for "case-insensitiveness".

@victordomingos
Copy link

Digging a little more, I think I have found an answer here:
http://stackoverflow.com/questions/8942371/tkinter-autocomplete-widget-lowercase-bias

RobB @ StackOverflow suggested this:

Try removing the

.lower()
from

if element.startswith(self.get().lower()):
or to make the match case-insensitive:

if element.lower().startswith(self.get().lower()):
which will convert your entry string into lowercase and then the list values to lowercase as well so that a match will be made anytime the same letters are entered even if the case is off.

For me, it seems to be working. I get my previous database entry regardless of case :)

@victordomingos
Copy link

For the kind of usage I was looking for (adding a new item to a SQLite database while getting autocompletion from previous records), there was also something weird about pressing Left key or clicking somewhere in the text and a lot of characters getting deleted. So, for me, this works better:

        def handle_keyrelease(self, event):
        """event handler for the keyrelease event on this widget"""
        if event.keysym == "BackSpace":
            if self.position < self.index(END): # delete the selection
                self.delete(self.position, END)
            else:
                #self.delete(self.index(INSERT), END) 
                self.position = self.index(END)
        if event.keysym == "Left":
            if self.position < self.index(END): # delete the selection
                self.delete(self.position, END)
            #else:
                #self.position = self.position-1 # delete one character
                #self.delete(self.position, END)

I added an IF clause to the "backspace" key part and I removed the above commented lines in order to allow the user to go back and correct some misspelling. Now if the user goes back and edits something, the rest of the previously entered text will be intact and the text only gets deleted if the user presses (deletes only the remaining autocompletion but leaves the rest intact) or backspace (deletes autocompletion and one character at a time, as expected). If the user mouse-clicks in the text, nothing is deleted.

@Sapython
Copy link

Sapython commented Jan 25, 2019

#Help
##Need it same a son tkinter text widget
Can anyone help me, I want same it on a tkinter text widget . It awesome I need it because I am making a text editor.

@Gepsonka
Copy link

Thank you bro ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment