Question

I'm using Python to present a menu hierarchy on a Raspberry Pi, for time lapse photography. I'm using a 2 line x 16 char LCD and four buttons < (go to parent menu), ^ (go to previous sibling), v (go to next sibling) & > (select - ie go to first child).

It's my first play with Python, so there's a heap of stuff I'm not yet aware of. So far, I've defined a menuItem class with methods to add and get name, parent, and a bunch of other stuff. My difficulty is knowing how to get a collection of child menuItems (self.getChildren) or get the sibling menuItems (self.getSiblings), so I can derive next and previous siblings in the hierarchy. I get that the hierarchy is only implied by the creation order and the setParent method so far.

I've searched and seen that there may be a bunch of ways to tackle this, such as chainMaps? or a menu class - no doubt there are more ways. Can anyone help me choose an efficient and easy to understand method. (remember I'm new to this, so 'easy to understand' is better for me at least, than terse sophistication or complexity)

My code so far (be kind :o)

#####################################################################
# A menuItem chains to other menuItems
#####################################################################
class MenuItem: 
    def __init__(self):
        self.children = ''

    #setName: method to give a name to this menu item
    def setName(self, name):
        self.name = name

    #getName: method to report the name of this menu item
    def getName(self):
        return self.name

    #setParent: method to set the parent menu item for this menu item
    def setParent(self, parent):
        self.parent = parent

    #getParent: method to report the parent menu item for this menu item
    def getParent(self):
        return self.parent

    #setPrevious: method to set the previous sibling menu item
#   def setPrevious(self, previous):
#       self.previous = previous

    #getPrevious: method to report the previous sibling menu item
    def getPreviousSibling(self):
        return self.previousSibling

    #setNext: method to set the next sibling menu item
#   def setNext(self, nxt):
#       self.nxt = nxt

    #getNext: method to report the next sibling menu item
    def getNextSibling(self):
        return self.nextSibling

    #setChildren: method to append a new child menuItem
    # to be called from the point of instantiating the child menuItem
    def setChildren(self, appendMenuItem):
        self.children = self.children + ',' + appendMenuItem

    #getChildren: method to report the list of children manu items
    def getChildren(self):
        return self.children

    #getSiblings: method to report the sibling menu items
    def getSiblings(self):
        return self.siblings

    #setDisplayString: method to set the string that will be displayed on the LCD (max 16 characters)
    def setDisplayStringL1(self, displayStringL1):
        self.displayStringL1 = displayStringL1

    #setDisplayString: method to set the string that will be displayed on the LCD (max 16 characters)
    def setDisplayStringL2(self, displayStringL2):
        self.displayStringL2 = displayStringL2

    #getDisplayStringL1: method to report the first string that is displayed on the LCD for this menu item
    def getDisplayStringL1(self):
        return self.displayStringL1

    #getDisplayStringL2: method to report the second string that is displayed on the LCD for this menu item
    def getDisplayStringL2(self):
        return self.displayStringL2

    #setValidValues: method to set valid values (comma delimited)(need to think about how to do ranges or positive integers)
    def setValidValues(self, validValues):
        self.validValues = validValues

    #stringUpdateChars: method to update a section of the display string
    def stringUpdateChars(self,LineNum, startChar, endChar, ValString):
        if LineNum == 1: 
            for i in range (0,(endChar-startChar),1): #there must be a better way to do this!
                self.displayStringL1[startChar+i] = ValString[i]
        elif LineNum == 2:
            for i in range (0,(endChar-startChar),1):
                self.displayStringL2[startChar+i] = ValString[i]


