'AutoComplete with backspacing tkinter
I am making an IDE and had asked for some help with getting a special widget that could give autocomplete. Thanks to @BryanOakley here I was able to get that help. The widget works well but it has a couple issues that I can't seem to figure out on my own. One of these problems is that once the autocomplete code finds a match it inserts the rest of the code and it prevents the user from removing the previous character. It looks like it should work as is given that it puts the insert to where it was left off but when I attempt to remove the last character with the Delete button it doesn't. I am unsure of how to fix this. How could I go about doing this?
The autocomplete Text widget:
from tkinter import *
class AutocompleteText(Frame):
def __init__(self, *args, **kwargs):
self.callback = kwargs.pop("autocomplete", None)
super().__init__(*args, **kwargs)
Frame.__init__(self, *args, **kwargs)
self.text = Text(self, font=("Courier New bold", 15), wrap=NONE, undo=True)
self.text.pack(side=LEFT, fill=BOTH, expand=True)
self.text.bind("<Any-KeyRelease>", self._autocomplete)
self.text.bind("<Tab>", self._handle_tab)
def _handle_tab(self, event):
tag_ranges= self.text.tag_ranges("autocomplete")
if tag_ranges:
self.text.mark_set("insert", tag_ranges[1])
self.text.tag_remove("sel", "1.0", "end")
self.text.tag_remove("autocomplete", "1.0", "end")
return "break"
def _autocomplete(self, event):
if event.char and self.callback:
word = self.text.get("insert-1c wordstart", "insert-1c wordend")
matches = self.callback(word)
if matches:
remainder = matches[0][len(word):]
insert = self.text.index("insert")
self.text.insert(insert, remainder, ("sel", "autocomplete"))
self.text.mark_set("insert", insert)
def get_matches(word):
#Returns possible matches
#words is a list of almost every keyword and builtin function
words=["False", "await", "else", "import", "pass", "None", "break", "except", "in", "raise", "True", "class", "finally", "is", "return", "and", "continue", "for", "lambda", "try", "as", "def", "from", "nonlocal", "while", "assert", "del", "global", "not", "with", "async", "elif", "if", "or", "yield", "abs", "all", "any", "ascii", "bin", "bool", "bytearray", "bytes", "callable", "chr", "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "exec", "filter", "float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex", "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "map", "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "print", "property", "range", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip"]
matches = [x for x in words if x.startswith(word)]
return matches
root = Tk()
text = AutocompleteText(root, autocomplete=get_matches)
text.pack()
root.mainloop()
Solution 1:[1]
I figured it out. Here's the code:
from tkinter import *
class AutocompleteText(Frame):
def __init__(self, *args, **kwargs):
self.callback = kwargs.pop("autocomplete", None)
super().__init__(*args, **kwargs)
Frame.__init__(self, *args, **kwargs)
self.text = Text(self, font=("Courier New bold", 15), wrap=NONE, undo=True)
self.text.pack(side=LEFT, fill=BOTH, expand=True)
self.text.bind("<Any-KeyRelease>", self._autocomplete)
self.text.bind("<Tab>", self._handle_tab)
def _handle_tab(self, event):
tag_ranges= self.text.tag_ranges("autocomplete")
if tag_ranges:
self.text.mark_set("insert", tag_ranges[1])
self.text.tag_remove("sel", "1.0", "end")
self.text.tag_remove("autocomplete", "1.0", "end")
return "break"
def _autocomplete(self, event):
if event.char and self.callback and event.keysym != "BackSpace":
word = self.text.get("insert-1c wordstart", "insert-1c wordend")
matches = self.callback(word)
if matches:
remainder = matches[0][len(word):]
insert = self.text.index("insert")
self.text.insert(insert, remainder, ("sel", "autocomplete"))
self.text.mark_set("insert", insert)
def get_matches(word):
#Returns possible matches
#words is a list of almost every keyword and builtin function
words=["False", "await", "else", "import", "pass", "None", "break", "except", "in", "raise", "True", "class", "finally", "is", "return", "and", "continue", "for", "lambda", "try", "as", "def", "from", "nonlocal", "while", "assert", "del", "global", "not", "with", "async", "elif", "if", "or", "yield", "abs", "all", "any", "ascii", "bin", "bool", "bytearray", "bytes", "callable", "chr", "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "exec", "filter", "float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex", "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "map", "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "print", "property", "range", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip"]
matches = [x for x in words if x.startswith(word)]
return matches
root = Tk()
text = AutocompleteText(root, autocomplete=get_matches)
text.pack()
root.mainloop()
This works because when you give an event it has a value to tell what key was pressed. By using this value we can make a backspacing edge case
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | TRCK |
