Dynamic text color

D

Dave McCormick

Hi All,

I am new to Python and the list so I hope I am posting this correctly...

I am working on a way to have text automatically formated in a Tkiniter
Text widget and would like some input on my code.
Currently I am using Python 2.5 because the server I use has that
installed. Tkinter is tk8.4.

Most of the time when I type red, blue, or green the code works as
expected. When I copy paste text into the widget the last line is
parsed with part of the previous lines
So I guess the problem is in the "looping"?

Here is my code:
from Tkinter import *
root = Tk()
def get_position(event):
start = 1.0
while 1:
pos = Tbox.search("red",END,backwards=TRUE)
if not pos:
break
red = pos + "-1c"
Tbox.tag_add("red", pos, float(pos)+.03)
Tbox.tag_config("red", foreground="red")

pos = Tbox.search("blue",END,backwards=TRUE)
if not pos:
break
blue = pos + "-1c"
Tbox.tag_add("blue", pos, float(pos)+.04)
Tbox.tag_config("blue", foreground="blue")

pos = Tbox.search("green",END,backwards=TRUE)
if not pos:
break
green = pos + "-1c"
Tbox.tag_add("green", pos, float(pos)+.05)
Tbox.tag_config("green", foreground="green")
break

Tbox = Text(root,width=40, height=15, wrap=CHAR)
Tbox.grid(column=0, row=0, sticky=(N+W+E+S))
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(0, weight=1)
Tbox.bind("<KeyRelease>", get_position)
Tbox.focus()
root.mainloop()

Thank you,
Dave
 
J

John Posner

Hi All,

I am new to Python and the list so I hope I am posting this correctly...

I am working on a way to have text automatically formated in a Tkiniter
Text widget and would like some input on my code.
Currently I am using Python 2.5 because the server I use has that
installed. Tkinter is tk8.4.

Most of the time when I type red, blue, or green the code works as
expected. When I copy paste text into the widget the last line is
parsed with part of the previous lines
So I guess the problem is in the "looping"?

Here is my code:
from Tkinter import *
root = Tk()
def get_position(event):
start = 1.0

A couple of problems here: you define "start", but then never use it.
Worse, it looks like you don't understand that a line-column index into a
Tkinter Text widget is a STRING, not a FLOAT.

while 1: pos = Tbox.search("red",END,backwards=TRUE)

I suggest that you use Tbox.get() instead of Tbox.search(), and then use
Python's more powerful text-search tools. More on this below.

if not pos:
break
red = pos + "-1c"
Tbox.tag_add("red", pos, float(pos)+.03)
Tbox.tag_config("red", foreground="red")

You don't want to define the "red" tag every time get_position() is
executed -- that is, every time the user presses a key. Define the
red/green/blue tags just once, right after you create the Text widget.

pos = Tbox.search("blue",END,backwards=TRUE)
if not pos:
break
blue = pos + "-1c"
Tbox.tag_add("blue", pos, float(pos)+.04)
Tbox.tag_config("blue", foreground="blue")

pos = Tbox.search("green",END,backwards=TRUE)
if not pos:
break
green = pos + "-1c"
Tbox.tag_add("green", pos, float(pos)+.05)
Tbox.tag_config("green", foreground="green")

The previous 6 lines are almost identical to the 6 lines that precede
them. This is fine for prototyping, but when you find yourself writing
code like this, think about using a loop or a parameterized function call.
For example, you might write this function:

def insert_color_markup(color):
...

.... and then call it as many times as you need to:

insert_color_markup("red")
insert_color_markup("green")
insert_color_markup("blue")

