Listboxes and Scrollbars

Listbox widgets allow you to display a list of items for selection, and Scrollbarsare designed for navigating through the contents of other widgets. Because it is common to use these widgets together, let's study them both at once. Example 8-9 builds both a Listbox and a Scrollbar, as a packaged set.

Example 8-9. PP2EGuiTourscrolledlist.py

from Tkinter import * 

class ScrolledList(Frame):
 def __init__(self, options, parent=None):
 Frame.__init__(self, parent)
 self.pack(expand=YES, fill=BOTH) # make me expandable
 self.makeWidgets(options)
 def handleList(self, event):
 index = self.listbox.curselection() # on list double-click
 label = self.listbox.get(index) # fetch selection text
 self.runCommand(label) # and call action here
 def makeWidgets(self, options): # or get(ACTIVE)
 sbar = Scrollbar(self)
 list = Listbox(self, relief=SUNKEN)
 sbar.config(command=list.yview) # xlink sbar and list
 list.config(yscrollcommand=sbar.set) # move one moves other
 sbar.pack(side=RIGHT, fill=Y) # pack first=clip last
 list.pack(side=LEFT, expand=YES, fill=BOTH) # list clipped first
 pos = 0
 for label in options: # add to list-box
 list.insert(pos, label) # or insert(END,label)
 pos = pos + 1
 #list.config(selectmode=SINGLE, setgrid=1) # select,resize modes
 list.bind('', self.handleList) # set event handler
 self.listbox = list
 def runCommand(self, selection): # redefine me lower
 print 'You selected:', selection

if __name__ == '__main__':
 options = map((lambda x: 'Lumberjack-' + str(x)), range(20))
 ScrolledList(options).mainloop()

This module can be run standalone to experiment with these widgets, but is also designed to be useful as a library object. By passing in different selection lists to the options argument and redefining the runCommand method in a subclass, the ScrolledList component class defined here can be reused any time you need to display a scrollable list. With just a little forethought, it's easy to extend the Tkinter library with Python classes.

When run standalone, this script generates the window shown in Figure 8-12. It's a Frame, with a Listbox on its left containing 20 generated entries (the fifth has been clicked), along with an associated Scrollbar on its right for moving through the list. If you move the scroll, the list moves, and vice versa.

Figure 8-12. scrolledlist at the top

figs/ppy2_0812.gif

8.3.1 Programming Listboxes

Listboxes are straightforward to use, but they are populated and processed in somewhat unique ways compared to the widgets we've seen so far. Many listbox calls accept a passed-in index to refer to an entry in the list. Indexes start at integer and grow higher, but Tkinter also accepts special name strings in place of integer offsets -- "end" to refer to the end of the list, "active" to denote the line selected, and more. This generally yields more than one way to code listbox calls.

For instance, this script adds items to the listbox in this window by calling its insert method, with successive offsets (starting at zero):

list.insert(pos, label)
pos = pos + 1

But you can also fill a list by simply adding items at the end without keeping a position counter at all, with either of these statements:

list.insert('end', label) # add at end: no need to count positions
list.insert(END, label) # END is preset to 'end' inside Tkinter

The listbox widget doesn't have anything like the command option we use to register callback handlers for button presses, so you either need to fetch listbox selections while processing other widgets' events (e.g., a button press elsewhere in the GUI), or tap into other event protocols to process user selections. To fetch a selected value, this script binds the left mouse button double-click event to a callback handler method with bind (seen earlier on this tour).

In the double-click handler, this script grabs the selected item out of the listbox with this pair of listbox method calls:

index = self.listbox.curselection() # get selection index
label = self.listbox.get(index) # fetch text by its index

Here, too, you can code this differently. Either of the following lines have the same effect; they get the contents of the line at index "active" -- the one selected:

label = self.listbox.get('active') # fetch from active index
label = self.listbox.get(ACTIVE) # ACTIVE='active' in Tkinter

For illustration purposes, the class's default runCommand method prints the value selected each time you double-click an entry in the list -- as fetched by this script, it comes back as a string reflecting the text in the selected entry:

C:...PP2EGuiTour>python scrolledlist.py
You selected: Lumberjack-2
You selected: Lumberjack-19
You selected: Lumberjack-4
You selected: Lumberjack-12

8.3.2 Programming Scrollbars