def InitialiseMenu():
    menuitem1 = MenuItem()
    menuitem1.setName('applications')
    menuitem1.setDisplayStringL1('Applications')
    menuitem1.setDisplayStringL2('')
    menuitem1.setParent('none')

    menuitem2 = MenuItem()
    menuitem2.setName('timeLapse')
    menuitem2.setDisplayStringL1('Time Lapse')
    menuitem2.setDisplayStringL2('')
    menuitem2.setParent(menuitem1)

    menuitem3 = MenuItem()
    menuitem3.setName('bulb')
    menuitem3.setDisplayStringL1("Man. Set 'bulb'")
    menuitem3.setDisplayStringL2('')
    menuitem3.setParent(menuitem2)

    menuitem4 = MenuItem()
    menuitem4.setName('exposure')
    menuitem4.setDisplayStringL1("Exp:00 s")
    menuitem4.setDisplayStringL2("E00s")
    menuitem4.setValidValues(1,2,3,4,5,6,7,8,9,10,12,14,16,18,20,25,30,35,40,45,50,60)
    menuitem4.setParent(menuitem3)

    menuitem5 = MenuItem()
    menuitem5.setName('expUnits')
    menuitem5.setDisplayStringL1("Exp:00 s")
    menuitem5.setDisplayStringL2("E00s")
    menuitem5.setValidValues('s','m')
    menuitem5.setParent(menuitem4)

    menuitem6 = MenuItem()
    menuitem6.setName('delay')
    menuitem6.setDisplayStringL1("Delay:00 s")
    menuitem6.setDisplayStringL2("E00s D00s")
    menuitem6.setValidValues(1,2,3,4,5,6,7,8,9,10,12,14,16,18,20,25,30,35,40,45,50,60)
    menuitem6.setParent(menuitem5)

    menuitem7 = MenuItem()
    menuitem7.setName('delayUnits')
    menuitem7.setDisplayStringL1("Delay:00 s")
    menuitem7.setDisplayStringL2("E00s D00s")
    menuitem7.setValidValues('s','m')
    menuitem7.setParent(menuitem6)

    menuitem8 = MenuItem()
    menuitem8.setName('frames')
    menuitem8.setDisplayStringL1("Frames:00000")
    menuitem8.setDisplayStringL2("E00s D00s F0000")
    menuitem8.setParent(menuitem7)

    #--------------------------      

    menuitem9 = MenuItem()
    menuitem9.setName('clipLength')
    menuitem9.setDisplayStringL1("Clip length")
    menuitem9.setDisplayStringL2('')
    menuitem9.setParent(menuitem2)

    menuitem10 = MenuItem()
    menuitem10.setName('clipLengthPeriod')
    menuitem10.getDisplayStringL1("Len:00 s")
    menuitem10.getDisplayStringL2("L:00s")
    menuitem10.setParent(menuitem9)

    menuitem11 = MenuItem()
    menuitem11.setName('clipLengthPeriodUnits')
    menuitem11.getDisplayStringL1("Len:00 s")
    menuitem11.getDisplayStringL2("L:00s")
    menuitem11.setParent(menuitem10)

    menuitem12 = MenuItem()
    menuitem12.setName('fps')
    menuitem12.getDisplayStringL1("FPS:00/s")
    menuitem12.getDisplayStringL2("F:00s fps:00")
    menuitem12.setValidValues(20,21,22,23,24,25,26,27,28,29,30)
    menuitem12.setParent(menuitem11)      

    #--------------------------      
    menuitem13 = MenuItem()
    menuitem13.setName('realTime')
    menuitem13.getDisplayStringL1("Real-time")
    menuitem13.setDisplayStringL2('')
    menuitem13.setParent(menuitem2)

    menuitem14 = MenuItem()
    menuitem14.setName('realTimePeriod')
    menuitem14.getDisplayStringL1("Per:00 s")
    menuitem14.getDisplayStringL2("P:00s")
    menuitem14.setParent(menuitem13)

    menuitem15 = MenuItem()
    menuitem15.setName('realTimePerioddUnits')
    menuitem15.getDisplayStringL1("Per:00 s")
    menuitem15.getDisplayStringL2("P:00s")
    menuitem15.setParent(menuitem14)

    menuitem16 = MenuItem()
    menuitem16.setName('realTimefps')
    menuitem16.getDisplayStringL1("FPS:00/s")
    menuitem16.getDisplayStringL2("f:00s fps:00")
    menuitem16.setValidValues(20,21,22,23,24,25,26,27,28,29,30)
    menuitem16.setParent(menuitem15)      

    menuitem16 = MenuItem()
    menuitem16.setName('startPauseStop')
    menuitem16.setDisplayStringL1("Start")
    menuitem16.setDisplayStringL2("press > to start")
    menuitem16.setParent(menuitem2)      

userInput = 'yes'
#when the other navigation is enabled, I'll set this to menuItem1
currentMenuItem = menuitem8  #<-this is in error - indent problem?
#type 'no' to exit or use <|^|v|> to navigate
#I'm expecting this to keep asking for input. Each time < is pressed it'll print out the name of the parent menuItem up to the root item
while userInput != "no":
    userInput = raw_input("< ^ v >  ")
    #up tree
    if userInput == "<":
        if currentMenuItem.getParent().getName()  != "none":
            currentMenuItem = currentMenuItem.getParent()
            print "current menu item: " + currentMenuItem.getName()
        else:
            print "current menu item: " + currentMenuItem.getName()
#     #previous sibling
#     elif userInput == "^":
#         if currentMenuItem.getParent() != "none":
#             currentMenuItem = currentMenuItem.getParent()
#             print currentMenuItem.getName()
#     #next sibling
#     elif userInput == "v":
#         if currentMenuItem.getParent() != "none":
#             currentMenuItem = currentMenuItem.getParent()
#             print currentMenuItem.getName()
#     #select - first child
#     elif userInput == ">":
#         if currentMenuItem.getParent() != "none":
#             currentMenuItem = currentMenuItem.getParent()
#             print currentMenuItem.getName()
print "Finished!"
Was it helpful?

Solution

One way to do this would be a class attribute holding all MenuItem instances, then using the keys to this dictionary, the names of the items, to move between them:

class MenuItem(object):

    items = {}

    def __init__(self, name, parent):
        self.name = name
        self._parent = parent
        MenuItem.items[name] = self      

    @property
    def parent(self):
        return MenuItem.items[self._parent]

    @parent.setter
    def set_parent(self, parent):
        if parent in MenuItem.items:
            self._parent = parent
        else:
            raise ValueError

Note the use of property, which is more Pythonic than get and set.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top