Now, about those text-search tools: the "re" (regular expression) module
include the function "finditer". This is a real power tool, combining
regular expressions and Python iterators -- both of which can be
intimidating to newcomers. But it's just what you want, IMHO. I hope the
following annotated IDLE transcript convinces you:

Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit
(Intel)] on win32.... hand, the green ball has both red and blue highlights.
.... Thank you.
.... """
<callable-iterator object at 0x00CC46D0>

... not too exciting, but this is better:
[<_sre.SRE_Match object at 0x00C01F70>, <_sre.SRE_Match object at
0x00C06E20>, <_sre.
SRE_Match object at 0x00C06E58>]

... this list indicates that we got three hits on the word "red"
[ matchobj.span() for matchobj in re.finditer("red", text) ]
[(4, 7), (16, 19), (77, 80)]

... paydirt: a list of (start,end) pairs for an invocation of
Text.tag_add()

One more hint: forget about the line-column indexes into the contexts of a
Text widget. Just count characters from the beginning, e.g.:

"1.0 + %d chars" % start_position
break

Tbox = Text(root,width=40, height=15, wrap=CHAR)
Tbox.grid(column=0, row=0, sticky=(N+W+E+S))
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(0, weight=1)
Tbox.bind("<KeyRelease>", get_position)
Tbox.focus()
root.mainloop()

Thank you,
Dave

I hope this set of hints is helpful, and not too disjointed, terse, or
cryptic. I think this is a cute little app!

-John
 
J

John Posner

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
 
D

Dave McCormick

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 said:
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
 
C

Cousin Stanley

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 ....
 
D

Dave McCormick

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 said:
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
 
J

John Posner

On Fri, 01 Jan 2010 21:01:04 -0500, Cousin Stanley

I was not familiar with the re.finditer method
for searching strings ...

Stanley and Dave --

So far, we've just been using finditer() to perform standard-string
searches (e.g. on the word "red"). Since Dave now wants to color multiple
words the same color (e.g. the words in redList), we can use a single
regular-expression search to locate *all* the words in a list. This
eliminates the need to use a "for" loop to handle the list. Here's what I
mean:

########## individual searches
>>> [matchobj.span() for matchobj in re.finditer("red", s)] [(14, 17)]
>>> [matchobj.span() for matchobj in re.finditer("crimson", s)] [(22, 29)]
>>> [matchobj.span() for matchobj in re.finditer("scarlet", s)]
[(35, 42)]

########## one "swell foop"
>>> redList = "red crimson scarlet".split()
>>> redList_regexp = "|".join(redList)
>>> redList_regexp 'red|crimson|scarlet'
>>> [matchobj.span() for matchobj in re.finditer(redList_regexp, s)]
[(14, 17), (22, 29), (35, 42)]

-John
 
D

Dave McCormick

John said:
On Fri, 01 Jan 2010 21:01:04 -0500, Cousin Stanley

I was not familiar with the re.finditer method
for searching strings ...

Stanley and Dave --

So far, we've just been using finditer() to perform standard-string
searches (e.g. on the word "red"). Since Dave now wants to color
multiple words the same color (e.g. the words in redList), we can use
a single regular-expression search to locate *all* the words in a
list. This eliminates the need to use a "for" loop to handle the list.
Here's what I mean:

########## individual searches
[matchobj.span() for matchobj in re.finditer("red", s)] [(14, 17)]
[matchobj.span() for matchobj in re.finditer("crimson", s)] [(22, 29)]
[matchobj.span() for matchobj in re.finditer("scarlet", s)]
[(35, 42)]

########## one "swell foop"
redList = "red crimson scarlet".split()
redList_regexp = "|".join(redList)
redList_regexp 'red|crimson|scarlet'
[matchobj.span() for matchobj in re.finditer(redList_regexp, s)]
[(14, 17), (22, 29), (35, 42)]

-John
Thanks again John,
This is fun!!!
I made a "red.text" file to hold the "red words", they are separated by
a space in the file.
Still need to add the extra parameter "color" someplace. But this is
what I have so far.
##############
file = 'red.txt'
file = open("red.txt","r")
rList = file.readlines()
file.close()
redList = str(rList).split()
blueList = "blue ball".split()
greenList = "green grass".split()
def get_complete_text(event):
complete_text = Tbox.get("1.0", END)
Tbox.tag_remove("red", "1.0", END)
Tbox.tag_remove("blue", "1.0", END)
Tbox.tag_remove("green", "1.0", END)
####RED########
redList_regexp = "|".join(redList)
for matchobj in re.finditer(redList_regexp, complete_text):
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#######
blueList_regexp = "|".join(blueList)
for matchobj in re.finditer(blueList_regexp, complete_text):
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#######
greenList_regexp = "|".join(greenList)
for matchobj in re.finditer(greenList_regexp, complete_text):
start,end = matchobj.span()
Tbox.tag_add("green", "1.0 + %d chars" % start,"1.0 + %d chars"
% end)
Tbox.tag_config("green", foreground="green")
 
J

John Posner

... But this is what I have so far.
##############
file = 'red.txt'
file = open("red.txt","r")
rList = file.readlines()
file.close()
redList = str(rList).split()

Dave, you're doing exactly the right thing: gradually expanding your
program, to provide more functionality and to learn more about the
available programming tools. It's also very good that you take care to
close() the file after processing it. Now for the bad news ...

1. Don't use "file" as a variable name -- it's a built-in object type.
(Some people don't like the fact that Python allows you to redefine such
"reserved words".)

2. It's probably not the best idea to use a single variable (you use
"file") to do double-duty: to hold the name of a file, and to hold the
open-file object returned by the open() function. It's perfectly legal,
but it hides information that might be useful when you're debugging a
program. This is better:

fname = 'red.txt'
inpf = open(fname, "r")

3. It might be better to use read() rather than readlines() to process the
"red.txt" file. It depends on what that file is supposed to contain. For
example, if you expect "red.txt" to contain exactly one line, which has
one or more words, you can process the open-file object like this:

file_contents = inpf.read()
redList = file_contents.split()

... or ...

redList = inpf.read().split()

It's certainly a mistake to use the expression "str(rList).split()". Using
str() to convert the list "rList" into a string creates a mess that
includes square-bracket characters. Did this actually work for you?

Best,
John
 
N

Neil Cerutti

2. It's probably not the best idea to use a single variable
(you use "file") to do double-duty: to hold the name of a
file, and to hold the open-file object returned by the open()
function. It's perfectly legal, but it hides information that
might be useful when you're debugging a program. This is
better:

fname = 'red.txt'
inpf = open(fname, "r")
Alternatively:
'red.txt'
 
D

Dave McCormick

John said:
Dave, you're doing exactly the right thing: gradually expanding your
program, to provide more functionality and to learn more about the
available programming tools. It's also very good that you take care to
close() the file after processing it. Now for the bad news ...
Seems like there is always bad news :)
1. Don't use "file" as a variable name -- it's a built-in object type.
(Some people don't like the fact that Python allows you to redefine
such "reserved words".)

2. It's probably not the best idea to use a single variable (you use
"file") to do double-duty: to hold the name of a file, and to hold the
open-file object returned by the open() function. It's perfectly
legal, but it hides information that might be useful when you're
debugging a program. This is better:

3. It might be better to use read() rather than readlines() to process
the "red.txt" file. It depends on what that file is supposed to
contain. For example, if you expect "red.txt" to contain exactly one
line, which has one or more words, you can process the open-file
object like this: All noted and fixed.

It's certainly a mistake to use the expression "str(rList).split()".
Using str() to convert the list "rList" into a string creates a mess
that includes square-bracket characters. Did this actually work for you?
It sort of worked. With one color file it seemed fine but after I
posted I added another color file and things fell apart.
Now with the above fixes it works with three colors from three files.
When the list are printed to the shell the list look like this:
redList ['red', 'dog', 'apple', '#']
blueList ['blue', 'ball', 'berry']
greenList ['green', 'grass', 'do']

But another problem is noticed. It does not matter if the list is built
in code or from a file.
If dog is entered, "do" will be green with the "g" being red.
Back to the drawing board.....

Here is the complete code"
######
from Tkinter import *
import re
RFfile = 'red.txt'
inpRF = open(RFfile,"r")
rList = inpRF.read()
inpRF.close()

BFfile = 'blue.txt'
inpBF = open(BFfile,"r")
bList = inpBF.read()
inpBF.close()

GFfile = 'green.txt'
inpGF = open(GFfile,"r")
gList = inpGF.read()
inpGF.close()

def get_complete_text(event):
complete_text = Tbox.get("1.0", END)
redList = str(rList).split()
blueList = str(bList).split()
greenList = str(gList).split()
print "redList",redList
print "blueList",blueList
print "greenList",greenList
Tbox.tag_remove("red", "1.0", END)
Tbox.tag_remove("blue", "1.0", END)
Tbox.tag_remove("green", "1.0", END)
####RED########
redList_regexp = "|".join(redList)
for matchobj in re.finditer(redList_regexp, complete_text):
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#######
blueList_regexp = "|".join(blueList)
for matchobj in re.finditer(blueList_regexp, complete_text):
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#######
greenList_regexp = "|".join(greenList)
for matchobj in re.finditer(greenList_regexp, complete_text):
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_complete_text)
Tbox.focus()
root.mainloop()
####
Thanks again,
Dave
 
J

John Posner

It sort of worked. With one color file it seemed fine but after I
posted I added another color file and things fell apart.
Now with the above fixes it works with three colors from three files.
When the list are printed to the shell the list look like this:
redList ['red', 'dog', 'apple', '#']
blueList ['blue', 'ball', 'berry']
greenList ['green', 'grass', 'do']

But another problem is noticed. It does not matter if the list is built
in code or from a file.
If dog is entered, "do" will be green with the "g" being red. Back to
the drawing board.....

It sounds like the program is doing exactly what you TOLD it to do (which
might not be what you WANT it to do):

1. In an earlier pass on the text, color the string "dog" red.
2. In a later pass, color the string "do" green.

You need to decide what you WANT to happen if one word to be colored is a
substring of another word to be colored differently. Or maybe you want to
outlaw such situations. After making that decision, you can start to think
about how to write the appropriate code.

Best,
John
 
D

Dave McCormick

John said:
On Tue, 05 Jan 2010 15:08:04 -0500, Dave McCormick

It sounds like the program is doing exactly what you TOLD it to do
(which might not be what you WANT it to do):

1. In an earlier pass on the text, color the string "dog" red.
2. In a later pass, color the string "do" green.

You need to decide what you WANT to happen if one word to be colored
is a substring of another word to be colored differently. Or maybe you
want to outlaw such situations. After making that decision, you can
start to think about how to write the appropriate code.

Best,
John
Darn thing doing what I told it to do... Guess that means I did
something right :)
But it is not what I am wanting.
I first thought to make it look for a space but that would not work when
a single character like "#" is to be colored if there is a "string" of
them. Or if all of the characters between quotes are to be colored.

I always did like puzzles!

Dave
 
J

John Posner

But it is not what I am wanting. I first thought to make it look for a
space but that would not work when a single character like "#" is to be
colored if there is a "string" of them. Or if all of the characters
between quotes are to be colored.

Regular expressions are good at handling searches like:

* all the characters between quotes
* the two-character string "do", but only if it's a complete word

-John
 
M

MRAB

Dave said:
On Wed, Jan 6, 2010 at 9:18 AM, John Posner <[email protected]

On Tue, 05 Jan 2010 16:54:44 -0500, Dave McCormick

But it is not what I am wanting. I first thought to make it look
for a space but that would not work when a single character like
"#" is to be colored if there is a "string" of them. Or if all
of the characters between quotes are to be colored.


Regular expressions are good at handling searches like:

* all the characters between quotes
* the two-character string "do", but only if it's a complete word

-John

--
http://mail.python.org/mailman/listinfo/python-list

I need another hint...

Been doing some reading and playing and it looks like
r'\bxxx\b'
is what I need. But I can not figure out how to pass a variable between
\b___\b
If the word in question is between the "\b \b" and in the list then it
works like I want it to.
The below does not work.

greenList_regexp = "|".join(greenList)
for matchobj in re.finditer(r'\bgreenList_regexp\b', complete_text):
start,end = matchobj.span()
The regex r'\bgreenList_regexp\b' will match the string
'greenList_regexp' if it's a whole word.

What you mean is "any of these words, provided that they're whole
words". You'll need to group the alternatives within "(?:...)", like
this:

r'\b(?:' + greenList_regexp + ')\b'
 
J

John Posner

The regex r'\bgreenList_regexp\b' will match the string
'greenList_regexp' if it's a whole word.

What you mean is "any of these words, provided that they're whole
words". You'll need to group the alternatives within "(?:...)", like
this:

r'\b(?:' + greenList_regexp + ')\b'

Oops, MRAB, you forgot to make the last literal a RAW string -- it should
be r')\b'

Dave, we're already into some pretty heavy regular-expression work, huh?.
Here's another approach -- not nearly as elegant as MRAB's:

Given this list:

greenList = ['green', 'grass', 'grump']

.... you currently are using join() to construct this regexp search string:

'green|grass|grump'

.... but you've decided that you really want this similar regexp search
string:

r'\bgreen\b|\bgrass\b|\bgrump\b'

You can achieve this by transforming each item on the list, then invoking
join() on the transformed list to create the search string. Here are a
couple of ways to transform the list:

* List comprehension:

whole_word_greenList = [ r'\b' + word + r'\b' for word in greenList]

* map() and a user-defined function:

def xform_to_wholeword_searchstring(word):
return r'\b' + word + r'\b'

whole_word_greenList = map(xform_to_wholeword_searchstring, greenList)


HTH,
John
 
D

Dave McCormick

John said:
The regex r'\bgreenList_regexp\b' will match the string
'greenList_regexp' if it's a whole word.

What you mean is "any of these words, provided that they're whole
words". You'll need to group the alternatives within "(?:...)", like
this:

r'\b(?:' + greenList_regexp + ')\b'

Oops, MRAB, you forgot to make the last literal a RAW string -- it
should be r')\b'

Dave, we're already into some pretty heavy regular-expression work,
huh?. Here's another approach -- not nearly as elegant as MRAB's:

Given this list:

greenList = ['green', 'grass', 'grump']

... you currently are using join() to construct this regexp search
string:

'green|grass|grump'

... but you've decided that you really want this similar regexp search
string:

r'\bgreen\b|\bgrass\b|\bgrump\b'

You can achieve this by transforming each item on the list, then
invoking join() on the transformed list to create the search string.
Here are a couple of ways to transform the list:

* List comprehension:

whole_word_greenList = [ r'\b' + word + r'\b' for word in greenList]

* map() and a user-defined function:

def xform_to_wholeword_searchstring(word):
return r'\b' + word + r'\b'

whole_word_greenList = map(xform_to_wholeword_searchstring, greenList)


HTH,
John
John,
That second "r" appears to do the trick.

Yea, pretty heavy into it. I read someplace that regular-expressions
were tricky, but I did not expect this :)

Now to start working this into the rest of my app and study your second
approach.

Thanks again for the help!!!
Dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top