Dynamic text color

Dave McCormick mackrackit at gmail.com
Sat Jan 2 13:47:24 EST 2010


WooHoo!!!
I got it!!! Yup, I am sure it can be optimized but it works!!!
John,
I have to admit that I spent several hours working on this before I 
looked at your example, then I spent another several hours getting this far.
Would never have gotten it with out you help.  Thanks!!!

Also reading the thread "Significant whitespace" helped.  I had an 
indent problem in a FOR loop.
This is a whole lot different than the MCU programming I do in ASM or 
BASIC, starting to make sense though.

Thanks again for all the help and putting up with folks like me,
Dave

Here is what I can up with.
#######
from Tkinter import *
import re

redList = "red dog".split()
blueList = "blue ball".split()
greenList = "green grass".split()
def get_position(event):
    complete = Tbox.get("1.0", END)
####RED########
    for word in redList:
        new_Rword(complete, word)      
def new_Rword(complete, word):
    Tbox.tag_remove(word, "1.0", END)
    for matchobj in re.finditer(word, complete):
        start,end =  matchobj.span()        
        Tbox.tag_add("red", "1.0 + %d chars" % start,"1.0 + %d chars" % end)
    Tbox.tag_config("red", foreground="red")
####BLUE#######
    for word in blueList:
        new_Bword(complete, word)      
def new_Bword(complete, word):
    Tbox.tag_remove(word, "1.0", END)
    for matchobj in re.finditer(word, complete):
        start,end =  matchobj.span()        
        Tbox.tag_add("blue", "1.0 + %d chars" % start,"1.0 + %d chars" % 
end)
    Tbox.tag_config("blue", foreground="blue")
####GREEN#######
    for word in greenList:
        new_Gword(complete, word)      
def new_Gword(complete, word):
    Tbox.tag_remove(word, "1.0", END)
    for matchobj in re.finditer(word, complete):
        start,end =  matchobj.span()        
        Tbox.tag_add("green", "1.0 + %d chars" % start,"1.0 + %d chars" 
% end)
    Tbox.tag_config("green", foreground="green")  

root = Tk()
Tbox = Text(root, width=40, height=15, wrap=CHAR,
           font="Times 14 bold", bg="#dddddd")
Tbox.pack()
Tbox.bind("<KeyRelease>", get_position)
Tbox.focus()
root.mainloop()


John Posner wrote:
> On Thu, 31 Dec 2009 10:24:44 -0500, Dave McCormick <mackrackit at gmail.com>
> wrote:
>
>> John,
>>
>> Thank you for the tips.
>> I was changing the line-column index to a FLOAT because the search 
>> would return the starting position (pos) of the string, then by 
>> making it a FLOAT and adding the string length I was able to get the 
>> end position.
>> If "red" was on line 1 column 0..
>>           Tbox.tag_add("red", pos, float(pos)+.03)
>>        =
>>           Tbox.tag_add("red", 1.0, 1.3)
>> It was all I could come up with.
>
> Yup, Dave, I've dug this kind of hole for myself many times!
>
>>
>> You have convinced me about the re.finditer for this,  I think... 
>> Still in the prototyping mode:
>>
>>        def get_position(event):
>>                 pos = Tbox.get(1.0, END)
>>                 match = [ matchobj.span() for matchobj in 
>> re.finditer("red", pos) ]
>>                 print "match ",match  #debug to shell
>
> Notes:
>
> * Variable "pos" should be "text" or "complete_text" or something 
> similar.
>
> * The first argument to the get() function must be a string:
>
>       wrong ... complete_text = Tbox.get(1.0, END)
>       right ... complete_text = Tbox.get("1.0", END)
>
> But there's a more important problem. Is this function supposed to 
> handle *one* word to be colored red, or *all the words* to be colored 
> red? Here's what you want to do on each user keystroke:
>
>     1. Use get() to place the entire contents of the Text widget in a
> variable, say "complete_text".
>
>     2. Use re.finditer() to generate START,END pairs for the 
> substrings to
> be colored red. You might find it easier to create a list from the 
> iterator, though it isn't really necessary:
>
>      start_end_pairs = list(re.finditer("red", complete_text))
>
>     3. Loop over the START,END pairs in this list. In each loop, use
> tag_add() to tag one of the substrings.
>
> [OOPS: I apologize if my suggestion in the previous post misled you. I 
> described the output of finditer() as "a list of (start,end) pairs for 
> an invocation of Text.tag_add()". I should have said "a list of 
> (start,end) pairs, *WHICH CAN BE LOOPED OVER, FOR A SERIES OF 
> INVOCATIONS* of Text.tag_add()".]
>
> Note that the 3 steps above only handle the color red. So you want to 
> place these steps in a function, then call the function for each color:
>
>    insert_color_markup(text, "red")
>    insert_color_markup(text, "green")
>    insert_color_markup(text, "blue")
>
> For each call, pass in the contents of complete_text as the first 
> argument.
>
> So the function-call hierarchy would be:
>
>  get_position()                    <--- invoked on each keystroke
>     insert_color_markup()          <--- called once for each color
>        get() then finditer() then loop-driven calls to tag_add()
>
> I hope that makes sense to you!
>
>
>> Gives all of START,END pairs just fine. It is the last hint about 
>> line-column indexes that I am have problems with.  All of the 
>> documentation I can find about "text.tag_add()" uses line-column for 
>> coordinates.
>
> Lie Ryan has already pointed you to clarifying documentation. Be sure 
> to bookmark http://infohost.nmt.edu/tcc/help/pubs/tkinter/ in your Web 
> browser!
>
>> If I count characters from the beginning how do I know what line the 
>> text is on? Would you mind making your last hint a bit stronger...
>
> The point is that *you don't need to*. Handling the text as a simple 
> sequence of characters is much simpler than worrying about line/column 
> pairs. (If you were using a different shade of red on different lines, 
> you *would* need to worry about line/column pairs.)
>
> One more suggestion, which I forgot to make in the previous round. 
> Your code includes this:
>
>    Tbox.grid(column=0, row=0, sticky=(N+W+E+S))
>    root.grid_columnconfigure(0, weight=1)
>    root.grid_rowconfigure(0, weight=1)
>
> You're working too hard here. This is sufficient:
>
>    Tbox.pack()
>
> Keep at it, Dave. I've posted a complete solution at 
> http://cl1p.net/jjp_dynamic_text_color/. But I suggest that you put in 
> a couple of more hours of coding before peeking at it.
>
> Best,
> John



More information about the Python-list mailing list