Mailing List Archive

Newbie scoping problem
I'm trying to put together a fairly simple Tkinter application.
In a nutshell, I want to have a root window with a scrollbox of
"selected" files, and N "browser" windows displaying files in
different directories. Double clicking on an entry in one of the
browser windows should insert it in the root window scrollbox.

Here's the code attached to the "New Browser" button:

def browsewin(self):
new = Toplevel() # replace current box
new.title('Testing new win') # new resizable window
new.iconname("Miller_time")
Label(new, text='The Ultimate Browser').pack(side=TOP)
list = Listbox(new)
scroll = Scrollbar(new)
list.config(yscrollcommand=scroll.set, relief=SUNKEN, height=20, width=40)
list.pack(side=LEFT, expand=YES, fill=BOTH)
scroll.config(command=list.yview, relief=SUNKEN)
scroll.pack(side=RIGHT, fill=BOTH)
pos = 0
retcode = os.system("ls /etc > /tmp/ls")
for label in open("/tmp/ls", "r").readlines(): # Grab some text
list.insert(pos, label[:-1]) # and menu/toolbars
pos = pos + 1
list.config(selectmode=SINGLE, setgrid=1) # select,resize modes
list.bind('<Double-1>', self.handleList) # set event handler
self.listbox = list


And the handleList method is pretty simple:

def handleList(self, event):
index = self.listbox.curselection() # on listbox double-click
label = self.listbox.get(index) # fetch selection text
print index, label # and call action here

The problem:

This seems to work fine - I can create a new browser, double-click away
and pop text into the root scrollbox (listbox + scrollbar). Create a
new browser and do the same thing. The problem comes when going back to
an early browser window, where the following happens:

Exception in Tkinter callback
Traceback (innermost last):
File "/usr/local/lib/python1.5/lib-tk/Tkinter.py", line 752, in __call__
return apply(self.func, args)
File "./sched.py", line 91, in handleList
label = self.listbox.get(index) # fetch selection text
File "/usr/local/lib/python1.5/lib-tk/Tkinter.py", line 1364, in get
return self.tk.call(self._w, 'get', first)
TclError: bad listbox index "": must be active, anchor, end, @x,y, or a number


I assume this means that self.listbox.curselection() can only
reference the most recently created listbox (browser) because I'm
assigning that handle when creating it.

So how do I reference the other browser windows I've created?

Thanks!

--- David Miller

Remove the ONMAPS from my email address to reply
Newbie scoping problem [ In reply to ]
David Miller <dmillerON@MAPSmote.rsn.com> wrote:

: I'm trying to put together a fairly simple Tkinter application.
: In a nutshell, I want to have a root window with a scrollbox of
: "selected" files, and N "browser" windows displaying files in
: different directories. Double clicking on an entry in one of the
: browser windows should insert it in the root window scrollbox.

: Here's the code attached to the "New Browser" button:

: def browsewin(self):
: new = Toplevel() # replace current box
: new.title('Testing new win') # new resizable window
: new.iconname("Miller_time")
: Label(new, text='The Ultimate Browser').pack(side=TOP)
: list = Listbox(new)
: scroll = Scrollbar(new)
: list.config(yscrollcommand=scroll.set, relief=SUNKEN, height=20, width=40)
: list.pack(side=LEFT, expand=YES, fill=BOTH)
: scroll.config(command=list.yview, relief=SUNKEN)
: scroll.pack(side=RIGHT, fill=BOTH)
: pos = 0
: retcode = os.system("ls /etc > /tmp/ls")
: for label in open("/tmp/ls", "r").readlines(): # Grab some text
: list.insert(pos, label[:-1]) # and menu/toolbars
: pos = pos + 1
: list.config(selectmode=SINGLE, setgrid=1) # select,resize modes
: list.bind('<Double-1>', self.handleList) # set event handler
: self.listbox = list


: And the handleList method is pretty simple:

: def handleList(self, event):
: index = self.listbox.curselection() # on listbox double-click
: label = self.listbox.get(index) # fetch selection text
: print index, label # and call action here

: The problem:

: This seems to work fine - I can create a new browser, double-click away
: and pop text into the root scrollbox (listbox + scrollbar). Create a
: new browser and do the same thing. The problem comes when going back to
: an early browser window, where the following happens:

