Python search for next word in a Scrolled text box using tkinter

Viewed 596

I created a search function to parse text in a scrolled text box and it will find the first entry, and I want to add a function that will look for the next word that matches on a button. I found a post that had something similar and I tried replicating that in my code but I think it is effected by the trace function I use to parse the scrolled text box as letters are typed into the entry box. I assume I have to use tag_nextrange in the next_match() function. Can anyone lead me in the right direction?

Thanks in advance!

Here is what my code looks like:

def find_text(event):
    def get_text(var, indx, mode):
        word = my_var.get()
        start = '1.0'
        results_text.tag_remove('found', start, END)
        if word:
            start = results_text.search(word, start, nocase=1, stopindex=END)
            last = '%s+%dc' % (start, len(word))
            results_text.tag_add('found', start, last)
            results_text.tag_config('found', background='yellow')

    def next_match():
        #This is what I tied but doesn't work.
        while 'found' in results_text.tag_names('found'):
            n_match = results_text.tag_nextrange('found', END)
        if n_match:
            results_text.mark_set('found', n_match[0])
            results_text.see('found')

    my_var = StringVar()
    my_var.trace_add('write', get_text)
    findstr = Toplevel()
    findstr.wm_title("Find")
    findstr.wm_iconbitmap("cs_icon.ico")
    findstr.resizable(width = False, height = False)
    find_label = Label(findstr, text="Enter your search query:", font=("Helvetica", 12))
    find_label.grid(row=0, column=0,columnspan=3, sticky='nsew')
    find_entry = Entry(findstr, bd=4, textvariable=my_var, width=100)
    find_entry.grid(row=1, column=0, columnspan=3,padx=10, sticky='nsew')
    find_entry.focus()
    next_btn = Button(findstr, text="Next", width=20, command=next_match)
    next_btn.grid(row=2, column=2, padx=10, pady=5, sticky='e')
    #Binds Ctrl + f key to find_text function.
    root.bind("<Control-f>", find_text)
1 Answers

I figured it out some time ago. Looks like what worked for me is creating a separate list (search_list) to act as the position counter and have it increment and tag the words. I placed the declaration of the new list at the beginning of my code with the other mutable list I created. Also, for those who are curious, I pass var, indx, and mode into the get_text function to get live search results via a trace function. I hope this can be useful to those who are trying to implement this feature:

def get_text(var, indx, mode):
    word = my_var.get()
    search_list.clear()
    start = '1.0'
    results_text.tag_remove('next', '1.0', END)
    results_text.tag_remove('found', start, END)
    global matches_found
    matches_found = 0
    if word:
        while 1:
            start = results_text.search(word, start, regexp=True ,nocase=1,
                                        stopindex=END)
            if not start: break
            last = '%s+%dc' % (start, len(word))
            results_text.tag_add('found', start, last)
            matches_found += 1
            start = last
            results_text.tag_config('found', background='yellow')
    total_label.config(text=f"0:{str(matches_found)}")

def next_match():
    results_text.tag_remove('next', '1.0', END)
    end_search_label.config(text="")
    word = find_entry.get()
    count_pos = 0
    if word:
        start = "1.0" if search_list == [] else search_list[-1]
        start = results_text.search(word, start, nocase=1, stopindex=END)
        last = '%s+%dc' % (start, len(word))
        try:
            results_text.tag_add('next', start, last)
            results_text.tag_config('next', background='cyan',foreground='black',
                                    underline=1)
            counter_list = start.split('.')
            results_text.mark_set("insert", "%d.%d" % 
                                  (int(counter_list[0]),int(counter_list[1])))
            results_text.see(float(int(counter_list[0])))
            search_list.append(last)
            for i in search_list:
                count_pos += 1
            total_label.config(text=f"{str(count_pos)}:{str(matches_found)}")
        except:
            end_search_label.config(text="Search complete. No further matches",
                                    anchor='w')
            search_list.clear()
Related