0

I've got the following code to highlight word-matches within a tk.Text.

However, this code will highlight all matches it finds all at once (e.g. if the sentence is "my cat is my friend", it would highlight all occurrences of "my" at the first time I type that word).

How can I modify this code in order to make it highlight only the first occurrence of a word at the first time I pass it through the function, and then when I pass that word through the function a second time, it highlights the next occurrence of that word (so, in my example, it would first highlight "my cat is my friend", and then, on a second run, "my cat is my friend".

Thanks in advance!

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

    def highlight_pattern(self, pattern, tag, start="1.0", end="end",
                          regexp=False):
        start = self.index(start)
        end = self.index(end)
        self.mark_set("matchStart", start)
        self.mark_set("matchEnd", start)
        self.mark_set("searchLimit", end)

        count = tk.IntVar()
        while True:
            index = self.search(pattern, "matchEnd","searchLimit",
                                count=count, regexp=regexp)
            if index == "": break
            if count.get() == 0: break # degenerate pattern which matches zero-length strings
            self.mark_set("matchStart", index)
            self.mark_set("matchEnd", "%s+%sc" % (index, count.get()))
            self.tag_add(tag, "matchStart", "matchEnd")
Alex
  • 3
  • 2
  • 2
    Have you tried removing the `while` statement, and executing that code just once? – Bryan Oakley Jun 02 '23 at 23:59
  • @BryanOakley You're correct, Bryan. Your suggestion did what I initially thought I needed. However, I then realized that I actually need the following occurrences of a word to be highlighted as well, on followings runs of that method. I edited my initial post in order to reflect that need. Thank you, though. – Alex Jun 03 '23 at 03:04
  • The code you copied allows you to specify where to start looking. The second time you call it, provide an index after the previous match. – Bryan Oakley Jun 03 '23 at 03:53
  • @BryanOakley I apologize for my ignorance. I have been trying to find documentation that explains how these methods work, but I haven't managed to properly understand what this code is doing with what I found... I tried to ask ChatGPT and it did suggest that I save in a variable what the last index was. But in all of my attempts with ChatGPT, my code would either ignore a second occurrence of the word or it would highlight both occurrences at once. I'm still learning to code... Would you be able to assist? – Alex Jun 03 '23 at 04:03

1 Answers1

0

Since you only need to find one match at a time you can greatly simplify your search function by removing the while loop so that it always searches exactly once.

The search function sets two special marks (indexes) named matchStart and matchEnd. If you start all but the first search at matchEnd it will find the next occurrence after the previous search. For the very first search you can use the index "1.0".

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

    def highlight_pattern(self, pattern, tag, start="1.0", end="end", regexp=False):
        count = tk.IntVar()
        index = self.search(pattern, start, end, count=count, regexp=regexp)
        if index != "":
            self.mark_set("matchStart", index)
            self.mark_set("matchEnd", "%s+%sc" % (index, count.get()))
            self.tag_add(tag, "matchStart", "matchEnd")

You can then call this function from another function triggered by a button or binding. That function can check to see if matchEnd exists, and use the appropriate starting point for the search.

text = CustomText(root)
...
def search(event=None):
    start = "1.0" if "matchEnd" not in text.mark_names() else "matchEnd"
    pattern = search_entry.get()
    text.highlight_pattern(pattern, "search", start)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thank you so much for the help! It finally worked! I realized I had made a huge mistake in my code as well, as I had put the call to this function within a for loop, so obviously it would run more than once. But I really appreciate the explanation, after reading your comment and trying to adapt your code within mine, I was finally able to understand how all of this works. Cheers! – Alex Jun 04 '23 at 01:03