Skip to content

Instantly share code, notes, and snippets.

@uroshekic
Last active March 22, 2023 07:18
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save uroshekic/11078820 to your computer and use it in GitHub Desktop.
Save uroshekic/11078820 to your computer and use it in GitHub Desktop.
Tkinter - Autocomplete Entry Field
"""
Inspired by http://code.activestate.com/recipes/578253-an-entry-with-autocompletion-for-the-tkinter-gui/
Changes:
- Fixed AttributeError: 'AutocompleteEntry' object has no attribute 'listbox'
- Fixed scrolling listbox
- Case-insensitive search
- Added focus to entry field
- Custom listbox length, listbox width matches entry field width
- Custom matches function
"""
from tkinter import *
import re
class AutocompleteEntry(Entry):
def __init__(self, autocompleteList, *args, **kwargs):
# Listbox length
if 'listboxLength' in kwargs:
self.listboxLength = kwargs['listboxLength']
del kwargs['listboxLength']
else:
self.listboxLength = 8
# Custom matches function
if 'matchesFunction' in kwargs:
self.matchesFunction = kwargs['matchesFunction']
del kwargs['matchesFunction']
else:
def matches(fieldValue, acListEntry):
pattern = re.compile('.*' + re.escape(fieldValue) + '.*', re.IGNORECASE)
return re.match(pattern, acListEntry)
self.matchesFunction = matches
Entry.__init__(self, *args, **kwargs)
self.focus()
self.autocompleteList = autocompleteList
self.var = self["textvariable"]
if self.var == '':
self.var = self["textvariable"] = StringVar()
self.var.trace('w', self.changed)
self.bind("<Right>", self.selection)
self.bind("<Up>", self.moveUp)
self.bind("<Down>", self.moveDown)
self.listboxUp = False
def changed(self, name, index, mode):
if self.var.get() == '':
if self.listboxUp:
self.listbox.destroy()
self.listboxUp = False
else:
words = self.comparison()
if words:
if not self.listboxUp:
self.listbox = Listbox(width=self["width"], height=self.listboxLength)
self.listbox.bind("<Button-1>", self.selection)
self.listbox.bind("<Right>", self.selection)
self.listbox.place(x=self.winfo_x(), y=self.winfo_y() + self.winfo_height())
self.listboxUp = True
self.listbox.delete(0, END)
for w in words:
self.listbox.insert(END,w)
else:
if self.listboxUp:
self.listbox.destroy()
self.listboxUp = False
def selection(self, event):
if self.listboxUp:
self.var.set(self.listbox.get(ACTIVE))
self.listbox.destroy()
self.listboxUp = False
self.icursor(END)
def moveUp(self, event):
if self.listboxUp:
if self.listbox.curselection() == ():
index = '0'
else:
index = self.listbox.curselection()[0]
if index != '0':
self.listbox.selection_clear(first=index)
index = str(int(index) - 1)
self.listbox.see(index) # Scroll!
self.listbox.selection_set(first=index)
self.listbox.activate(index)
def moveDown(self, event):
if self.listboxUp:
if self.listbox.curselection() == ():
index = '0'
else:
index = self.listbox.curselection()[0]
if index != END:
self.listbox.selection_clear(first=index)
index = str(int(index) + 1)
self.listbox.see(index) # Scroll!
self.listbox.selection_set(first=index)
self.listbox.activate(index)
def comparison(self):
return [ w for w in self.autocompleteList if self.matchesFunction(self.var.get(), w) ]
if __name__ == '__main__':
autocompleteList = [ 'Dora Lyons (7714)', 'Hannah Golden (6010)', 'Walker Burns (9390)', 'Dieter Pearson (6347)', 'Allen Sullivan (9781)', 'Warren Sullivan (3094)', 'Genevieve Mayo (8427)', 'Igor Conner (4740)', 'Ulysses Shepherd (8116)', 'Imogene Bullock (6736)', 'Dominique Sanchez (949)', 'Sean Robinson (3784)', 'Diana Greer (2385)', 'Arsenio Conrad (2891)', 'Sophia Rowland (5713)', 'Garrett Lindsay (5760)', 'Lacy Henry (4350)', 'Tanek Conley (9054)', 'Octavia Michael (5040)', 'Kimberly Chan (1989)', 'Melodie Wooten (7753)', 'Winter Beard (3896)', 'Callum Schultz (7762)', 'Prescott Silva (3736)', 'Adena Crane (6684)', 'Ocean Schroeder (2354)', 'Aspen Blevins (8588)', 'Allegra Gould (7323)', 'Penelope Aguirre (7639)', 'Deanna Norman (1963)', 'Herman Mcintosh (1776)', 'August Hansen (547)', 'Oscar Sanford (2333)', 'Guy Vincent (1656)', 'Indigo Frye (3236)', 'Angelica Vargas (1697)', 'Bevis Blair (4354)', 'Trevor Wilkinson (7067)', 'Kameko Lloyd (2660)', 'Giselle Gaines (9103)', 'Phyllis Bowers (6661)', 'Patrick Rowe (2615)', 'Cheyenne Manning (1743)', 'Jolie Carney (6741)', 'Joel Faulkner (6224)', 'Anika Bennett (9298)', 'Clayton Cherry (3687)', 'Shellie Stevenson (6100)', 'Marah Odonnell (3115)', 'Quintessa Wallace (5241)', 'Jayme Ramsey (8337)', 'Kyle Collier (8284)', 'Jameson Doyle (9258)', 'Rigel Blake (2124)', 'Joan Smith (3633)', 'Autumn Osborne (5180)', 'Renee Randolph (3100)', 'Fallon England (6976)', 'Fallon Jefferson (6807)', 'Kevyn Koch (9429)', 'Paki Mckay (504)', 'Connor Pitts (1966)', 'Rebecca Coffey (4975)', 'Jordan Morrow (1772)', 'Teegan Snider (5808)', 'Tatyana Cunningham (7691)', 'Owen Holloway (6814)', 'Desiree Delaney (272)', 'Armand Snider (8511)', 'Wallace Molina (4302)', 'Amela Walker (1637)', 'Denton Tillman (201)', 'Bruno Acevedo (7684)', 'Slade Hebert (5945)', 'Elmo Watkins (9282)', 'Oleg Copeland (8013)', 'Vladimir Taylor (3846)', 'Sierra Coffey (7052)', 'Holmes Scott (8907)', 'Evelyn Charles (8528)', 'Steel Cooke (5173)', 'Roth Barrett (7977)', 'Justina Slater (3865)', 'Mara Andrews (3113)', 'Ulla Skinner (9342)', 'Reece Lawrence (6074)', 'Violet Clay (6516)', 'Ainsley Mcintyre (6610)', 'Chanda Pugh (9853)', 'Brody Rosales (2662)', 'Serena Rivas (7156)', 'Henry Lang (4439)', 'Clark Olson (636)', 'Tashya Cotton (5795)', 'Kim Matthews (2774)', 'Leilani Good (5360)', 'Deirdre Lindsey (5829)', 'Macy Fields (268)', 'Daniel Parrish (1166)', 'Talon Winters (8469)' ]
def matches(fieldValue, acListEntry):
pattern = re.compile(re.escape(fieldValue) + '.*', re.IGNORECASE)
return re.match(pattern, acListEntry)
root = Tk()
entry = AutocompleteEntry(autocompleteList, root, listboxLength=6, width=32, matchesFunction=matches)
entry.grid(row=0, column=0)
Button(text='Python').grid(column=0)
Button(text='Tkinter').grid(column=0)
Button(text='Regular Expressions').grid(column=0)
Button(text='Fixed bugs').grid(column=0)
Button(text='New features').grid(column=0)
Button(text='Check code comments').grid(column=0)
root.mainloop()
@OriginalName75
Copy link

