Tkinter:在MultiListBox中选择多个项目

时间:2015-06-22 13:34:00

标签: python-2.7 tkinter

我无法修改现有的MultiListBox实施,以允许我使用shift键选择多个项目。该类的构造函数是:

class MultiListbox(Frame):
    def __init__(self, master, lists):
        Frame.__init__(self, master)
        self.lists=[]
        for l,w in lists:
            frame = Frame(self); frame.pack(side=LEFT, expand=YES, fill=BOTH)
            Label(frame, text=l, borderwidth=1, relief=RAISED).pack(fill=X)
            lb = Listbox(frame, width=w, borderwidth=0, selectborderwidth=0,
                relief=FLAT, exportselection=FALSE, selectmode=EXTENDED)
            lb.pack(expand=YES, fill=BOTH)
            self.lists.append(lb)
            lb.bind('<B1-Motion>', lambda e, s=self: s._select(e.y))
            lb.bind('<Button-1>', lambda e, s=self: s._select(e.y))
            lb.bind('<Leave>', lambda e: 'break')
            lb.bind('<B2-Motion>', lambda e, s=self: s._b2motion(e.x, e.y))
            lb.bind('<Button-2>', lambda e, s=self: s._button2(e.x, e.y))
        frame = Frame(self); frame.pack(side=LEFT, fill=Y)
        Label(frame, borderwidth=1, relief=RAISED).pack(fill=X)
        sb = Scrollbar(frame, orient=VERTICAL, command=self._scroll)
        sb.pack(expand=YES, fill=Y)

        self.lists[0]['yscrollcommand']=sb.set

即使我设置了selectmode=EXTENDED,也没有扩展选择。

为了支持扩展选择,我需要为multilistbox实现什么功能?

您可以看到multilistbox here的完整实现。

2 个答案:

答案 0 :(得分:2)

我更喜欢使用tgk.TreeView的vegaseat方法,因为所有列都链接到行,这使事情变得更容易。当选择行中的任何列时,对其示例的这种轻微修改将打印id。您确实想要测试/打印id,因为您必须将id转换为行号/列表偏移量,即从self.tree.insert捕获返回值。今晚会更多地考虑多种选择。并尝试使用键而不是按钮。

import sys
if sys.version_info[0] < 3:
    import Tkinter as tk    ## Python 2.x
    import tkFont
    import ttk
else:
    import tkinter as tk    ## Python 3.x
    import tkinter.font as tkFont
    import tkinter.ttk as ttk

'''

ttk_multicolumn_listbox2.py

Python31 includes the Tkinter Tile extension ttk.

Ttk comes with 17 widgets, 11 of which already exist in Tkinter:
Button, Checkbutton, Entry, Frame, Label, LabelFrame, Menubutton,
PanedWindow, Radiobutton, Scale and Scrollbar

he 6 new widget classes are:
Combobox, Notebook, Progressbar, Separator, Sizegrip and Treeview

Here the TreeView widget is configured as a multi-column listbox
with adjustable column width and column-header-click sorting.

Tested with Python 3.1.1 and Tkinter 8.5
'''

class McListBox(object):
    """use a ttk.TreeView as a multicolumn ListBox"""
    def __init__(self, root):
        self.root=root
        self.tree = None
        self._setup_widgets()
        self._build_tree()
        ttk.Button(self.root, text='Exit',
                   command=self.root.quit).grid(row=20)

    def _setup_widgets(self):
        # create a treeview with dual scrollbars
        container = ttk.Frame(self.root)
        container.grid(sticky="nsew")

        self.tree = ttk.Treeview(columns=car_header, show="headings")
        vsb = ttk.Scrollbar(orient="vertical",
            command=self.tree.yview)
        hsb = ttk.Scrollbar(orient="horizontal",
            command=self.tree.xview)
        self.tree.configure(yscrollcommand=vsb.set,
            xscrollcommand=hsb.set)
        self.tree.grid(column=0, row=0, sticky='nsew', in_=container)
        vsb.grid(column=1, row=0, sticky='ns', in_=container)
        hsb.grid(column=0, row=1, sticky='ew', in_=container)

        container.grid_columnconfigure(0, weight=1)
        container.grid_rowconfigure(0, weight=1)
        self.tree.bind("<Button1-Motion>", self.selection)

    def _build_tree(self):
        for col in car_header:
            self.tree.heading(col, text=col.title())
            # adjust the column's width to the header string
            self.tree.column(col,
                width=tkFont.Font().measure(col.title()))

        for item in car_list:
            print self.tree.insert('', 'end', values=item)
            # adjust column's width if necessary to fit each value
            for ix, val in enumerate(item):
                col_w = tkFont.Font().measure(val)
                if self.tree.column(car_header[ix],width=None)<col_w:
                    self.tree.column(car_header[ix], width=col_w)


    def selection(self, event):
        """ gets selected item id and prints them
        """
        id = self.tree.identify_row(event.y)
        print "selection", id


# the test data ...
car_header = ['car', 'repair']
car_list = [
('Hyundai', 'brakes') ,
('Honda', 'light') ,
('Lexus', 'battery') ,
('Benz', 'wiper') ,
('Ford', 'tire') ,
('Chevy', 'air') ,
('Chrysler', 'piston') ,
('Toyota', 'brake pedal') ,
('BMW', 'seat'),
('Audi', 'starter'),
('Fiat', 'shocks'),
('Porsche', 'fuel pump')
]

root = tk.Tk()
root.wm_title("multicolumn ListBox")
mc_listbox = McListBox(root)
root.mainloop()

