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

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.

Python, 212 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
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.