We're back after a server migration that caused effbot.org to fall over a bit harder than expected. Expect some glitches.

Adding File Completion to the Entry Widget

May 19, 1998 | Fredrik Lundh

from Tkinter import *

import os

#
# completion entry baseclass.

class CompletionEntry(Entry):

    def __init__(self, master, **kw):

	apply(Entry.__init__, (self, master), kw)

	self.bind("<Tab>", self.__tab)

	self.__state = None, None, None

    def __tab(self, event):

	index = self.index(INSERT)
	value = self.get()
	prefix, suffix = value[:index], value[index:]

	# check state
	state, old_index, old_prefix = self.__state
	if index != old_index or prefix != old_prefix:
	    state = 0

	try:

	    # get next completion
	    match = self.complete(state, prefix, suffix)

	    if match:

		# insert completion
		if self.select_present():
		    self.delete(SEL_FIRST, SEL_LAST)
		self.insert(index, match)
		self.select_range(index, index+len(match))
		self.icursor(index)

		self.__state = state+1, index, prefix

	    else:

		self.__state = 0, index, prefix

	except IndexError:
	    pass

	return "break"

    def complete(self, state, prefix, suffix):
	return None # override

#
# file completion

class FileEntry(CompletionEntry):

    def complete(self, state, prefix, suffix):

	# workaround os.path.isdir buglet
	path = prefix
	if path[-1:] in ("/", os.sep):
	    path = path[:-1]

	# get directory and file prefix
	if os.path.isdir(path):
	    path, prefix = prefix, ""
	else:
	    path, prefix = os.path.split(prefix)

	if state == 0:

	    try:

		# look for matching files in directory
		prefix = os.path.normcase(prefix)
		length = len(prefix)

		self.__files = []
		for file in os.listdir(path):
		    file = os.path.normcase(file)
		    if file[:length] == prefix:
			if os.path.isdir(os.path.join(path, file)):
			    file = file + os.sep
			self.__files.append(file)

	    except os.error:
		self.__files = []

	# return next match
	return self.__files[state][len(prefix):]