Home > Blockchain >  Tkinter Text Autofill
Tkinter Text Autofill

Time:04-08

I am trying to make a simple and personal IDE for python using tkinter. I have seen it done before and have everything form syntax highlighting to a built in terminal but have the problem of no autofill. I know that you can have autofill in entry's with many methods out there but after searching for autofill with Text entries I couldn't find anything. If I could get some help that would be fantastic! I am looking for something similar to what is seen here.

Code of similar idea:

from ttkwidgets.autocomplete import AutocompleteEntry
from tkinter import *

countries = [
        'Antigua and Barbuda', 'Bahamas','Barbados','Belize', 'Canada',
        'Costa Rica ', 'Cuba', 'Dominica', 'Dominican Republic', 'El Salvador ',
        'Grenada', 'Guatemala ', 'Haiti', 'Honduras ', 'Jamaica', 'Mexico',
        'Nicaragua', 'Saint Kitts and Nevis', 'Panama ', 'Saint Lucia', 
        'Saint Vincent and the Grenadines', 'Trinidad and Tobago', 'United States of America'
        ]

ws = Tk()
ws.title('PythonGuides')
ws.geometry('400x300')
ws.config(bg='#f25252')

frame = Frame(ws, bg='#f25252')
frame.pack(expand=True)

Label(
    frame, 
    bg='#f25252',
    font = ('Times',21),
    text='Countries in North America '
    ).pack()

entry = AutocompleteEntry(
    frame, 
    width=30, 
    font=('Times', 18),
    completevalues=countries
    )
entry.pack()

ws.mainloop()

Link to source code of AutocompleteEntry

CodePudding user response:

For a rudimentary autocomplete feature the basic algorithm is fairly simple:

  • intercept key releases (which occur after tkinter automatically inserts the text that was typed)
  • get the word before the cursor and call a callback to find possible matches
  • add the first possible match, and select the part that was added so that future keypresses will replace it

You can also add a binding for the tab key that will see if there is autocomplete text that is visible, and move the cursor to the end.

This is a very hacked-together example to illustrate the principle, though it lacks any bulletproofing, optimizations, or handling of edge cases such as when backspacing, typing in the middle of a word, choosing alternate replacements, etc.

Just to be clear: this isn't the best way to implement autocomplete, it merely illustrates the concepts.

import tkinter as tk

class AutocompleteText(tk.Text):
    def __init__(self, *args, **kwargs):
        self.callback = kwargs.pop("autocomplete", None)
        super().__init__(*args, **kwargs)

        # bind on key release, which will happen after tkinter
        # inserts the typed character
        self.bind("<Any-KeyRelease>", self._autocomplete)

        # special handling for tab, which needs to happen on the
        # key _press_
        self.bind("<Tab>", self._handle_tab)

    def _handle_tab(self, event):
        # see if any text has the "autocomplete" tag
        tag_ranges= self.tag_ranges("autocomplete")
        if tag_ranges:
            # move the insertion cursor to the end of
            # the selected text, and then remove the "sel"
            # and "autocomplete" tags
            self.mark_set("insert", tag_ranges[1])
            self.tag_remove("sel", "1.0", "end")
            self.tag_remove("autocomplete", "1.0", "end")

            # prevent the default behavior of inserting a literal tab
            return "break"

    def _autocomplete(self, event):
        if event.char and self.callback:
            # get word preceeding the insertion cursor
            word = self.get("insert-1c wordstart", "insert-1c wordend")

            # pass word to callback to get possible matches
            matches = self.callback(word)

            if matches:
                # autocomplete on the first match
                remainder = matches[0][len(word):]

                # remember the current insertion cursor
                insert = self.index("insert")

                # insert at the insertion cursor the remainder of
                # the matched word, and apply the tag "sel" so that
                # it is selected. Also, add the "autocomplete" text
                # which will make it easier to find later.
                self.insert(insert, remainder, ("sel", "autocomplete"))

                # move the cursor back to the saved position
                self.mark_set("insert", insert)


def get_matches(word):
    # For illustrative purposes, pull possible matches from 
    # what has already been typed. You could just as easily 
    # return a list of pre-defined keywords.
    words = text.get("1.0", "end-1c").split()
    matches = [x for x in words if x.startswith(word)]
    return matches

root = tk.Tk()
text = AutocompleteText(root, autocomplete=get_matches)
text.pack(fill="both", expand=True)

root.mainloop()

  • Related