[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

Sample code usable Tkinter listbox

Alf P. Steinbach

3/1/2010 12:30:00 AM

In case Someone Else(TM) may need this.

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
7 Answers

rantingrick

3/1/2010 3:10:00 AM

0

On Feb 28, 6:30 pm, "Alf P. Steinbach" <al...@start.no> wrote:
> In case Someone Else(TM) may need this.
>
> 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.

Thanks for sharing Alf,
Thats works fine "as-is" but what about inheriting from "tk.Listbox"
directly and having the "container frame" as an attribute? I prefer
this API because I hate to write the laborious
"megawidget.mainwidget.methodX()" when i could instead write
"megawidget.methodX()". What is your opinion on this?

Alf P. Steinbach

3/1/2010 3:58:00 AM

0

* rantingrick:
> On Feb 28, 6:30 pm, "Alf P. Steinbach" <al...@start.no> wrote:
>> In case Someone Else(TM) may need this.
>>
>> 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.
>
> Thanks for sharing Alf,
> Thats works fine "as-is" but what about inheriting from "tk.Listbox"
> directly and having the "container frame" as an attribute? I prefer
> this API because I hate to write the laborious
> "megawidget.mainwidget.methodX()" when i could instead write
> "megawidget.methodX()". What is your opinion on this?

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 )

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

rantingrick

3/1/2010 6:41:00 AM

0

On Feb 28, 9:57 pm, "Alf P. Steinbach" <al...@start.no> wrote:
> * rantingrick:

I just hate overriding all the paths to each listbox method. How about
just overideing the Listboxe's geomerty methods once an your done?


#-- start script --#
import Tkinter as tk
from Tkconstants import *
import tkMessageBox as MB

class ScrolledList(tk.Listbox):
def __init__(self, master, **kw):
self.frame = tk.Frame(master)
self.frame.rowconfigure(0, weight=1)
self.frame.columnconfigure(0, weight=1)
self.hbar = tk.Scrollbar(self.frame, orient=HORIZONTAL)
self.block = tk.Frame(self.frame, width=18, height=18)
self.block.grid(row=1, column=1)
self.vbar = tk.Scrollbar(self.frame, orient=VERTICAL)
kw.setdefault('activestyle', 'none')
kw.setdefault('highlightthickness', 0)
if 'pack' in kw.keys() and kw.pop('pack') == 1:
self.frame.pack(fill=BOTH, expand=1)
tk.Listbox.__init__(self, self.frame, **kw)
self.grid(row=0, column=0, sticky=N+S+E+W)
self.hbar.configure(command=self.xview)
self.vbar.configure(command=self.yview)
self.config(
yscrollcommand=self.vbar.set,
xscrollcommand=self.hbar.set
)
self.hbar.grid(row=1, column=0, sticky=W+E)
self.vbar.grid(row=0, column=1, sticky=N+S)

self.pack = lambda **kw: self.frame.pack(**kw)
self.grid = lambda **kw: self.frame.grid(**kw)
self.place = lambda **kw: self.frame.place(**kw)
self.pack_config = lambda **kw: self.frame.pack_config(**kw)
self.grid_config = lambda **kw: self.frame.grid_config(**kw)
self.place_config = lambda **kw: self.frame.place_config(**kw)
self.pack_configure = lambda **kw:
self.frame.pack_config(**kw)
self.grid_configure = lambda **kw:
self.frame.grid_config(**kw)
self.place_configure = lambda **kw:
self.frame.place_config(**kw)

def gets(self):
return self.get(0, END)

def sets(self, arg):
self.delete(0, END)
try:
arg = arg.strip('\n').splitlines()
except AttributeError:
pass
if hasattr(arg, '__getitem__'):
for item in arg:
self.insert(END, str(item))
else:
raise TypeError("Scrolledlist.sets() requires a string of
iterable of strings")


if __name__ == '__main__':
root = tk.Tk()
listbox = ScrolledList(root, width=50, height=5, fg='green',
selectmode='Extended', pack=1)
#listbox.sets(1.25)
#listbox.sets('1\n2\n3\n4\n5\n\n\n')
listbox.sets(range(100))
root.mainloop()
#-- end script --#

Alf P. Steinbach

3/1/2010 12:20:00 PM

0

* rantingrick:
>
> kw.setdefault('activestyle', 'none')

Hm, let me steal this line... Thanks!


Cheers,

- Alf

rantingrick

3/1/2010 5:18:00 PM

0

On Mar 1, 6:19 am, "Alf P. Steinbach" <al...@start.no> wrote:

> >         kw.setdefault('activestyle', 'none')
>
> Hm, let me steal this line... Thanks!

Yes! the default activestyle is quite annoying!

Alf P. Steinbach

3/3/2010 6:51:00 PM

0

* Alf P. Steinbach:
> In case Someone Else(TM) may need this.
>
> 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.


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 )

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

rantingrick

3/6/2010 10:46:00 PM

0

Opps: found a few errors in that last ScrolledListbox class, try this
one...



import Tkinter as tk
from Tkconstants import *

class ScrolledList(tk.Listbox):
def __init__(self, master, **kw):
self.frame = tk.Frame(master)
self.frame.rowconfigure(0, weight=1)
self.frame.columnconfigure(0, weight=1)
self.hbar = tk.Scrollbar(self.frame, orient=HORIZONTAL)
self.block = tk.Frame(self.frame, width=18, height=18)
self.block.grid(row=1, column=1)
self.vbar = tk.Scrollbar(self.frame, orient=VERTICAL)
kw.setdefault('activestyle', 'none')
kw.setdefault('highlightthickness', 0)
kw.setdefault('pack', 0)
if kw.pop('pack') == 1:
self.frame.pack(fill=BOTH, expand=1)
tk.Listbox.__init__(self, self.frame, **kw)
self.grid(row=0, column=0, sticky=N+S+E+W)
self.hbar.configure(command=self.xview)
self.vbar.configure(command=self.yview)
self.config(
yscrollcommand=self.vbar.set,
xscrollcommand=self.hbar.set
)
self.hbar.grid(row=1, column=0, sticky=W+E)
self.vbar.grid(row=0, column=1, sticky=N+S)

self.pack = lambda **kw: self.frame.pack(**kw)
self.grid = lambda **kw: self.frame.grid(**kw)
self.place = lambda **kw: self.frame.place(**kw)
self.pack_config = lambda **kw: self.frame.pack_config(**kw)
self.grid_config = lambda **kw: self.frame.grid_config(**kw)
self.place_config = lambda **kw: self.frame.place_config(**kw)
self.pack_configure = lambda **kw:
self.frame.pack_config(**kw)
self.grid_configure = lambda **kw:
self.frame.grid_config(**kw)
self.place_configure = lambda **kw:
self.frame.place_config(**kw)

def gets(self):
return self.get(0, END)

def sets(self, arg):
self.delete(0, END)
try:
arg = arg.strip('\n').splitlines()
except AttributeError:
pass
if hasattr(arg, '__getitem__'):
for item in arg:
self.insert(END, str(item))
else:
raise TypeError("Scrolledlist.sets() requires a string or
iterable of strings")


if __name__ == '__main__':
root = tk.Tk()
listbox = ScrolledList(root, width=50, height=10, pack=1)
#listbox.sets(1.25)
#listbox.sets('1\n2\n3\n4\n5\n\n\n')
listbox.sets(range(100))
#listbox.grid(row=0, column=0)
root.mainloop()