pythonware.com | products ::: library ::: search ::: daily Python-URL! |
PatternsPatternsWhen you first create the listbox, it is empty. The first thing to do is usually to insert one or more lines of text. The insert method takes an index and a string to insert. The index is usually an item number (0 for the first item in the list), but you can also use some special indexes, including ACTIVE, which refers to the "active" item (set when you click on an item, or by the arrow keys), and END, which is used to append items to the list. listbox = Listbox(master) listbox.insert(END, "a list entry") for item in ["one", "two", "three", "four"]: listbox.insert(END, item) To remove items from the list, use the delete method. The most common operation is to delete all items in the list (something you often need to do when updating the list). listbox.delete(0, END) listbox.insert(END, newitem) You can also delete individual items. In the following example, a separate button is used to delete the ACTIVE item from a list. lb = Listbox(master) b = Button(master, text="Delete", command=lambda lb=lb: lb.delete(ANCHOR)) The listbox offers four different selection modes through the selectmode option. These are SINGLE (just a single choice), BROWSE (same, but the selection can be moved using the mouse), MULTIPLE (multiple item can be choosen, by clicking at them one at a time), or EXTENDED (multiple ranges of items can be chosen, using the Shift and Control keyboard modifiers). The default is BROWSE. Use MULTIPLE to get "checklist" behavior, and EXTENDED when the user would usually pick only one item, but sometimes would like to select one or more ranges of items. lb = Listbox(selectmode=EXTENDED) To query the selection, use curselection method. It returns a list of item indexes, but a bug in Tkinter 1.101 (Python 1.5.1) and earlier versions causes this list to be returned as a list of strings, instead of integers. This will most likely be fixed in later versions of Tkinter, so you should make sure that your code is written to handle either case. Here's one way to do that: items = list.curselection() try: items = map(int, items) except ValueError: pass In versions before Python 1.5, use string.atoi of int. Use the get method to get the list item corresponding to a given index. You can also use a listbox to represent arbitrary Python objects. In the next example, we assume that the input data is represented as a list of tuples, where the first item in each tuple is the string to display in the list. For example, you could display a dictionary by using the items method to get such a list. self.lb.delete(0, END) # clear for key, value in data: self.lb.insert(END, key) self.data = data When querying the list, simply fetch the items indexed by the selection list: items = self.lb.curselection() try: items = map(string.atoi, items) except ValueError: pass items = map(lambda i,d=self.data: d[i], items) Unfortunately, the listbox doesn't provide a command option allowing you to track changes to the selection. The standard solution is to bind a double-click event to the same callback as the OK (or Select, or whatever) button. This allows the user to either select an alternative as usual, and click OK to carry out the operation, or to select and carry out the operation in one go by double-clicking on an alternative. This solution works best in BROWSE and EXTENDED modes. lb.bind("<Double-Button-1>", self.ok) If you wish to track arbitrary changes to the selection, you can either rebind the whole bunch of selection related events (see the Tk manual pages for a complete list of Listbox event bindings), or, much easier, poll the list using a timer: def __init__(self, master): self.list = Listbox(selectmode=EXTENDED) self.list.pack() self.current = None self.poll() # start polling the list def poll(self): now = self.list.curselection() if now != self.current: self.list_has_changed(now) self.current = now self.after(250, self.poll) By default, the selection is exported via the X selection mechanism (or the clipboard, on Windows). If you have more than one listbox on the screen, this really messes things up for the poor user. If she selects something in one listbox, and then selects something in another, the original selection disappears. It is usually a good idea to disable this mechanism in such cases. In the following example, three listboxes are used in the same dialog: b1 = Listbox(exportselection=0) for item in families: b1.insert(END, item) b2 = Listbox(exportselection=0) for item in fonts: b2.insert(END, item) b3 = Listbox(exportselection=0) for item in styles: b3.insert(END, item) The listbox itself doesn't include a scrollbar. Attaching a scrollbar is pretty straightforward. Simply set the xscrollcommand and yscrollcommand options of the listbox to the set method of the corresponding scrollbar, and the command options of the scrollbars to the corresponding xview and yview methods in the listbox. Also remember to pack the scrollbars before the listbox. In the following example, only a vertical scrollbar is used. For more examples, see pattern section in the Scrollbar description. frame = Frame(master) scrollbar = Scrollbar(frame, orient=VERTICAL) listbox = Listbox(frame, yscrollcommand=scrollbar.set) scrollbar.config(command=listbox.yview) scrollbar.pack(side=RIGHT, fill=Y) listbox.pack(side=LEFT, fill=BOTH, expand=1) With some more trickery, you can use a single vertical scrollbar to scroll several lists in parallel. This assumes that all lists have the same number of items. Also note how the widgets are packed in the following example. def __init__(self, master): scrollbar = Scrollbar(master, orient=VERTICAL) self.b1 = Listbox(master, yscrollcommand=scrollbar.set) self.b2 = Listbox(master, yscrollcommand=scrollbar.set) scrollbar.config(command=self.yview) scrollbar.pack(side=RIGHT, fill=Y) self.b1.pack(side=LEFT, fill=BOTH, expand=1) self.b2.pack(side=LEFT, fill=BOTH, expand=1) def yview(self, *args): apply(self.b1.yview, args) apply(self.b2.yview, args) |