Prev: Solved: TypeError: startView() takes exactly 1 argument (3 given)
Next: How to Suppress Interactive Assignment to "_"
From: Dave McCormick on 30 Dec 2009 12:58 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
From: John Posner on 30 Dec 2009 17:18 On Wed, 30 Dec 2009 12:58:06 -0500, Dave McCormick <mackrackit(a)gmail.com> wrote: > 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 >>> import re >>> text = """The red ball is red, not green. On other other .... hand, the green ball has both red and blue highlights. .... Thank you. .... """ >>> re.finditer("red", text) <callable-iterator object at 0x00CC46D0> ... not too exciting, but this is better: >>> list(re.finditer("red", text)) [<_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
From: Dave McCormick on 31 Dec 2009 10:24 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. 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 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. 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... Thanks again! Dave John Posner wrote: > On Wed, 30 Dec 2009 12:58:06 -0500, Dave McCormick > <mackrackit(a)gmail.com> wrote: > >> 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 >>>> import re > >>>> text = """The red ball is red, not green. On other other > ... hand, the green ball has both red and blue highlights. > ... Thank you. > ... """ > >>>> re.finditer("red", text) > <callable-iterator object at 0x00CC46D0> > > ... not too exciting, but this is better: > >>>> list(re.finditer("red", text)) > [<_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
From: Lie Ryan on 31 Dec 2009 10:52 On 1/1/2010 2:24 AM, Dave McCormick wrote: > 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... From http://infohost.nmt.edu/tcc/help/pubs/tkinter/text-index.html: """ + n chars From the given index, move forward n characters. This operation will *cross line boundaries.* """
From: John Posner on 31 Dec 2009 11:52 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
|
Next
|
Last
Pages: 1 2 3 4 5 Prev: Solved: TypeError: startView() takes exactly 1 argument (3 given) Next: How to Suppress Interactive Assignment to "_" |