Bryan Oakley's answer has helped me a lot on configuring highlights on many text widgets. Thanks to them, I'm able to understand how the highlighting works now.
Drawbacks
Only drawback I found was the difference between the regex syntax used by tcl/tk and the python regex syntax. The tcl/tk regex syntax is close to the normal python regular expression syntax, but it's not the same. Due to this issue, many of the regex testing applications available were not usable for writing regex for tkinter search method.
Solution
NOTE: This won't work as expected if the text widget has embedded images or widgets since the indexes in the widget won't be the same as the indexes in just the text portion.
I tried to incorporate python's regular expression standard library with the tkinter Text widget.
import re
import tkinter as tk
...
def search_re(self, pattern):
"""
Uses the python re library to match patterns.
pattern - the pattern to match.
"""
matches = []
text = textwidget.get("1.0", tk.END).splitlines()
for i, line in enumerate(text):
for match in re.finditer(pattern, line):
matches.append((f"{i + 1}.{match.start()}", f"{i + 1}.{match.end()}"))
return matches
the return value is a list of tuples containing the start and end indices of the matches. Example:
[('1.1', '1.5'), ('1.6', '1.10'), ('3.1', '3.5')]
Now these values can be used to highlight the pattern in the text widget.
Reference
CustomText widget
This is a wrapper for tkinter's Text widget with additional methods for highlighting and searching with regular expression library. It's based on bryan's code, thanks to them.
import re
import tkinter as tk
class CustomText(tk.Text):
"""
Wrapper for the tkinter.Text widget with additional methods for
highlighting and matching regular expressions.
highlight_all(pattern, tag) - Highlights all matches of the pattern.
highlight_pattern(pattern, tag) - Cleans all highlights and highlights all matches of the pattern.
clean_highlights(tag) - Removes all highlights of the given tag.
search_re(pattern) - Uses the python re library to match patterns.
"""
def __init__(self, master, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.master = master
# sample tag
self.tag_config("match", foreground="red")
def highlight(self, tag, start, end):
self.tag_add(tag, start, end)
def highlight_all(self, pattern, tag):
for match in self.search_re(pattern):
self.highlight(tag, match[0], match[1])
def clean_highlights(self, tag):
self.tag_remove(tag, "1.0", tk.END)
def search_re(self, pattern):
"""
Uses the python re library to match patterns.
Arguments:
pattern - The pattern to match.
Return value:
A list of tuples containing the start and end indices of the matches.
e.g. [("0.4", "5.9"]
"""
matches = []
text = self.get("1.0", tk.END).splitlines()
for i, line in enumerate(text):
for match in re.finditer(pattern, line):
matches.append((f"{i + 1}.{match.start()}", f"{i + 1}.{match.end()}"))
return matches
def highlight_pattern(self, pattern, tag="match"):
"""
Cleans all highlights and highlights all matches of the pattern.
Arguments:
pattern - The pattern to match.
tag - The tag to use for the highlights.
"""
self.clean_highlights(tag)
self.highlight_all(pattern, tag)
Example usage
Following code uses the above class, and shows an example of how to use it:
import tkinter as tk
root = tk.Tk()
# Example usage
def highlight_text(args):
text.highlight_pattern(r"\bhello\b")
text.highlight_pattern(r"\bworld\b", "match2")
text = CustomText(root)
text.pack()
text.tag_config("match2", foreground="green")
# This is not the best way, but it works.
# instead, see: https://stackoverflow.com/a/40618152/14507110
text.bind("<KeyRelease>", highlight_text)
root.mainloop()