The deepest magic in this script, though, boils down to two lines of code:

sbar.config(command=list.yview) # call list.yview when I move
list.config(yscrollcommand=sbar.set) # call sbar.set when I move

The scrollbar and listbox are effectively cross-linked to each other through these configuration options; their values simply refer to bound widget methods of the other. By linking like this, Tkinter automatically keeps the two widgets in sync with each other as they move. Here's how this works:

  • Moving a scrollbar invokes the callback handler registered with its command option. Here, list.yview refers to a built-in listbox method that adjusts the listbox display proportionally, based on arguments passed to the handler.
  • Moving a listbox vertically invokes the callback handler registered with its yscrollcommand option. In this script, the sbar.set built-in method adjusts a scrollbar proportionally.

In other words, moving one automatically moves the other. It turns out that every scrollable object in Tkinter -- Listbox, Entry, Text, and Canvas -- has built-in yview and xview methods to process incoming vertical and horizontal scroll callbacks, as well as yscrollcommand and xscrollcommand options for specifying an associated scrollbar's callback handler. Scrollbars all have a command option, to name a handler to call on moves. Internally, Tkinter passes information to all these methods that specifies their new position (e.g., "go 10% down from the top"), but your scripts need never deal with that level of detail.

Because the scrollbar and listbox have been cross-linked in their option settings, moving the scrollbar automatically moves the list, and moving the list automatically moves the scrollbar. To move the scrollbar, either drag the solid part or click on its arrows or empty areas. To move the list, click on the list and move the mouse pointer above or below the listbox without releasing the mouse button. In both cases, the list and scrollbar move in unison. Figure 8-13 is the scene after moving down a few entries in the list, one way or another.

Figure 8-13. scrolledlist in the middle

figs/ppy2_0813.gif

8.3.3 Packing Scrollbars

Finally, remember that widgets packed last are always clipped first when a window is shrunk. Because of that, it's important to pack scrollbars in a display as soon as possible, so that they are the last to go when the window becomes too small for everything. You can generally make due with less than complete listbox text, but the scrollbar is crucial for navigating through the list. As Figure 8-14 shows, shrinking this script's window cuts out part of the list, but retains the scrollbar.

Figure 8-14. scrolledlist gets small

figs/ppy2_0814.gif

At the same time, you don't generally want a scrollbar to expand with a window, so be sure to pack it with just a fill=Y (or fill=X for a horizontal scroll), and not an expand=YES. Expanding this example's window, for instance, makes the listbox grow along with the window, but keeps the scrollbar attached to the right, and of the same size.

We'll see both scrollbars and listboxes repeatedly in later examples in this and later chapters (flip ahead to PyEdit, PyForm, PyTree, and ShellGui for more examples). And although the example script in this section captures the fundamentals, I should point out that there is more to both scrollbars and listboxes than meets the eye here.

For example, it's just as easy to add horizontal scrollbars to scrollable widgets; they are programmed almost exactly like the vertical one implemented here, but callback handler names start with "x", not "y", and an orient='horizontal' configuration option is set for the scrollbar object (see the later PyEdit and PyTree programs for examples). Listboxes can also be useful input devices even without attached scrollbars; they also accept color, font, and relief configuration options, and support multiple selections (the default is selectmode=SINGLE).

Scrollbars see more kinds of GUI action too -- they can be associated with other kinds of widgets in the Tkinter library. For instance, it is common to attach one to the Text widget; which brings us to the next point of interest on this tour.

Introducing Python

Part I: System Interfaces

System Tools

Parallel System Tools

Larger System Examples I

Larger System Examples II

Part II: GUI Programming

Graphical User Interfaces

A Tkinter Tour, Part 1

A Tkinter Tour, Part 2

Larger GUI Examples

Part III: Internet Scripting

Network Scripting

Client-Side Scripting

Server-Side Scripting

Larger Web Site Examples I

Larger Web Site Examples II

Advanced Internet Topics

Part IV: Assorted Topics

Databases and Persistence

Data Structures

Text and Language

Part V: Integration

Extending Python

Embedding Python

VI: The End

Conclusion Python and the Development Cycle



Programming Python
Python Programming for the Absolute Beginner, 3rd Edition
ISBN: 1435455002
EAN: 2147483647
Year: 2000
Pages: 245

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net