Welcome, guest | Sign In | My Account | Store | Cart
# Author: Miguel Martinez Lopez
# Uncomment the next line to see my email
# print("Author's email: %s"%"61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex"))


try:
    from Tkinter import Frame, Listbox
    from ttk import Scrollbar
    from Tkconstants import *
except ImportError:
    from tkinter import Frame, Listbox
    from tkinter.ttk import Scrollbar
    from tkinter.constants import *


def make_autoscroll(sbar, first, last):
    """Hide and show scrollbar as needed."""
    first, last = float(first), float(last)
    if first <= 0 and last >= 1:
        sbar.grid_remove()
    else:
        sbar.grid()
    sbar.set(first, last)

def bisect(list_of_items, item, ascending_order=True, ignore_case=False):    
    lo = 0
    hi = len(list_of_items)

    # I repeat a little bit myself here because I want to be more efficient.
    if ascending_order:
        if ignore_case:
            item = item.lower()
            while lo < hi:
                mid = (lo+hi)//2

                if item < list_of_items[mid].lower(): hi = mid
                else: lo = mid+1
        else:
            while lo < hi:
                mid = (lo+hi)//2

                if item < list_of_items[mid]: hi = mid
                else: lo = mid+1
    else:
        if ignore_case:
            item = item.lower()
            while lo < hi:
                mid = (lo+hi)//2

                if item > list_of_items[mid].lower(): hi = mid
                else: lo = mid+1
        else:
            while lo < hi:
                mid = (lo+hi)//2

                if item > list_of_items[mid]: hi = mid
                else: lo = mid+1
    return lo


class Ordered_Listbox(Frame):
    def __init__(self, master, data=None, ascending_order = True, ignore_case=False, autoscroll=False, vscrollbar=True, hscrollbar=False, scrollbar_background=None, scrollbar_troughcolor=None, **kwargs):
        Frame.__init__(self, master)

        self._ignore_case = ignore_case
        self._ascending_order = ascending_order

        master.grid_rowconfigure(0, weight=1)
        master.grid_columnconfigure(0, weight=1)

        self._listbox = Listbox(self, *kwargs)        
        self._listbox.grid(row=0, column=0, sticky= N+E+W+S)

        scrollbar_kwargs = {}
        if scrollbar_background is not None:
            scrollbar_kwargs["background"] = scrollbar_background
            
        if scrollbar_troughcolor is not None:
            scrollbar_kwargs["throughcolor"] = scrollbar_troughcolor

        if vscrollbar:
            self._vbar=Scrollbar(self,takefocus=0, command=self._listbox.yview, **scrollbar_kwargs)
            self._vbar.grid(row=0, column=1, sticky= N+S)
            
            if autoscroll:
                self._listbox.config(yscrollcommand=lambda f, l: make_autoscroll(self._vbar, f, l))
            else:
                self._listbox.config(yscrollcommand=self._vbar.set)

        if hscrollbar:
            self._hbar=Scrollbar(self,takefocus=0, command=self._listbox.xview, **scrollbar_kwargs)
            self._hbar.grid(row=0, column=1, sticky= E+W)
            
            if autoscroll:
                self._listbox.config(xscrollcommand=lambda f, l: make_autoscroll(self._hbar, f, l))
            else:
                self._listbox.config(xscrollcommand=self._hbar.set)

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

    def add_item(self, item):
        list_of_items = self._listbox.get(0, END)

        index = bisect(list_of_items, item, ignore_case=self._ignore_case, ascending_order=self._ascending_order)
        self._listbox.insert(index, item)

    def delete_item(self, item):
        list_of_items = self._listbox.get(0, END)
        index = bisect(list_of_items, item, ignore_case=self._ignore_case, ascending_order=self._ascending_order)
        self._listbox.delete(index-1)
        
    def selected_items(self):
        list_of_items = []

        for index in self._listbox.curselection():
            list_of_items.append(self._listbox.get(index))
            
        return list_of_items
        
    def selected_item(self):
        return self._listbox.curselection()[0]

    def deselect_all(self):
        self._listbox.selection_clear(0, END)
        
    def select(self, item):
        index = self.index(item)
        
        if index is None:
            return
        
        self._listbox.selection_set(index)

    def deselect(self, item):
        index = self.index(item)
        
        if index is None:
            return
        
        self._listbox.selection_clear(index)

    def index(self, item):
        list_of_items = self._listbox.get(0, END)

        try:
            index = list_of_items.index(item)
        except ValueError:
            return None

        return index

    def bind(self, event, handler):
        self._listbox.bind(event, handler)
    
    def clear(self):
        self._listbox.delete(1,END)

    def __iter__(self):
        return self.items
    
    @property
    def items(self):
        return self._listbox.get(0, END)


if __name__ == "__main__":
    try:
        from Tkinter import Tk, Label
    except ImportError:
        from tkinter import Tk, Label
        
    root = Tk()
    list_of_cities = ["New York", "London", "mADrid", "Tokyo", "caRaCas", "Ottawa", "PraGUE", "Cairo", "Moscow", "Quito", "LuxEMbourg", "Mexico City"]

    for ascending_order in (True, False):
        for ignore_case in (True, False):
            Label(root, text="ascending_order= %s, ignore_case= %s"%(ascending_order, ignore_case)).pack()
            ordered_listbox = Ordered_Listbox(root, data=list_of_cities, ascending_order=ascending_order, ignore_case=ignore_case)
            ordered_listbox.pack(pady=(0,6))
    

    ordered_listbox.add_item("Rome")
    ordered_listbox.add_item("Paris")

    ordered_listbox.select("Rome")
    ordered_listbox.pack()

    root.mainloop()

Diff to Previous Revision

--- revision 5 2017-02-11 18:51:40
+++ revision 6 2017-02-11 19:01:52
@@ -64,6 +64,9 @@
 
         self._ignore_case = ignore_case
         self._ascending_order = ascending_order
+
+        master.grid_rowconfigure(0, weight=1)
+        master.grid_columnconfigure(0, weight=1)
 
         self._listbox = Listbox(self, *kwargs)        
         self._listbox.grid(row=0, column=0, sticky= N+E+W+S)

History