Search code examples
pythontkintertk-toolkit

Tkinter text replace tab with four spaces


I would like to create a text widget that has the tab replaced with four spaces. Though this answer by @martineau has something similar all it truly does is make the tab look/be smaller. I would like actual spaces. How can I do this? I can't use the first function showed in the linked question because then it messes with the autocomplete

Code:

from tkinter import *
from tkinter.ttk import *
keywords_list = ["from", "tkinter", "import", "Label", "Button", "Entry"]
class specialText(Frame):
    def __init__(self, *args, **kwargs):
        Frame.__init__(self, *args, **kwargs)
        self.text = Text(self, width=50, height=10)
        self.text.pack(fill=BOTH, expand=True)
        self.text.insert(END, "# This is a test. Autocomplete works")
        self.text.bind("<Any-KeyRelease>", self._autocomplete)
        self.text.bind("<Tab>", self._handle_tab, add=True)

    def callback(self, word):
        #Returns possible matches
        #words is a list of almost every keyword and builtin function
        words = keywords_list
        matches = [x for x in words if x.startswith(word)]
        return matches

    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)

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

Solution

  • I figured it out

    Code:

    from tkinter.ttk import *
    keywords_list = ["from", "tkinter", "import", "Label", "Button", "Entry"]
    class specialText(Frame):
        def __init__(self, *args, **kwargs):
            Frame.__init__(self, *args, **kwargs)
            self.text = Text(self, width=50, height=10)
            self.text.pack(fill=BOTH, expand=True)
            self.text.insert(END, "# This is a test. Autocomplete works")
            self.text.bind("<Any-KeyRelease>", self._autocomplete)
            self.text.bind("<Tab>", self._handle_tab, add=True)
    
        def callback(self, word):
            #Returns possible matches
            #words is a list of almost every keyword and builtin function
            words = keywords_list
            matches = [x for x in words if x.startswith(word)]
            return matches
    
        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"
    
            # Here's the difference:
            else:
                self.text.insert("    ")
                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)
    
    if __name__ == "__main__":
        window = Tk()
        window.title("Autocomplete")
        window.geometry("500x500")
        text = specialText(window)
        text.pack(fill=BOTH, expand=True)
        window.mainloop()