10

I don't quite understand how text.search method works. For example there is a sentence: Today a red car appeared in the park. I need to find a red car sequence and highlight it. It is found but here is how my highlighting looks like:

enter image description here

enter image description here

I am using self.text.search(word, start, stopindex=END) on the sentence. And it looks like search method works exactly like python's regexp search. Adding exact=True didn't change anything since it is default behavior which is why I don't understand what exact=True actually means. How to make a red car highlighted correctly?

stovfl
  • 14,998
  • 7
  • 24
  • 51
minerals
  • 6,090
  • 17
  • 62
  • 107

3 Answers3

15

The search method returns the index of the first match at or after the starting index, and optionally the number of characters that matched. You are responsible for highlighting what it found by using this information.

For example, consider this search:

countVar = tk.StringVar()
pos = text.search("a red car", "1.0", stopindex="end", count=countVar)

If a match is found, pos will contain the index of the first character of the match and countVar will contain the number of characters that matched. You can use this information to highlight the match by using an index of the form "index + N chars" or the shorthand "index + Nc". For example, if pos was 2.6 and count was 9, the index of the last character of the match would be 2.6+9c

With that, and assuming you've already configured a tag named "search" (eg: text.tag_configure("search", background="green")), you can add this tag to the start and end of the match like this:

text.tag_add("search", pos, "%s + %sc" (pos, countVar.get()))

To highlight all matches, just put the search command in a loop, and adjust the starting position to be one character past the end of the previous match.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • My tag_add code is `self.text.tag_add('style1', first_word_pos, end_pos)`. The thing is that I do not try to match `a red car` sequence. I am iterating over each word and each word is being searched by text widget. Of course when it receives `a` as an input it stops at `Today`. What you're saying means that I need to compare countVar to len(word) every time? But countVar's output is not an integer, am I correct? – minerals Oct 19 '13 at 14:45
  • `countVar` can be an integer if you use `IntVar`, or you can do `int(countVar.get())`. I don't know why you would compare countvar to the length of the word. countvar tells you how many characters matched, which you can use to highlight the exact sequence of characters that matched your pattern. – Bryan Oakley Oct 19 '13 at 14:49
  • @tasty: your question says, "I need to find `a red car` sequence". Are you saying you don't need to find that sequence, but instead you need to find the word "a" followed by the word "red" followed by the word "car"? If so, you need to update your question. The solution remains the same though, whether you're searching for "a", "red", or "a red car". Do the search, and get an index. Use the count to compute the length of the text that matched. – Bryan Oakley Oct 19 '13 at 14:55
  • I am not matching `a` only, I am matching part-of-speech tags and determiner can be `a` and `the` and you will never know that until the text.search loop. For example: `DT car` query can be either `a car` and `the car` what I don't understand is how countVar would help me to match `a red car` correctly as it as well find a match in `Today` and highlight an `a` in it. I am sorry, maybe I just don't understand – minerals Oct 19 '13 at 15:00
  • I mean how can countVar help me to disambiguate between "a" in `Today` and `a`? It returns the matched number of chars, isn't it? So in both ways it shall return 1. – minerals Oct 19 '13 at 15:09
  • @tasty: `countVar` doesn't help you find anything. It simply describes what was found. Yes, if you search for "a", it will always return 1. If you want to search for _words_, that's a different question than what you asked -- you asked how to match a specific sequence. You can use regular expressions to only match "a" if it is a whole word. For example, r`\ma\M` will only match "a" if it's a word, not if it's embedded in a string. You have to set `regexp=True` in your search, however. – Bryan Oakley Oct 19 '13 at 15:14
  • Yes, that is what I meant. regexp=True is what I need, something like \bword\b. countVar seems like an interesting feature I didn't know about. Thank you for your help. – minerals Oct 19 '13 at 15:39
2

It may be a problem of the indexes.

In a program of mine, i have to search the start index and calculate the end index

my method for example, it works fine:

def highlight(self):
    start = 1.0
    pos = self.area_example.search(self.item.name, start, stopindex=END)
    while pos:
        length = len(self.item.name)
        row, col = pos.split('.')
        end = int(col) + length
        end = row + '.' + str(end)
        self.area_example.tag_add('highlight', pos, end)
        start = end
        pos = self.area_example.search(self.item.name, start, stopindex=END)
    self.area_example.tag_config('highlight', background='white', foreground='red')
thinker3
  • 12,771
  • 5
  • 30
  • 36
  • 4
    Be aware that your solution won't work if you're doing a regular expression search, since the length of the match won't necessarily be the length of the pattern. Also, tkinter has a way to define relative indexes. For example, you can append "+10c" to an index to get the index 10 characters later. In your example you can do `"%s +%s c" % (pos, length)` instead of trying to compute a new index from the row and column. – Bryan Oakley Oct 19 '13 at 13:53
0

I have looked into @Bryan Oakley's approach and added some lines of code, in case you would like to highlight all the matches :

    def search_for_me(event):
    start_pos = "1.0"
    for tag in text.tag_names():
        text.tag_remove(tag, "1.0", "end")
    countVar = StringVar()
    while start_pos != "end":
        pos = text.search(search_entry.get(), start_pos, stopindex="end", 
        count=countVar)
        start_pos =  "%s + %sc" % (pos, int(countVar.get())+1)
        text.tag_configure("search", background="yellow")
        text.tag_add("search", pos, "%s + %sc" % (pos, countVar.get()))
georgette
  • 1
  • 1