try:
    import Tkinter as tk
    import tkFont
    import ttk
except ImportError:  # Python 3
    import tkinter as tk
    import tkinter.font as tkFont
    import tkinter.ttk as ttk


class MultiColumnListbox(object):
    """Use a ttk.TreeView as a multicolumn ListBox"""

    def __init__(self, headers, data=None, sortable=True, scrollbars_on_overflow=False, autowidth_on_add=False, master=None, **kw):
        self.container = tk.Frame(master)
        self.container.pack(fill="both", expand=True)

        self.tree = ttk.Treeview(master, columns=headers, show="headings", **kw)

        if not scrollbars_on_overflow:
            self.tree.pack(fill="both", expand=True, in_=self.container)
        else:
            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_=self.container)
            vsb.grid(column=1, row=0, sticky='ns', in_=self.container)
            hsb.grid(column=0, row=1, sticky='ew', in_=self.container)
            self.container.grid_columnconfigure(0, weight=1)
            self.container.grid_rowconfigure(0, weight=1)

        for iCol,col in enumerate(headers):
            on_sort = (lambda c=iCol: self.sortby(c)) if sortable else ''
            self.tree.heading(iCol, text=col, command=on_sort)
            self.tree.column(iCol, width=tkFont.Font().measure(col))  # Adjust the column's width to the header string

        self.autowidth_on_add = autowidth_on_add
        if data is not None:
            for item in data:
                self.add_item(item)

    def autowidth(self, new_item):
        for iCol, val in enumerate(new_item):
            if val is None: continue
            col_w = tkFont.Font().measure(val)
            if self.tree.column(iCol, width=None) < col_w:
                self.tree.column(iCol, width=col_w)

    def add_item(self, new_item):
        self.tree.insert('', 'end', values=new_item)
        if self.autowidth_on_add:
            self.autowidth(new_item)

    def sortby(self, iCol, descending=False):
        """sort tree contents when a column header is clicked on"""
        # Grab values to sort
        data = [(self.tree.set(child, iCol), child) for child in self.tree.get_children('')]
        # Sort the data in place
        data.sort(reverse=descending)
        for iRow, item in enumerate(data):
            self.tree.move(item[1], '', iRow)
        # Switch the heading so it will sort in the opposite direction
        self.tree.heading(iCol, command=lambda c=iCol: self.sortby(c, not descending))