The odyssey of working with Tkinter comboboxes

Python

My first challenge after starting to work with Tkinter has arrived, and it’s … the combobox control (the case is similar to Listbox), and …… I must admit I am seriously disappointed with this widget, basicaly because I cannot assign a identifier as a response after a selection, I mean, I cannot insert a value and its identifier on the widget. The widget only admits lists, becuase an internal list handle everything.

In my opinion, this behaviour is not practical. Why not use dictionaries, at least? (This was my first question, where dictionaries has unique keys. I hope I could work more or less in a way similar to VBA or VB6, although I hoped to work as I did using XAML and C# (really easy!!).

Combobox en Tkinter

Combobox en Tkinter

Comboboxes under VBA, VB6 y C#

A long time ago, I wrote a post with the differences of working with combos in VBA and VB6 (combos en VB6 y VBA, in spanish). They are not perfect but very functional. To load the options, you can do it with a loop or a for, you asign an identifier to every option, that it’s different with the value showwed.

When working with C# and XAML, using comboboxes or Listboxes was really ease, because you can attach a collection of objects to the control, you tell the control what property is the key and what is the value (always a string value, of course) and you’re done! The SelectItem event give you the object selected (not only the key, but the full object!!). Any modification in the collection also update the combo or listbox … and this way it’s very easy to work against databases.

Tkinter comboboxes

As you know, I am working with Tkinter as a graphical library for a short time, and my first reference is the book Tkinter GUI Application Development, already reviewd here on manejandodatos.es. But sometimes, things are not as you like, and that’s my problem with combobox (and Listboxes). I will be surprised if Tkinter widgets would work with list of objects, as you can do with C#, or in the worst of the cases, as VBA and VB6 does …. but Tkinter is different.

Tkinter also describe the situation crearly here (http://www.tkdocs.com/tutorial/morewidgets.html#listbox), in “Keep Extra Item Data“, confirming that it’s NOT POSSIBLE to handle addicional informacion of every option, and if you want to do it, you need to do it off-side the widget. In my way of solving the problem, I use dictionaries:

dictionary = dict(zip(descriptor, identifier))

where descriptor and identificador are the two list of the same lenght that you need, and where the n item of both list is linked.

You need also thus function to deal with dictionaries:


def find_key(dic, val):
"""return the key of dictionary dic given the value"""
return [k for k, v in dic.iteritems() if v == val][0]

and what it does is to return the keys that correspond to the values are igual to “val”.

Now, let’s create an example to see how it works.


#-------------------------------------------------------------------------------
# Name:        Ejemplos Tkinter - Combos
# Copyright:   (c) David Trillo Montero 2014 - Manejando datos
#-------------------------------------------------------------------------------

miVersion = "Ejemplo Combos - version 0.1.0"
from Tkinter import *
import ttk

class colores():
def __init__(self, id, color):
self.id = id
self.color = color
def __str__(self):
return self.color

class Aplicacion(Frame):
def __init__(self, master):
Frame.__init__(self, master=None)
self.master.title(miVersion)
self.idcolor = StringVar(value=1)
# Cemporadas
Label(self.master, text="Colores").grid(row=1, sticky=W)
self.cbotemporadas = ttk.Combobox(self.master, textvariable=self.idcolor, state="readonly" )
self.cbotemporadas.grid(row=1, sticky=E, column=1, columnspan=3)
self.cbotemporadas.bind("<<ComboboxSelected>>", self.selecciona)
self.valores = self._cargaFromObject(o_colores, self.cbotemporadas, "color", "id", seleccionado, self.idcolor)

def selecciona(self, event):
val = self.idcolor.get()
print self.valores[val]

def _cargaFromObject(self, coleccion, objeto, campodesc, campoid, val2select, variable):
misc , misv = [] , []
for vv in coleccion:
misv.append(getattr(vv,campodesc))
misc.append(getattr(vv,campoid))
if getattr(vv,campoid) == val2select: variable.set(getattr(vv,campodesc))  # Selección de Quiniela
objeto["values"] = misv
return dict(zip(misv, misc))    # Crea diccionario

def creavalores():
c = colores(1,"rojo")
o_colores.append(c)
c = colores(2,"verde")
o_colores.append(c)
c = colores(3,"amarillo")
o_colores.append(c)

o_colores = []
seleccionado = 2
if __name__ == '__main__':
creavalores()
root = Tk()
a = Aplicacion(root)

root.mainloop()

And running:

Tkinter combo

Tkinter combo

When you modify your selection, on the consol, you’ll see the ID of the element selected. Of course, the source code is available on GitHub.

I hope the article will be of your interest, and let’s hope that future Tkinter versions break this rudimentary way of working with combos and listboxes! Have a nice day!