答案 1 :(得分:1)

不幸的是,看起来你必须自己滚动,至少就我所知。下面的代码不完整,因为它会做两次接受相同选择的事情,但这是我有时间的。没有向下箭头代码,但它与向上箭头代码基本相同。我假设您需要按下按钮才能显示或使用所选项目。下面它们只是在Tkinter关闭后打印出来。

import sys
if sys.version_info[0] < 3:
    import Tkinter as tk    ## Python 2.x
    import tkFont
    import ttk
else:
    import tkinter as tk    ## Python 3.x
    import tkinter.font as tkFont
    import tkinter.ttk as ttk

'''

ttk_multicolumn_listbox2.py

Python31 includes the Tkinter Tile extension ttk.

Ttk comes with 17 widgets, 11 of which already exist in Tkinter:
Button, Checkbutton, Entry, Frame, Label, LabelFrame, Menubutton,
PanedWindow, Radiobutton, Scale and Scrollbar

he 6 new widget classes are:
Combobox, Notebook, Progressbar, Separator, Sizegrip and Treeview

For additional info see the Python31 manual:
http://gpolo.ath.cx:81/pydoc/library/ttk.html

Here the TreeView widget is configured as a multi-column listbox
with adjustable column width and column-header-click sorting.

Tested with Python 3.1.1 and Tkinter 8.5
'''

class McListBox(object):
    """use a ttk.TreeView as a multicolumn ListBox"""
    def __init__(self, root):
        self.root=root
        self.tree = None
        self._setup_widgets()
        self._build_tree()
        ttk.Button(self.root, text='Exit',
                   command=self.root.quit).grid(row=20)
        self.selected_offsets=[]

    def _setup_widgets(self):
        container = ttk.Frame(self.root)
        container.grid(sticky="nsew")

        # create a treeview with dual scrollbars
        self.tree = ttk.Treeview(columns=car_header, show="headings",
                                 selectmode="extended")
        vsb = ttk.Scrollbar(orient="vertical",
            command=self.tree.yview)
        hsb = ttk.Scrollbar(orient="horizontal",
            command=self.tree.xview)
        self.tree.configure(yscrollcommand=vsb.set,
            xscrollcommand=hsb.set)
        self.tree.grid(column=0, row=0, sticky='nsew', in_=container)
        vsb.grid(column=1, row=0, sticky='ns', in_=container)
        hsb.grid(column=0, row=1, sticky='ew', in_=container)

        container.grid_columnconfigure(0, weight=1)
        container.grid_rowconfigure(0, weight=1)
        self.tree.bind("<Shift-Up>", self.shift_up_arrow)
        self.tree.bind("<Up>", self.up_arrow)

    def _build_tree(self):
        for col in car_header:
            self.tree.heading(col, text=col.title())
            # adjust the column's width to the header string
            self.tree.column(col,
                width=tkFont.Font().measure(col.title()))

        self.item_id=[]
        for item in car_list:
            self.item_id.append(self.tree.insert('', 'end', values=item))  ## store tkinter id
            # adjust column's width if necessary to fit each value
            for ix, val in enumerate(item):
                col_w = tkFont.Font().measure(val)
                if self.tree.column(car_header[ix],width=None)<col_w:
                    self.tree.column(car_header[ix], width=col_w)

        ## set the focus in the middle for testing
        new_id=self.item_id[5]
        self.tree.focus_set()       ## sets focus to the treeview
        self.tree.selection_set((new_id, new_id))  ## updates background
        self.tree.focus(new_id)     ## sets new id as focus

    def shift_up_arrow(self, event):
        """ gets selected item(s) and stores them in a list
        """
        id_selected=self.tree.focus()
        for offset, id in enumerate(self.item_id):
##            print offset, id
            if id==id_selected:
                self.selected_offsets.append(offset)## save selection
                print offset, car_list[offset]

                ## change background color
                ## you could also give each row a unique tag and
                ## tag_configure the selected row's tag
                self.tree.delete(id_selected)
                self.tree.insert('', offset, id_selected,
                                 values=car_list[offset], tags=("ABC",))
                self.tree.tag_configure("ABC", background='yellow')

                new_id=self.item_id[0]
                if offset > 0:
                    new_id=self.item_id[offset]
                self.tree.focus_set()       ## sets focus to the treeview
                self.tree.selection_set((new_id, new_id))  ## updates background
                self.tree.focus(new_id)     ## sets new id as focus
                return

    def up_arrow(self, event):
        id_selected=self.tree.focus()
        for offset, id in enumerate(self.item_id):
            if id==id_selected and offset > 0:
                new_id=self.item_id[offset]
                self.tree.focus_set()       ## sets focus to the treeview
                self.tree.selection_set((new_id, new_id))  ## updates background
                self.tree.focus(new_id)     ## sets new id as focus

# the test data ...
car_header = ['car', 'repair']
car_list = [
('Hyundai', 'brakes') ,
('Honda', 'light') ,
('Lexus', 'battery') ,
('Benz', 'wiper') ,
('Ford', 'tire') ,
('Chevy', 'air') ,
('Chrysler', 'piston') ,
('Toyota', 'brake pedal') ,
('BMW', 'seat'),
('Audi', 'starter'),
('Fiat', 'shocks'),
('Porsche', 'fuel pump')
]

root = tk.Tk()
root.wm_title("multicolumn ListBox")
mc_listbox = McListBox(root)
root.mainloop()
##
## print selected items
print "Selections %s" %  ("-"*50)
for offset in mc_listbox.selected_offsets:
    print offset, car_list[offset]