This code is just how it currently looks, what I needed for my code, so it's not
a full-fledged or even tested class.
But it works.
<code language="Py3">
import tkinter as t
import tkinter.simpledialog
import tkinter.messagebox
t.askstring = tkinter.simpledialog.askstring
t.warningbox = tkinter.messagebox.showwarning
class UsableListbox( t.Frame ):
def __init__( self, parent_widget ):
t.Frame.__init__( self, parent_widget )
scrollbar = t.Scrollbar( self, orient = "vertical" )
self.lb = t.Listbox( self, yscrollcommand = scrollbar.set )
scrollbar.config( command = self.lb.yview )
scrollbar.pack( side = "right", fill = "y" )
self.lb.pack( side = "left", fill = "both", expand = 1 )
def current_index( self ):
indices = self.lb.curselection()
assert( len( indices ) <= 1 ) # TODO: about multi-selection.
return None if len( indices ) == 0 else int( indices[0] )
def current( self ):
#return self.lb.get( "active" ) # Incorrect with mousing
i = self.current_index()
return "" if i is None else self.lb.get( i )
def append( self, item ):
return self.lb.insert( "end", item )
def add_selection_event_handler( self, handler ):
"An event handler takes one argument, a Tkinter Event"
return self.lb.bind( "<<ListboxSelect>>", handler )
</code>
Cheers,
- Alf
Well the frame contains a listbox and scrollbar. And with the frame as attribute
the object that you have a direct reference to would then not be the complete
thing on screen, with respect to sizing and placement and such. I generally
don't like widgets that present stuff outside their bounding box. I guess that
could be fixed up somehow by overriding this and that, but I find it simpler to
just make the enclosing widget the widget that one has a reference to. And in a
way it's also good that it's more laborious to directly access the tkinter
listbox stuff, because what I discovered so far is that much of it requires work
arounds and fixups, i.e. that it should be wrapped in higher level methods.
I had to add some more such functionality after I posted that code.
So, current (this is untested except that it works for what I'm using it for!):
<code language="Py3">
class UsableListbox( t.Frame ):
def __init__( self, parent_widget ):
t.Frame.__init__( self, parent_widget )
scrollbar = t.Scrollbar( self, orient = "vertical" )
self.lb = t.Listbox( self, exportselection = 0, yscrollcommand =
scrollbar.set )
scrollbar.config( command = self.lb.yview )
scrollbar.pack( side = "right", fill = "y" )
self.lb.pack( side = "left", fill = "both", expand = 1 )
def current_index( self ):
indices = self.lb.curselection()
assert( len( indices ) <= 1 ) # TODO: about multi-selection.
return None if len( indices ) == 0 else int( indices[0] )
def current( self ):
#return self.lb.get( "active" ) # Incorrect with mousing
i = self.current_index()
return "" if i is None else self.lb.get( i )
def item_count( self ):
return self.lb.size()
def clear( self ):
self.lb.delete( 0, "end" )
def append( self, item ):
return self.lb.insert( "end", item )
def select_item( self, i ):
assert( 0 <= i < self.item_count() )
self.lb.selection_set( i )
Hm, let me steal this line... Thanks!
Cheers,
- Alf
That code evolved a little to cover more Tk listbox quirks (thanks to Ratingrick
for the "activestyle"):
<code>
class UsableListbox( t.Frame ):
def __init__( self, parent_widget ):
t.Frame.__init__( self, parent_widget )
scrollbar = t.Scrollbar( self, orient = "vertical" )
self.lb = t.Listbox(
self,
exportselection = 0, activestyle = "none", selectmode = "browse",
yscrollcommand = scrollbar.set
)
scrollbar.config( command = self.lb.yview )
scrollbar.pack( side = "right", fill = "y" )
self.lb.pack( side = "left", fill = "both", expand = 1 )
def current_index( self ):
indices = self.lb.curselection()
assert( len( indices ) <= 1 ) # TODO: about multi-selection.
return None if len( indices ) == 0 else int( indices[0] )
def item_at( self, i ):
assert( 0 <= i < self.item_count() )
return "" if i is None else self.lb.get( i )
def current( self ):
#return self.lb.get( "active" ) # Incorrect with mousing
return self.item_at( self.current_index() )
def item_count( self ):
return self.lb.size()
def clear( self ):
self.lb.delete( 0, "end" )
def append( self, item ):
self.lb.insert( "end", item )
return self.item_count() - 1
def scroll_into_view( self, i ):
self.lb.see( i )
def select_item( self, i ):
assert( 0 <= i < self.item_count() )
old_i = self.current_index();
self.scroll_into_view( i )
if old_i is not None and old_i == i:
return
self.lb.selection_set( i )
if old_i is not None:
self.lb.selection_clear( old_i )