"""
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 AutocompleteCombobox(ttk.Combobox):
""":class:`ttk.Combobox` widget that features autocompletion."""
[docs] def __init__(self, master=None, completevalues=None, **kwargs):
"""
Create an AutocompleteCombobox.
:param master: master widget
:type master: widget
:param completevalues: autocompletion values
:type completevalues: list
:param kwargs: keyword arguments passed to the :class:`ttk.Combobox` initializer
"""
ttk.Combobox.__init__(self, master, values=completevalues, **kwargs)
self._completion_list = completevalues
if isinstance(completevalues, list):
self.set_completion_list(completevalues)
self._hits = []
self._hit_index = 0
self.position = 0
[docs] def set_completion_list(self, completion_list):
"""
Use the completion list as drop down selection menu, arrows move through menu.
: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)
self['values'] = self._completion_list # Setup our popup menu
[docs] def autocomplete(self, delta=0):
"""
Autocomplete the Combobox.
: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 == "Return":
self.handle_return(None)
return
if len(event.keysym) == 1:
self.autocomplete()
# No need for up/down, we'll jump to the popup
# list at the position of the autocompletion
[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()