First, a new announcement about my blog. I realized that a whole slew of problems were arising when I posted code here, due to html formatting messing up the ASCII codes of certain characters. As such, I have now installed Enlighter which allows me to post my code more beautifully with some nice syntax highlighting, among other features.
It’s been a crazy week progress-wise. Thanks to some amazing help from the wizard known as Steve, I have been able to get a working skeleton up and running that I can now import all of my data to. Currently, all the program does is create this:
The issue we were tackling was having the option selected in one OptionMenu dynamically alter the options available in another. You can find the finished skeleton below, but before we get to it, I’d like to take a moment to explain some of the more interesting issues that arose during its development.
Let’s start by looking at myFunction(). Originally, it looked something like this:
[python]
def myFunction(*args):
myList = []
if myVar.get() == ‘Fruits’:
for item in fruits.children:
myList.append(item.data)
mySecondMenu.set_menu(‘select’, *myList)
elif myVar.get() == ‘Vegetables’:
for item in vegetables.children:
myList.append(item.data)
mySecondMenu.set_menu(‘select’, *myList)
[/python]A key thing to notice here is *myList. Those little asterisks were something I did not know about until Steve introduced them to me. According to Steve, those little asterisks unpack a list, “so that the elements of the list are passed individually to the function as parameters, as opposed to passing the whole list as a single parameter.” I had been struggling with figuring out how to do that for the whole weekend. Finally fixing it turned out to be quite a relief.
The next big change was turning those for statements into a list comprehension. List comprehensions make the code a little bit easier to read and are more “pythonic.” Here’s what it looked like after:
[python]
def myFunction(*args):
myList = []
if myVar.get() == ‘Fruits’:
myList = [child.data for child in fruits.children]
mySecondMenu.set_menu(‘select’,*myList)
elif myVar.get() == ‘Vegetables’:
myList = [child.data for child in vegetables.children]
mySecondMenu.set_menu(‘select’,*myList)
[/python]Next, I decided to make part of the myfunction() into a method of the Node class. My thinking was that it would make the code a little bit neater (however, I’m still not sure if this makes the code easier to read). The method I added looked like this:
[python]
def get_child(self):
myList = [child.data for child in self.children]
mySecondMenu.set_menu(‘select’,*myList)
[/python]which resulted in myFunction getting trimmed down to this:
[python]
def myFunction(*args):
if myVar.get() == ‘Fruits’:
fruits.get_child()
if myVar.get() == ‘Vegetables’:
vegetables.get_child()
[/python]The final big problem was that, as Steve put it, I should work to generalize my code more. His meaning was that my functions should be more applicable for different parts. At that point in the development of my code, I would of had to do numerous if statements in response to what the .get() method returned. My final project will be using a pretty hefty tree and such a structure would have me writing an absolutely ridiculous number of if statements. It would work, but it would be very tedious. At this point, Steve’s vision was a little above me, so he went ahead and showed me what the code could look like. And thus, he came up with this:
[python]
from tkinter import *
from ttk import OptionMenu
root = Tk()
class Node(object):
def __init__(self, data):
self.data = data
self.children = []
def add_child(self, obj):
self.children.append(obj)
def get_child(self, data):
for type in self.children:
if(type.data == data):
return type;
apple = Node(‘Apple’)
banana = Node(‘Banana’)
fruits = Node(‘Fruits’)
fruits.add_child(apple)
fruits.add_child(banana)
broccoli = Node(‘Broccoli’)
carrot = Node(‘Carrot’)
vegetables = Node(‘Vegetables’)
vegetables.add_child(broccoli)
vegetables.add_child(carrot)
food = Node(‘Food’)
food.add_child(fruits)
food.add_child(vegetables)
def myFunction(*args):
selectedText = myVar.get()
selectedFoodType = food.get_child(selectedText)
foods = [child.data for child in selectedFoodType.children]
mySecondMenu.set_menu(‘select’,*foods)
myVar = StringVar(root)
myMenu = OptionMenu(root, myVar, ‘select’, ‘Fruits’, ‘Vegetables’)
myMenu.pack()
mySecVar = StringVar(root)
mySecondMenu = OptionMenu(root, mySecVar, ‘—–‘)
mySecondMenu.pack()
myVar.trace(‘w’, myFunction)
root.mainloop()
[/python]Suffice to say I was ecstatic that the code had become so streamlined. With any luck, my project should be wrapping up and finished within the next week or so.
