Reputation: 777
I have a python tkinter application with two drop downs, and two radio buttons
-The first dropdown for is years
-the two radio buttons are color and navigation
-the third dropdown is region selection
now for every model year, there is a different dictionary of column and navigation. and depending on whether you choose color or navigation the region area will change. Every radio button has it's own keys in the dictionaries. 1 is for color, and 2 is for navigation
i am using trace
to find the changes in the dropdown and in navigation, and i am calling function callback1 if first dropdown changes and callback2 if radio button changes
Here is my code, I left enough comments for the program to be understood:
from Tkinter import *
class App:
def __init__(self, master):
self.radioButtonVariable = IntVar()
self.text = StringVar()
frame = Frame(master, width=500, height=500)
frame.pack_propagate(0) # set the flag to use the size
frame.pack()
#dictionaries for evey year [NOTE: 1 is for Color and 2 is for Navigation]
self.dict17 = {1: ['China', 'Europe', 'North America'],
2: ['Australia', 'China', 'Europe']}
self.dict16 = {1: ['China ', 'Europe ', 'North America '],
2: ['Australia ', 'China ', 'Germany']}
self.dict15 = {1: ['China ', 'Europe-Russia ', 'North America '],
2: ['Australia ', 'New Zealand', 'England', 'Israel Region', 'Latin America Region', 'Middle East Region', 'North America Region', 'Philipines _Region', 'South American _Region', 'Turkey _Region']}
self.dict14 = {1: ['China', 'Europe-Russia ', 'South America '],
2: ['Philipines Region', 'Turkey _Region']}
self.dict = self.dict17.copy() #set default dictionary to 2017
#label for first dropdown
self.MY_LABEL = Label(frame, text="Model Year:", anchor=W)
self.MY_LABEL.grid(row=0, column=1)
self.variable = StringVar(master)
self.variable.set("2014") #set default value to 2014 of dropdown
#create the dropdown of model year
self.w = OptionMenu(frame, self.variable, "2014", "2015", "2016", "2017")
self.w.grid(row=0, column=2)
self.variable.trace("w", self.callback1) #check for changes in the variable and call callback function
#radio buttons
self.COL = Radiobutton(frame, text="Color", variable=self.radioButtonVariable, value=1)
self.COL.grid(row=1, column=2)
self.NAV = Radiobutton(frame, text="Navigation", variable=self.radioButtonVariable, value=2)
self.NAV.grid(row=1, column=3)
#region label
self.REGION_LABEL = Label(frame, text="Region:", anchor=W)
self.REGION_LABEL.grid(row=2,column=1)
#check for changes in the radio buttons and call callback function 2
self.radioButtonVariable.trace("w", self.callback2)
self.variable2 = StringVar(master)
#create second dropdown
self.w2 = OptionMenu(frame, self.variable2, "")
self.w2.grid(row=2, column=2)
#this function clears the deault dictionary and copies a new one to it (SOMETHING IS WRONG HERE)
def callback1(self, *args):
module = self.variable.get()
if module == "2017":
self.dict.clear()
self.dict = self.dict17.copy()
elif module == "2016":
self.dict.clear()
self.dict = self.dict16.copy()
elif module == "2015":
self.dict.clear()
self.dict == self.dict15.copy()
else:
self.dict.clear()
self.dict == self.dict14.copy()
#this function calls the second callback function and changes the dropdown the the new dictionary that is set
def callback2(self, *args):
countries = self.dict[self.radioButtonVariable.get()]
self.variable2.set(countries[0])
menu = self.w2["menu"]
menu.delete(0, 'end')
for country in countries:
menu.add_command(label=country, command=lambda nation=country: self.variable2.set(nation))
root = Tk()
root.wm_title("application")
app = App(root)
root.mainloop()
ERROR:
Now this program works absolutely fine if the first choice in the dropdown is the default one (2017). So if you switch between color and navigation the actual values of the dropdown in region will change
However once you select another year, like 2015, Regions won't change and I get this error:
line 81, in callback2
countries = self.dict[self.radioButtonVariable.get()]
KeyError: 1
I believe it is because I am clearing the dictioanry and copying a new one, however it seems the new dictionary is not being copied, so it is not finding the correct keys. so problem is in function callback2
Any solution to that problem ?
Upvotes: 1
Views: 1321
Reputation: 4679
As the regions depend on both, the year and the radio button selection, you need only one callback which uses both informations to populate the regions dropdown widget. This callback method could also be called from __init__()
to select the dictionary the first time taking into account the actual year selected to prevent errors like in your code where you put '2014' into the dropdown but copy the dictionary for 2017.
Holding the information for each year in a dictionary keyed by year simplifies the code and makes it more robust against changes of the data.
Here's an updated version of your code (also with better names and without unnecessary attributes on the application instance):
#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import Tkinter as tk
from functools import partial
COLOR, NAVIGATION = 1, 2
class Application(object):
def __init__(self, master):
frame = tk.Frame(master)
frame.pack()
self.year2data = {
2017: {
COLOR: ['China', 'Europe', 'North America'],
NAVIGATION: ['Australia', 'China', 'Europe'],
},
2016: {
COLOR: ['China ', 'Europe ', 'North America '],
NAVIGATION: ['Australia ', 'China ', 'Germany'],
},
2015: {
COLOR: ['China ', 'Europe-Russia ', 'North America '],
NAVIGATION: [
'Australia ', 'New Zealand', 'England', 'Israel Region',
'Latin America Region', 'Middle East Region',
'North America Region', 'Philipines _Region',
'South American _Region', 'Turkey _Region'
],
},
2014: {
COLOR: ['China', 'Europe-Russia ', 'South America '],
NAVIGATION: ['Philipines Region', 'Turkey _Region'],
},
}
self.current_data = None
tk.Label(frame, text='Model Year:', anchor=tk.E).grid(row=0, column=1)
years = sorted(self.year2data.iterkeys())
self.model_year_var = tk.IntVar(master)
self.model_year_var.set(years[0])
tk.OptionMenu(frame, self.model_year_var, *years).grid(
row=0, column=2, columnspan=2
)
self.model_year_var.trace('w', self.update_region)
self.color_or_navigation_var = tk.IntVar()
tk.Radiobutton(
frame, text='Color', variable=self.color_or_navigation_var, value=1
).grid(row=1, column=2)
tk.Radiobutton(
frame,
text='Navigation',
variable=self.color_or_navigation_var,
value=2,
).grid(row=1, column=3)
self.color_or_navigation_var.trace('w', self.update_region)
tk.Label(frame, text='Region:', anchor=tk.E).grid(row=2, column=1)
self.region_var = tk.StringVar(master)
self.region_dropdown = tk.OptionMenu(frame, self.region_var, '')
self.region_dropdown.grid(row=2, column=2, columnspan=2)
self.update_region()
def update_region(self, *_args):
self.current_data = self.year2data[self.model_year_var.get()]
color_or_navigation = self.color_or_navigation_var.get()
if color_or_navigation != 0:
regions = self.current_data[color_or_navigation]
menu = self.region_dropdown['menu']
menu.delete(0, tk.END)
for region in regions:
menu.add_command(
label=region, command=partial(self.region_var.set, region)
)
if self.region_var.get() not in regions:
self.region_var.set(regions[0])
def main():
root = tk.Tk()
root.title('application')
_ = Application(root)
root.mainloop()
if __name__ == '__main__':
main()
Upvotes: 1