1+ import tkinter
2+ import tkinter .ttk
3+
4+ tkinter_umlauts = ['odiaeresis' , 'adiaeresis' , 'udiaeresis' , 'Odiaeresis' , 'Adiaeresis' , 'Udiaeresis' , 'ssharp' ]
5+ class myentry (tkinter .Entry ):
6+ def set_completion_list (self , completion_list ):
7+ self ._completion_list = sorted (completion_list , key = str .lower )
8+ self ._hits = []
9+ self ._hit_index = 0
10+ self .position = 0
11+ self .bind ('<KeyRelease>' , self .handle_keyrelease )
12+
13+ def autocomplete (self , delta = 0 ):
14+ if delta :
15+ self .delete (self .position , tkinter .END )
16+ else :
17+ self .position = len (self .get ())
18+ _hits = []
19+ for element in self ._completion_list :
20+ if element .lower ().startswith (self .get ().lower ()):
21+ _hits .append (element )
22+ if _hits != self ._hits :
23+ self ._hit_index = 0
24+ self ._hits = _hits
25+ if _hits == self ._hits and self ._hits :
26+ self ._hit_index = (self ._hit_index + delta ) % len (self ._hits )
27+ if self ._hits :
28+ self .delete (0 ,tkinter .END )
29+ self .insert (0 ,self ._hits [self ._hit_index ])
30+ self .select_range (self .position ,tkinter .END )
31+
32+ def handle_keyrelease (self , event ):
33+ if event .keysym == "BackSpace" :
34+ self .delete (self .index (tkinter .INSERT ), tkinter .END )
35+ self .position = self .index (tkinter .END )
36+ if event .keysym == "Left" :
37+ if self .position < self .index (tkinter .END ):
38+ self .delete (self .position , tkinter .END )
39+ else :
40+ self .position = self .position - 1
41+ self .delete (self .position , tkinter .END )
42+ if event .keysym == "Right" :
43+ self .position = self .index (tkinter .END )
44+ if event .keysym == "Down" :
45+ self .autocomplete (1 )
46+ if event .keysym == "Up" :
47+ self .autocomplete (- 1 )
48+ if len (event .keysym ) == 1 or event .keysym in tkinter_umlauts :
49+ self .autocomplete ()
50+
51+ class mycombobox (tkinter .ttk .Combobox ):
52+
53+ def set_completion_list (self , completion_list ):
54+ self ._completion_list = sorted (completion_list , key = str .lower )
55+ self ._hits = []
56+ self ._hit_index = 0
57+ self .position = 0
58+ self .bind ('<KeyRelease>' , self .handle_keyrelease )
59+ self ['values' ] = self ._completion_list
60+
61+ def autocomplete (self , delta = 0 ):
62+ if delta :
63+ self .delete (self .position , tkinter .END )
64+ else :
65+ self .position = len (self .get ())
66+ _hits = []
67+ for element in self ._completion_list :
68+ if element .lower ().startswith (self .get ().lower ()): # Match case insensitively
69+ _hits .append (element )
70+ if _hits != self ._hits :
71+ self ._hit_index = 0
72+ self ._hits = _hits
73+ if _hits == self ._hits and self ._hits :
74+ self ._hit_index = (self ._hit_index + delta ) % len (self ._hits )
75+ if self ._hits :
76+ self .delete (0 ,tkinter .END )
77+ self .insert (0 ,self ._hits [self ._hit_index ])
78+ self .select_range (self .position ,tkinter .END )
79+
80+ def handle_keyrelease (self , event ):
81+ if event .keysym == "BackSpace" :
82+ self .delete (self .index (tkinter .INSERT ), tkinter .END )
83+ self .position = self .index (tkinter .END )
84+ if event .keysym == "Left" :
85+ if self .position < self .index (tkinter .END ):
86+ self .delete (self .position , tkinter .END )
87+ else :
88+ self .position = self .position - 1
89+ self .delete (self .position , tkinter .END )
90+ if event .keysym == "Right" :
91+ self .position = self .index (tkinter .END )
92+ if len (event .keysym ) == 1 :
93+ self .autocomplete ()
94+ # TEST THIS
95+ def test (test_list ):
96+ root = tkinter .Tk (className = ' AutocompleteEntry demo' )
97+ entry = myentry (root )
98+ entry .set_completion_list (test_list )
99+ entry .pack ()
100+ entry .focus_set ()
101+ combo = mycombobox (root )
102+ combo .set_completion_list (test_list )
103+ combo .pack ()
104+ combo .focus_set ()
105+ root .bind ('<Control-Q>' , lambda event = None : root .destroy ())
106+ root .bind ('<Control-q>' , lambda event = None : root .destroy ())
107+ root .mainloop ()
0 commit comments