: Exception in Tkinter callback
: Traceback (innermost last):
: File "/usr/local/lib/python1.5/lib-tk/Tkinter.py", line 752, in __call__
: return apply(self.func, args)
: File "./sched.py", line 91, in handleList
: label = self.listbox.get(index) # fetch selection text
: File "/usr/local/lib/python1.5/lib-tk/Tkinter.py", line 1364, in get
: return self.tk.call(self._w, 'get', first)
: TclError: bad listbox index "": must be active, anchor, end, @x,y, or a number


: I assume this means that self.listbox.curselection() can only
: reference the most recently created listbox (browser) because I'm
: assigning that handle when creating it.

Actually, it's a little more basic than that. The curselection()
method is a front-end for Tcl and returns a tuple of strings (of
numbers). You have to interpret that.

def handleList(self, event):
index = self.listbox.curselection()
# since the widget is in selectmode=SINGLE, there should only be one
# item (but an empty tuple if no selection)
if index:
pos = int( index[0] ) # convert the first item to an integer
label = self.listbox.get(index)
print pos, label
else:
print None, None

: So how do I reference the other browser windows I've created?

: Thanks!

Also, just a tip for in your browsewin() method. It would be more
efficient to use os.listdir() instead of the os.system() call (you also
do not remove the "/tmp/ls" file afterward). It doesn't create another
process and even makes it (*gasp*) more portable.

for label in os.listdir('/etc'):
list.insert('end', label)

-Arcege
Newbie scoping problem [ In reply to ]
David Miller wrote:
> And the handleList method is pretty simple:
>
> def handleList(self, event):
> index = self.listbox.curselection() # on listbox double-click
> label = self.listbox.get(index) # fetch selection text
> print index, label # and call action here

without actually running your code, I suspect
this is the culprit. curselection doesn't do what
you expect:

http://www.pythonware.com/library/tkinter/introduction/listbox.htm

curselection

curselection(). Get a list of the currently selected
alternatives. The list contains the indexes of the
selected alternatives (beginning with 0 for the first
alternative in the list). In Tkinter 1.101 (Python 1.5.1),
the list contains strings instead of integers. Since
this may change in future versions, you should make
sure your code can handle either case. See the
patterns section for a suggested solution.

in other words, you need to treat the return value as a
list, and the list members as strings (don't think this is
changed in 1.5.2). see the patterns section on the
above page for some more sample code.

and yes, that os.system call is both inefficient and
non-portable. try something like:

for file in os.listdir("/etc"):
listbox.insert(END, file)

or if you feel fancy:

apply(listbox.insert, (END,) + tuple(os.listdir("/etc")))

</F>
Newbie scoping problem [ In reply to ]
Fredrik Lundh <fredrik@pythonware.com> wrote:
> David Miller wrote:
>> And the handleList method is pretty simple:
>>
>> def handleList(self, event):
>> index = self.listbox.curselection() # on listbox double-click
>> label = self.listbox.get(index) # fetch selection text
>> print index, label # and call action here

> without actually running your code, I suspect
> this is the culprit. curselection doesn't do what
> you expect:

I copied it from some examples in "Programming Python", and it
works pretty much OK, even if it needs to be revisited for the
reasons you stated.

Thanks to Michael P. Reilly for pointing out the real problem...

After I determined the real problem was that the last browser
window I opend was handling all the dbl-click events, Michael
suggested the following code:

def handleList(self, event):
listbox = event.widget
index = listbox.curselection()
print 'index =', index


... which worked like a charm. I just had to point listbox
at the widget with the event and it worked like I meant it to:)




> http://www.pythonware.com/library/tkinter/introduction/listbox.htm


I love this site! This is a terrific resource!

> in other words, you need to treat the return value as a
> list, and the list members as strings (don't think this is
> changed in 1.5.2). see the patterns section on the
> above page for some more sample code.

True, but the real problem was that when I clicked on window
A, window B was handling the event and correctly reporting that
none of its entries were selected:)

> non-portable. try something like:

> for file in os.listdir("/etc"):
> listbox.insert(END, file)

> or if you feel fancy:

> apply(listbox.insert, (END,) + tuple(os.listdir("/etc")))

These are great suggestions. I'll definately run with them:)

Thanks to Michael and Fredrik!!!!

--- David