Skip to content
Advertisement

Python: Tkinter bind(“<>”) works only once

so i was trying out tkinter Text widget.. and made a small code that highlights the word “print” in the text.. CODE:

from tkinter import *
def get(*arg):
    print("Highlighting...")
    text.tag_remove('found', '1.0', END)
    s = "print"
    if s:
        idx = '1.0'
        while 1:
            idx = text.search(s, idx, nocase=1, stopindex=END)
            if not idx: break
            lastidx = '%s+%dc' % (idx, len(s))
            text.tag_add('found', idx, lastidx)
            idx = lastidx
            text.see(idx)  # Once found, the scrollbar automatically scrolls to the text
            text.bind('<<Modified>>', get)
            break
        text.tag_config('found', foreground='green')

root = Tk()
text = Text(root)
text.grid()
root.bind('<<Modified>>', get)
root.mainloop()

In this, the root.bind('<<Modified>>', get) works only once. i checked it with the line print("Highlighting...") it just worked once even when i enter thousands of characters..

Am i doing something wrong? or my approach is bad?

SPECS:
OS: Windows 7 SP1 (i will upgrade only to windows 11)
Python: Python 3.8.10
Arch: Intel x86 

Advertisement

Answer

Try this:

from tkinter import *

# From: https://stackoverflow.com/a/40618152/11106801
class CustomText(Text):
    def __init__(self, *args, **kwargs):
        """A text widget that report on internal widget commands"""
        Text.__init__(self, *args, **kwargs)

        # create a proxy for the underlying widget
        self._orig = self._w + "_orig"
        self.tk.call("rename", self._w, self._orig)
        self.tk.createcommand(self._w, self._proxy)

    def _proxy(self, command, *args):
        cmd = (self._orig, command) + args
        result = self.tk.call(cmd)

        if command in ("insert", "delete", "replace"):
            self.event_generate("<<TextModified>>")

        return result


def get(event):
    text.tag_remove("found", "1.0", "end")
    string = "print"
    if string:
        idx = "1.0"
        while True:
            idx = text.search(string, idx, nocase=1, stopindex=END)
            if not idx:
                break
            lastidx = f"{idx}+{len(string)}c"
            text.tag_add("found", idx, lastidx)
            idx = lastidx
        text.tag_config("found", foreground="green")


root = Tk()
text = CustomText(root)
text.grid()
root.bind("<<TextModified>>", get)
root.mainloop()

This uses the CustomText that @BryanOakley created here.

Also I had to remove the break from the end of your while True loop because it stopped searching for "print".

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement