Search code examples
pythontkinterhighlight

Always tkinter Text highlight_patern not working


I've used this question but can't seem to do it again later. I call it with root.after later but then it does nothing.
my code is:

class CustomText(tk.Text):
    '''A text widget with a new method, highlight_pattern()

    example:

    text = CustomText()
    text.tag_configure("red", foreground="#ff0000")
    text.highlight_pattern("this should be red", "red")

    The highlight_pattern method is a simplified python
    version of the tcl code at http://wiki.tcl.tk/3246
    '''
    def __init__(self, *args, **kwargs):
        tk.Text.__init__(self, *args, **kwargs)

    def highlight_pattern(self, pattern, tag, start="1.0", end="end",
                          regexp=False):
        '''Apply the given tag to all text that matches the given pattern

        If 'regexp' is set to True, pattern will be treated as a regular
        expression according to Tcl's regular expression syntax.
        '''

        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")
root = Tk()
T = CustomText(root).pack()
T.tag_configure("red",foreground="#ff0000")
def highlighttext():
    T.highlight_pattern('hello', 'red')
    root.after(10, highlighttext)
root.after(10, highlighttext)
root.mainloop()

Thanks for the help.


Solution

  • Fixes

    • widget.pack() returns None, so your T variable will be None. You need to pack your widget after initialization.
    • A function named higlighttext doesn't exist, fix your after method inside the highlighttext method.
    • Remove the after method outside highlighttext method.

    Suggestions

    Highlighting words with after method seems inefficient and it may not work as expected. Most of the code editors out there make use of the Text Modified events generated by text widgets.

    The code for <<TextModified>> event below is taken from Bryan Oakley's answer to one of the previous questions asked in stackoverflow.

    import tkinter as tk
    
    class CustomText(tk.Text):
        """A text widget with a new method, highlight_pattern()
        The highlight_pattern method is a simplified python
        version of the tcl code at http://wiki.tcl.tk/3246"""
        def __init__(self, *args, **kwargs):
            tk.Text.__init__(self, *args, **kwargs)
    
            # create a proxy for the underlying widget
            self._orig = self._w + "_orig"
            self.tk.call("rename", self._w, self._orig)
            self.tk.createcommand(self._w, self._proxy)
    
        def _proxy(self, command, *args):
            cmd = (self._orig, command) + args
            result = self.tk.call(cmd)
    
            if command in ("insert", "delete", "replace"):
                self.event_generate("<<TextModified>>")
    
            return result
    
        def highlight_pattern(self, pattern, tag, start="1.0", end="end", regexp=False):
            """Apply the given tag to all text that matches the given pattern
            If 'regexp' is set to True, pattern will be treated as a regular
            expression according to Tcl's regular expression syntax."""
    
            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
                # degenerate pattern which matches zero-length strings
                if count.get() == 0: break 
                self.mark_set("matchStart", index)
                self.mark_set("matchEnd", "%s+%sc" % (index, count.get()))
                self.tag_add(tag, "matchStart", "matchEnd")
    
    root = tk.Tk()
    
    text = CustomText()
    text.pack()
    text.tag_configure("red", foreground="#ff0000")
    
    def highlighttext(*args):
        text.highlight_pattern("hello", "red")
    
    text.bind("<<TextModified>>", highlighttext)
    
    root.mainloop()
    

    Complete code

    With all the fixes applied, your code would look as following

    import tkinter as tk
    
    class CustomText(tk.Text):
        '''A text widget with a new method, highlight_pattern()
        The highlight_pattern method is a simplified python
        version of the tcl code at http://wiki.tcl.tk/3246'''
        def __init__(self, *args, **kwargs):
            tk.Text.__init__(self, *args, **kwargs)
    
        def highlight_pattern(self, pattern, tag, start="1.0", end="end",
                              regexp=False):
            '''Apply the given tag to all text that matches the given pattern
            If 'regexp' is set to True, pattern will be treated as a regular
            expression according to Tcl's regular expression syntax.
            '''
    
            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")
    
    root = tk.Tk()
    
    T = CustomText(root)
    T.pack()
    T.tag_configure("red",foreground="#ff0000")
    
    def highlighttext():
        T.highlight_pattern('hello', 'red')
        root.after(1000, highlighttext)
    
    highlighttext()
    root.mainloop()