KyferEz
KyferEz

Reputation: 397

How to convert ablosution position returned from re.finditer to row.column needed for a tkinter Text Widget?

I am using re.finditer to search for words in a tkinter text widget. This returns absolution positions for found items in the string, not row.column.

Now I need to convert this to row.column, however there seems to be no method to do this. However, in researching documentation here, it shows this, which doesn't work and gives the below error, and this which I found no option to do what i need:

.index(i)

    For an index i, this method returns the equivalent position in the form 'line.char'. 

Error:

loc = text.index(11)
Python Debugger 2:02:16 PM
  File "C:\Programs\Python37\lib\tkinter\__init__.py", line 3268, in index
Python Debugger 2:02:16 PM
    return str(self.tk.call(self._w, 'index', index))

So, how can I accomplish getting row.column from absolution position?

Background info: I have config file's I'm trying to search and highlight found items using tags. I'm using the below code to find the start index of the found items as well as the item itself so I know the length (since this uses regex and found items will not necessarily be the same length). I then created an OrderedDict so I can loop through it and highlight all found items.

The problem is that adding a tag doesn't work without absolute position, it requires row.column which I have no way to get from re.finditer.

Relevant code. For testing I am simply passing in StrToFind, and assume this is a long bit of text rows, separated by a \n. I can make it work if it's a long string without any \n's, but then the config being searched is incredibly hard to read.

import tkinter as tk
import re
import itertools
from collections import OrderedDict

def findStr(strToFind, strBeingSearched):
    '''returns OrderedDict of start locations and the word found'''
    wordlst = re.findall(strToFind, strBeingSearched) #get words found so I can know the length
    lst = [m.start() for m in re.finditer(strToFind, strBeingSearched)] #get index position of each word
    d = OrderedDict(zip(lst, wordlst)) #create ordered dict from 2 lists.
    return d

def btnFindAllClick(event):
    global search, color
    search[color] = findStr('StrToFind', text.get("1.0", tk.END))
    for index, word in search[color].items():
        length = len(word)
        text.tag_add(color, index, index + length) #this is where it breaks because I don't have row.column!

search = {}
color = 'yellow'

root = tk.Tk()
text = tk.Text(root, height=10, wrap="word")
text.pack(fill="both", expand=True)
text.tag_configure(color, background="yellow", foreground="black")
text.tag_raise("sel")

btn = tk.Button(root, text="Find All", width=12)
btn.pack(side="right")
btn.bind("<Button-1>", btnFindAllClick)

root.mainloop()

Upvotes: 0

Views: 94

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 385900

The text widget has a search method which can be used to find the exact line and character position of a match. It would be more efficient than getting all the text, doing a search, and then converting the results to a text widget index.

However, if you want to use your method, and assuming the text widget contains only text, you can have tkinter do some math for you if all you have is a number that represents an offset from zero.

For example, if you have the value 100, and you want to convert that to a proper index, you can use 1.0 + 100 characters (or 1.0+100c).

Thus, in your case you could compute the starting and ending index of the match like this:

start_index = text.index("1.0+{}c".format(index))
end_index = text.index("{} + {}c".format(start_index, length))

Upvotes: 1

Related Questions