Source code for ttkwidgets.autocomplete.autocomplete_entry

"""
Authors: Mitja Martini and Russell Adams
License: "Licensed same as original by Mitja Martini or public domain, whichever is less restrictive"
Source: https://mail.python.org/pipermail/tkinter-discuss/2012-January/003041.html

Edited by RedFantom for ttk and Python 2 and 3 cross-compatibility and <Enter> binding
"""
try:
    import Tkinter as tk
    import ttk
except ImportError:
    import tkinter as tk
    from tkinter import ttk

tk_umlauts = ['odiaeresis', 'adiaeresis', 'udiaeresis', 'Odiaeresis', 'Adiaeresis', 'Udiaeresis', 'ssharp']


[docs]class AutocompleteEntry(ttk.Entry): """ Subclass of :class:`ttk.Entry` that features autocompletion. To enable autocompletion use :meth:`set_completion_list` to define a list of possible strings to hit. To cycle through hits use down and up arrow keys. """
[docs] def __init__(self, master=None, completevalues=None, **kwargs): """ Create an AutocompleteEntry. :param master: master widget :type master: widget :param completevalues: autocompletion values :type completevalues: list :param kwargs: keyword arguments passed to the :class:`ttk.Entry` initializer """ ttk.Entry.__init__(self, master, **kwargs) self._completion_list = completevalues self.set_completion_list(completevalues) self._hits = [] self._hit_index = 0 self.position = 0
[docs] def set_completion_list(self, completion_list): """ Set a new auto completion list :param completion_list: completion values :type completion_list: list """ self._completion_list = sorted(completion_list, key=str.lower) # Work with a sorted list self._hits = [] self._hit_index = 0 self.position = 0 self.bind('<KeyRelease>', self.handle_keyrelease)
[docs] def autocomplete(self, delta=0): """ Autocomplete the Entry. :param delta: 0, 1 or -1: how to cycle through possible hits :type delta: int """ if delta: # need to delete selection otherwise we would fix the current position self.delete(self.position, tk.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.lower().startswith(self.get().lower()): # Match case-insensitively _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, tk.END) self.insert(0, self._hits[self._hit_index]) self.select_range(self.position, tk.END)
[docs] def handle_keyrelease(self, event): """ Event handler for the keyrelease event on this widget. :param event: Tkinter event """ if event.keysym == "BackSpace": self.delete(self.index(tk.INSERT), tk.END) self.position = self.index(tk.END) if event.keysym == "Left": if self.position < self.index(tk.END): # delete the selection self.delete(self.position, tk.END) else: self.position -= 1 # delete one character self.delete(self.position, tk.END) if event.keysym == "Right": self.position = self.index(tk.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 if event.keysym == "Return": self.handle_return(None) return if len(event.keysym) == 1 or event.keysym in tk_umlauts: self.autocomplete()
[docs] def handle_return(self, event): """ Function to bind to the Enter/Return key so if Enter is pressed the selection is cleared. :param event: Tkinter event """ self.icursor(tk.END) self.selection_clear()