Thanks !

@LightHouse6
Copy link

It's a big help, thanks.

Condense five lines of code into one:
self.listboxLength = kwargs.pop('listboxLength', 8)

@cardwizard
Copy link

Is there a way to pop the list out of the box? This way, we wouldn't need the extra buttons.

@ikerib
Copy link

ikerib commented Sep 7, 2017

Thanks for this code!! but is it working for you when you select a item with a mouse?

Copy link

ghost commented Oct 27, 2017

+1 rep

@NathanHoekstra
Copy link

When applying the function to a tkinter Toplevel window, the listbox is showing up on the 'main' window. Do you have a fix for that?

@SENOMOY
Copy link

SENOMOY commented Jun 27, 2018

How can I click on Python button and print the selected string from the Listbox?

@hackerpoint
Copy link

this code dosn't work with Top Level window

@Sapython
Copy link

Sapython commented Mar 6, 2019

How can I do this for a text widget. Instead of Entry widget.

@ricardj
Copy link

ricardj commented Jun 17, 2019

Thanks for the code! Quite useful.
I've forked It in order to enhance some features (like adding up and down cyclic movement through the list) and to adapt It to my needs.

@Kanishka00
Copy link

It's nice Please Can you explain how to use this In a Toplevel window;

@PerfecXX
Copy link

Thank this guy so much.
Your code is very useful and can be work with my project

@mrodas1994
Copy link

It is awesome

@samuelkazeem
Copy link

To use with either root or toplevel, do check my for of this https://gist.github.com/samuelkazeem/f3e7780de569f0cba5fd3eab469749a4

@mrodas1994
Copy link

I introduced this code into a search product of a POS Software.

@noormoghul
Copy link

Thank you.

@kotobi
Copy link

kotobi commented Oct 15, 2020

Thank you man!

@Michael-Evergreen
Copy link

It has some weird bug with ttk.Style apparently. The list got separated to another window and can't use search

@Anirbansingha1
Copy link

will you tell me how to read the autocompleted data?

@Parvat-R
Copy link

Parvat-R commented Jan 8, 2021

Wow, just saved my day!

Thanks

@ML1-W
Copy link

ML1-W commented Feb 23, 2021

Nice! How can I collect the value that match the list when we click on a button?

@AlbertoMQ
Copy link

Close. The auto suggested list pops up somewhere far away from the entry. Clicking the selection chooses the wrong value

@mikee47
Copy link

mikee47 commented Apr 3, 2021

Just what I was looking for to get started - saved me a ton of work! Thank you.

@CainGreythorne
Copy link

Saved me a lot of time, thanks

@arlem03
Copy link

arlem03 commented May 21, 2021

Is there a way to make the AutocompleteEntry read only so that only those items on the list can be used

@arlem03
Copy link

arlem03 commented May 21, 2021

Works fine. I pull the lists from SQLite. How do I make the list of item a bit bigger, or the font a bit larger?

@gmjr57
Copy link

gmjr57 commented Jun 5, 2021

will you tell me how to read the autocompleted data?

@akym00
Copy link

akym00 commented Oct 15, 2021

very very THANKS!!! God will bless you ~

@StarSein
Copy link

StarSein commented Apr 2, 2022

Thank you!!!

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