Home > front end >  Tkinter Text widget not recognizing "<" as part of a word
Tkinter Text widget not recognizing "<" as part of a word

Time:05-30

I'm trying to make a simple HTML Editor with Tkinter, with Python 3.10. Right now I'm trying to implement auto-completion using the Tkinter Text widget. The widget checks the word currently typed to see if it matches with any of the HTML tags in self._tags_list. And if it does, it auto-completes it.

Code:

import random
import tkinter as tk


class IDE(tk.Text):
    def __init__(self, *args, **kwargs):
        tk.Text.__init__(self, *args, **kwargs)

        self._tags_list = ['<html></html>', '<head></head>', '<title></title>', '<body></body>', '<h1></h1>',
                           '<h2></h2>', '<h3></h3>', '<h4></h4>', '<h5></h5>', '<h6></h6>', '<p></p>', '<b></b>']

        self.bind("<Any-KeyRelease>", self._autocomplete)
        self.bind("<Tab>", self._completion, add=True)
        self.bind("<Return>", self._completion, add=True)

    def callback(self, word):
        # Returns possible matches of `word`
        matches = [x for x in self._tags_list if x.startswith(word)]
        return matches

    def _completion(self, _event):
        tag_ranges = self.tag_ranges("autocomplete")
        if tag_ranges:
            self.mark_set("insert", tag_ranges[1])
            self.tag_remove("sel", "1.0", "end")
            self.tag_remove("autocomplete", "1.0", "end")
            return "break"

    def _autocomplete(self, event):
        if event.keysym not in ["Return"] and not self.tag_ranges("sel"):
            self.tag_remove("sel", "1.0", "end")
            self.tag_remove("autocomplete", "1.0", "end")
            word = self.get("insert-1c wordstart", "insert-1c wordend")
            print(f"word: {word}")

            matches = self.callback(word)
            print(f"matches: {matches}")

            if matches:
                remainder = random.choice(matches)[len(word):]
                print(remainder)
                insert = self.index("insert")
                self.insert(insert, remainder, ("sel", "autocomplete"))
                self.mark_set("insert", insert)
                return


if __name__ == "__main__":
    window = tk.Tk()
    window.title("Autocomplete")
    window.geometry("500x500")
    text = IDE(window)
    text.pack(fill=tk.BOTH, expand=True)

Used from Window

Can someone please tell me why this is happening and how to fix it? I've checked all around and found nothing yet. Sorry if it is obvious, but I am now learning Tkinter. Also if there are any other problems in the code, please tell me.

CodePudding user response:

A word in wordstart is defined as

"a word is either a string of consecutive letter, digit, or underbar (_) characters, or a single character that is none of these types."

when you type more characters after <, the wordstart starts from the character next to the <. You can make this work by adding -1c at the end of wordstart

word = self.get("insert -1c wordstart -1c", "insert -1c wordend")

By adding

if "<" in word:
    word = word[word.index("<"):]

after word you can have a slight improvement.

Besides that you also need to exclude BackSpace, Ctrl a etc from your keysym

  • Related