Welcome, guest | Sign In | My Account | Store | Cart

I provide an alternative listbox for tkinter.

Using this trick you can add horizontal and vertical padding to every item and also a width.

Python, 138 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
try:
    from Tkinter import Label, Frame, Toplevel, Entry
    from Tkconstants import *
    from ttk import Separator
except ImportError:
    from tkinter import Label, Frame, Toplevel, Entry
    from tkinter.constants import *
    from tkinter.ttk import Separator
    
try:
  basestring
except NameError:
  basestring = str

class Listbox(Frame):
    def __init__(self, master, options, width=20, bordercolor="#cccccc", foreground="black", background="white", activebackground="#2780E3", activeforeground="white",padx=15, pady=7, command=None):
        Frame.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=1, bd= 0)

        self._foreground = foreground
        self._background = background
        self._activebackground = activebackground
        self._activeforeground = activeforeground

        self._items = []
        
        index = 0
        for option in options:
            
            if option is None:
                Separator(self, orient=HORIZONTAL).pack(fill=X)
                continue
                
            if isinstance(option, basestring):
                test = option
                value = option
            else:
                text, value = option
            
            label_item = Label(self, width=width, text=text, background=background, foreground="black",  anchor=W, padx=padx, pady=pady)
            label_item.pack(fill=X)
            
            label_item.index = index
            label_item.value = value

            label_item.bind("<Enter>", self._on_enter_label_item)
            label_item.bind("<Leave>", self._on_leave_label_item)
            label_item.bind("<1>", lambda event, index=index:self._on_click_item(event.widget, index))
            self._items.append(label_item)
            
            index += 1
        
        self._actived_item = None
        self._command = command

        self.bind("<Up>", self._on_up)
        self.bind("<Down>", self._on_down)
        self.bind("<Return>", self._on_return)

    def _on_return(self, event):
        if self._command is not None:
             if self._actived_item is not None:
                 self._command(self._actived_item.value)

    def _activate_colors(self, label_item):
        label_item.configure(background=self._activebackground, foreground=self._activeforeground)
    
    def _deactivate_colors(self, label_item):
        label_item.configure(background=self._background, foreground=self._foreground)

    def _on_enter_label_item(self, event):
        label_item = event.widget
        self._activate_colors(label_item)
        
        self._actived_item = label_item

    def _on_leave_label_item(self, event):
        label_item = event.widget
        self._deactivate_colors(label_item)

    def _on_click_item(self, label_item, index):
        if self._actived_item != label_item:
            self._deactivate_colors(self._actived_item)
            self._activate_colors(label_item)
            self._actived_item = label_item
        
        self.focus_set()
        if self._command is not None:
            self._command(label_item.value)

    def _on_up(self, event):
        if self._actived_item is None:
            label_item = self._items[0]
            self._actived_item = label_item
            self._activate_colors(label_item)

        else:
            index = self._actived_item.index
            if index == 0: return
            index -= 1

            self._deactivate_colors(self._actived_item)
            
            self._actived_item = self._items[index]
            self._activate_colors(self._actived_item)

    def _on_down(self, event):
        if self._actived_item is None:
            label_item = self._items[0]
            self._actived_item = label_item
            self._activate_colors(label_item)

        else:
            index = self._actived_item.index
            if index == len(self._items) - 1: return
            
            index += 1
            
            self._deactivate_colors(self._actived_item)
            self._actived_item = self._items[index]
            self._activate_colors(self._actived_item)

if __name__ == "__main__":
    try:
        from Tkinter import Tk
    except ImportError:
        from tkinter import Tk
        
    root = Tk()

    def command(value):
        print("selected value: %s"%value)

    listbox = Listbox(root, options=(("Spain", "es"), None, ("USA", "us"), ("France","fr"), ("Britain", "br")), command=command)
    listbox.pack()

    listbox.focus()

    root.mainloop()