Search code examples
pythonesp32setalarmclock

Python - returning from a While Loop after its end


I am trying to code a menu routine for an alarm-clock in python, which displays the relevant information on a 7-segment display according to some inputs via pushbuttons. I managed to create a loop which displays the current time, and whe the button "debMenu" is clicked 3 menu options are displayed. This works well only for the first time untill the 3rd. menu option is reached. When I push the button again the routine does not work - so the function "main_menu" is not called again. What I am doing wrong...? Thanks !!

stop_loop = [False]

def main_menu(stop_loop, n=[0]):
    stop_loop[0] = True
    debSelect = DebouncedSwitch(butSelect, cbButSelect, "butSelect")
    menuList = ['HO  ', 'AL  ', 'UP  ']

    if n[0] < 3:
        display.scroll(menuList[n[0]], 200) #display menu on 7-seg display
        display.show(menuList[n[0]])
        n[0] += 1

    elif n[0] == 3:
        n=[0]
        stop_loop[0] = False

    main()

def main():
    stop_loop[0] = False
    debMenu = DebouncedSwitch(butMenu, main_menu, stop_loop)
    while not stop_loop[0]: # display current time on 7-seg display
        curTime = rtc.datetime()
        display.numbers(curTime.hour, curTime.minute, False)
        time.sleep_ms(500)
        display.numbers(curTime.hour, curTime.minute)
        time.sleep_ms(500)

main()

Solution

  • I guess the default value mutable variable is the one killing you. Check here: http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments.

    I don't even understand why you need a list variable in this case, why not just n=0 without having to use a list?.

    maybe make a new global variable or class to encapsulate both state-related variables (stop_loop and n). Like:

    loop = dict(stop=False, n=0)
    

    now you pass this instead of stop_loop to main_menu.

    Or use a class that encapsulates a circular array for menu like:

    class LoopMenu:
       """ circular array menu list with pause/continue/next"""
    
       def __init__(self, menu):
           self.menu = menu
           self.stop = False
           self.n = 0
    
       def next(self):          
           menu = self.menu[self.n]
           self.n += 1
           if self.n > len(self.menu):
               self.n = 0
           return menu
    
        def pause(self):
            self.stop = True
    
        def play(self):
            self.stop = False
    

    then you main_menu method could be:

    my_menu_loop = LoopMenu(['HO  ', 'AL  ', 'UP  '])
    
    def main_menu(menu_loop):
        menu_loop.pause()
        debSelect = DebouncedSwitch(butSelect, cbButSelect, "butSelect")
    
        menu = menu_loop.next()
        display.scroll(menu, 200) #display menu on 7-seg display
        display.show(menu)
    
        main()
    
    def main():
        my_menu_loop.play()
        debMenu = DebouncedSwitch(butMenu, main_menu, my_menu_loop)
        while not loop_menu.stop: # display current time on 7-seg display
            curTime = rtc.datetime()
            display.numbers(curTime.hour, curTime.minute, False)
            time.sleep_ms(500)
            display.numbers(curTime.hour, curTime.minute)
            time.sleep_ms(500)
    
    main()