From: Dave McCormick on
Thanks John and Lie,

This is supposed to color the word "red" red, "blue" blue, "green"
green, etc...
The idea is say for a database front end, the search words will be
highlighted or colored when the query is returned. Another thing I want
to try is a Program editor, something like IDLE.
The program editor will be for Pic Basic Pro, (I mainly work with PIC
CHIPS). Yes there is a couple of good editors for PBP in Windows but
not Linux.
Yup, I have a long ways to go. Got to start some place :)

I was doing the grid because that is how the example I read dealing with
scrollbars worked. I stripped the scrollbar part out while working on
the colors. So I need to look at the pack() method more.

I will work on this more tonight and I will not look at your code before
I get something working.

Thanks again,
Dave



John Posner wrote:
> On Thu, 31 Dec 2009 10:24:44 -0500, Dave McCormick <mackrackit(a)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
From: Cousin Stanley on

> John Posner wrote ....
> ....
> I've posted a complete solution ....
>
> http://cl1p.net/jjp_dynamic_text_color/.

John ....

Thanks for posting your solution to Dave McCormick's query
about colorizing text ....

I was not familiar with the re.finditer method
for searching strings or with using tags in
Tkinter Text widgets to change text attributes ....

Instead of binding the Text widget to <KeyRelease> events
to call the colorizer on each key-stroke, I used a button
callback ....

http://cl1p.net/cs_static_text_color

This provides a static one-shot colorization after all
of the text is entered and should save a few cpu cycles
although it's not as nifty as the dynamic process ....

I used a dictionary instead of a list for position data
and also added a { term : color } dictionary so that
words other than color names can be hi-lighted ....


--
Stanley C. Kitching
Human Being
Phoenix, Arizona

From: Dave McCormick on
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(a)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
From: John Posner on
On Sat, Jan 2, 2010 at 1:47 PM, Dave McCormick wrote:

> WooHoo!!!
> I got it!!! Yup, I am sure it can be optimized but it works!!!

Dave, please ignore a couple of my bogus complaints in the previous
message:

... you call function new_Rword() before you define it

... this version also has the advantage of defining each function
just once, instead of multiple times on each keystroke


But I stand by my overall statement that the program didn't work for me,
and that rearranging the lines produces a working program.

-John
From: John Posner on
On Sat, Jan 2, 2010 at 1:47 PM, Dave McCormick wrote:

> WooHoo!!!
> I got it!!! Yup, I am sure it can be optimized but it works!!!

Hmmm ... it doesn't work for me ...

<snip>

####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")

How *could* this work, Dave, since you call function new_Rword() before
you define it? I rearranged the statements in your program, and it now
works for me:

#---------------------------------------------
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)
for word in redList:
new_Rword(complete, word)
for word in blueList:
new_Bword(complete, word)
for word in greenList:
new_Gword(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)

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)

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)

root = Tk()
Tbox = Text(root, width=40, height=15, wrap=CHAR,
font="Times 14 bold", bg="#dddddd")
Tbox.tag_config("red", foreground="red")
Tbox.tag_config("blue", foreground="blue")
Tbox.tag_config("green", foreground="green")

Tbox.pack()
Tbox.bind("<KeyRelease>", get_position)
Tbox.focus()
root.mainloop()
#---------------------------------------------

This version also has the advantage of defining each function just once,
instead of multiple times on each keystroke! Still to-do:

* rename get_position() to get_complete_text()

* replace new_Rword(), new_Bword(), and new_Gword() with a single
function that has an extra parameter, "color".

Keep at it!

-John