This recipe shows how to create linked OptionMenus in Tkinter. That is, how to ensure that when you select an item in the first list, the list contents change in the second list, which changes the contents in the third list. While this code is for Pmw OptionMenus, it should be easily adaptable to other types of picklist or radiobutton widgets.
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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | #file linkedOptionMenus.py
#Stewart Midwinter, stewart 'at' midwinter 'dot' ca
#thanks to Eric Brunel and Peter Otten
#use and enjoy!
title = 'linked OptionMenus'
# Import Pmw from this directory tree.
import sys
sys.path[:0] = ['../../..']
import Tkinter
import Pmw, re
global continentList, countryList, stateList
continentList = ['N.America','C. America', 'S. America']
countryList = {'N.America':['Canada','USA','Mexico'],
'C. America':['Guatemala','Nicaragua','Panama'],
'S. America':['Venezuela','Colombia','Ecuador']
}
stateList = {'Canada':['BC','Alberta','Saskatchewan','Manitoba','Ontario','Quebec',
'New Brunswick','Nova Scotia','Prince Edward Island','Newfoundland',
'Nunavut','Northwest Territory'],
'USA':['California','Oregon','Washington','others'],
'Mexico':['Aguascalientes', 'Baja California', 'Baja California Sur',
'Campeche', 'Chiapas', 'Chihuahua', 'Coahuila de Zaragoza', 'Colima',
'Distrito Federal', 'Durango', 'Guanajuato', 'Guerrero', 'Hidalgo',
'Jalisco', 'Mexico', 'Michoacan de Ocampo', 'Morelos', 'Nayarit',
'Nuevo Leon', 'Oaxaca', 'Puebla', 'Queretaro de Arteaga', 'Quintana Roo',
'San Luis Potosi', 'Sinaloa', 'Sonora', 'Tabasco', 'Tamaulipas', 'Tlaxcala',
'Veracruz-Llave', 'Yucatan', 'Zacatecas'],
'Guatemala':['Alta Verapaz', 'Baja Verapaz', 'Chimaltenango',
'Chiquimula', 'El Progreso', 'Escuintla', 'Guatemala',
'Huehuetenango', 'Izabal', 'Jalapa', 'Jutiapa', 'Peten',
'Quetzaltenango', 'Quiche', 'Retalhuleu', 'Sacatepequez',
'San Marcos', 'Santa Rosa', 'Solola', 'Suchitepequez',
'Totonicapan', 'Zacapa'],
'Nicaragua':['Boaco', 'Carazo', 'Chinandega', 'Chontales', 'Esteli', 'Granada',
'Jinotega', 'Leon', 'Madriz', 'Managua', 'Masaya', 'Matagalpa', 'Nueva Segovia',
'Rio San Juan', 'Rivas', 'Atlantico Norte*', 'Atlantico Sur*'],
'Panama':['Bocas del Toro', 'Chiriqui', 'Cocle', 'Colon', 'Darien',
'Herrera', 'Los Santos', 'Panama', 'San Blas*', 'Veraguas'],
'Venezuela':['Amazonas', 'Anzoategui', 'Apure', 'Aragua', 'Barinas', 'Bolivar',
'Carabobo', 'Cojedes', 'Delta Amacuro', 'Falcon', 'Guarico', 'Lara', 'Merida',
'Miranda', 'Monagas', 'Nueva Esparta', 'Portuguesa', 'Sucre',
'Tachira', 'Trujillo', 'Vargas', 'Yaracuy', 'Zulia'],
'Colombia':['Amazonas', 'Antioquia', 'Arauca', 'Atlantico',
'Bolivar', 'Boyaca', 'Caldas', 'Caqueta', 'Casanare',
'Cauca', 'Cesar', 'Choco', 'Cordoba', 'Cundinamarca',
'Guainia', 'Guaviare', 'Huila', 'La Guajira', 'Magdalena',
'Meta', 'Narino', 'Norte de Santander', 'Putumayo', 'Quindio',
'Risaralda', 'San Andres y Providencia',
'Distrito Capital de Santa Fe de Bogota', 'Santander',
'Sucre', 'Tolima', 'Valle del Cauca', 'Vaupes', 'Vichada'],
'Ecuador':['Ecuador states']
}
# default selection
continentItem = continentList[0]
countryItem = countryList[continentItem][0]
stateItem = stateList[countryItem][0]
class selectSystem:
def __init__(self, parent):
# Create and pack the OptionMenu megawidgets.
# The first one has a textvariable.
self.varContinent = Tkinter.StringVar()
self.varCountry = Tkinter.StringVar()
self.varState = Tkinter.StringVar()
self.varContinent.set(continentItem) # N. America
self.varCountry.set(countryItem) # Canada
self.varState.set(stateItem) # B.C.
self.continent_menu = Pmw.OptionMenu(parent,
labelpos = 'w',
label_text = 'Select Continent:',
menubutton_textvariable = self.varContinent,
items = continentList,
menubutton_width = 20,
menubutton_direction = 'flush',
command = self._selectContinent
)
self.continent_menu.pack(anchor = 'w', padx = 10, pady = 10)
self.country_menu = Pmw.OptionMenu (parent,
labelpos = 'w',
label_text = 'Select country:',
menubutton_textvariable = self.varCountry,
items = countryList['N.America'],
menubutton_width = 20,
menubutton_direction = 'flush',
command = self._selectCountry
)
self.country_menu.pack(anchor = 'w', padx = 10, pady = 10)
self.state_menu = Pmw.OptionMenu (parent,
labelpos = 'w',
label_text = 'Select state:',
menubutton_textvariable = self.varState,
items = stateList['Canada'],
menubutton_width = 20,
menubutton_direction = 'flush' ,
command = self._getSelection
)
self.state_menu.pack(anchor = 'w', padx = 10, pady = 10)
menus = (self.continent_menu, self.country_menu, self.state_menu)
Pmw.alignlabels(menus)
# Create the dialog.
self.dialog = Pmw.Dialog(parent,
buttons = ('OK', 'Apply', 'Cancel', 'Help'),
defaultbutton = 'OK',
title = 'Select State',
command = self.execute)
self.dialog.withdraw()
# Add some contents to the dialog.
w = Tkinter.Label(self.dialog.interior(),
text = 'Pmw Dialog\n(put your widgets here)',
background = 'black',
foreground = 'white',
pady = 20)
w.pack(expand = 1, fill = 'both', padx = 4, pady = 4)
def showAppModal(self):
self.dialog.activate(geometry = 'centerscreenalways')
def execute(self, result):
print 'You clicked on', result
if result not in ('Apply', 'Help'):
self.dialog.deactivate(result)
def _getSelection(self, choice):
# Can use 'self.var.get()' instead of 'getcurselection()'.
print 'You have chosen %s : %s : %s' % \
(self.varContinent.get(),
self.varCountry.get(),
self.varState.get() )
print choice # debug
def _selectContinent(self, choice):
## Set appropriate list of countries
countries = countryList[self.varContinent.get()]
self.country_menu.setitems(countries)
## If currently selected country is not in new list, select first valid one
if not self.varCountry.get() in countries:
self.varCountry.set(countries[0])
## Set appropriate list of states
states = stateList[self.varCountry.get()]
self.state_menu.setitems(states)
## If currently selected state is not in list, select first valid one
if not self.varState.get() in states:
self.varState.set(states[0])
def _selectCountry(self, choice):
## Set appropriate list of states
states = stateList[self.varCountry.get()]
self.state_menu.setitems(states)
## If currently selected state is not in list, select first valid one
if not self.varState.get() in states:
self.varState.set(states[0])
def __call__(self):
self.dialog.show()
def indexContinent(name):
found = 'false'
for i in range(len(continentList)):
check = continentList[i]
# print 'checking %s in %s' % (name, check) # debug
if re.search(name,check):
found = 'true'
break
print found
if (found=='true'):
#print 'index of %s is %s' % (name,i) # debug
return i
else:
return -1
def indexCountry(continentindex, name):
found = 'false'
for i in range(len(countryList[continentindex])):
check = countryList[continentindex][i]
# print 'checking %s in %s' % (name, check) # debug
if re.search(name,check):
found = 'true'
break
print found
if (found=='true'):
#print 'index of %s is %s' % (name,i) # debug
return i
else:
return -1
######################################################################
# Create selectSystem in root window for testing.
if __name__ == '__main__':
root = Tkinter.Tk()
Pmw.initialise(root)
root.title(title)
OKButton = Tkinter.Button(root, text = 'OK', command = root.destroy)
OKButton.pack(side = 'bottom')
widget = selectSystem(root)
root.mainloop()
|
Why you would linked lists should become obvious by loading and running the recipe app. In the example, you might want to show a map of a particular state. Since a list of all the states in N. and S. America would be very long, you can use linked lists to allow the user to narrow down the list by first selecting the appropriate continent, then the relevant country, and finally the desired state.