3
\$\begingroup\$

I have a system for going back to a previously-visited page in a Python console application; it works well enough, but I feel that there could be a prettier way of doing it. Does anyone have any suggestions to improve what I have below?

def main():
    try:
        while True:
            print('Main Menu\n')

            option = input('page 1, page 2, quit\n')

            if option in ['1', 'one']:
                menu_stack.append(0)
                page_one()
            elif option in ['2', 'two']:
                menu_stack.append(0)
                page_two()
            elif option in ['q', 'quit']:
                quit()
            else:
                print('Invalid input, please try again\n')
                sleep(1)

    except KeyboardInterrupt:
        quit()

Very similar to the main menu page, below is page 1:

def page_one():
    while True:
        clear_terminal()

        print('Page One\n')

        option = input('Page 2, back\n')

        if option in ['2', 'two']:
            menu_stack.append(1)
            page_two()
        elif option in ['b', 'back']:
            menu_checker()
        else:
            print('Invalid input, please try again\n')
            sleep(1)

menu_checker() calls the other pages based on what pops from the stack:

def menu_checker():
    page = menu_stack.pop()

    if page == 1:
        page_one()
    elif page == 2:
        page_two()
    elif page == 0:
        main()
    else:
        print('Error')

Does anyone have any better ideas/solutions? Even though it doesn't cause any issues (as far as I am aware), I feel what I have is kind of clunky and could be improved.

\$\endgroup\$
6
  • \$\begingroup\$ Welcome to the Code Review Community where we review code that is working as expected and make suggestions on how to improve that code. This part of the question is off-topic for code review An idea I had was to stack function calls; so the popped values of menu_stack() would be functions, not integers. The issue with this is that I don't know how to do that, or even if it can be done. \$\endgroup\$
    – pacmaninbw
    Feb 9, 2021 at 12:44
  • \$\begingroup\$ No problem removing, but why is it off-topic? I am new to this forum, but I thought it would be OK to share an idea I had. \$\endgroup\$ Feb 9, 2021 at 12:49
  • \$\begingroup\$ It is asking about code not yet written, which means it doesn't belong in a code review. We don't answer how to questions on code review, that is what stack overflow is for. Please read How do I ask a good question?. \$\endgroup\$
    – pacmaninbw
    Feb 9, 2021 at 12:51
  • \$\begingroup\$ Well, I wasn't asking how to do that per se, just that it was an idea I had, help give an insight to what my application is trying to achieve. \$\endgroup\$ Feb 9, 2021 at 16:53
  • \$\begingroup\$ Welcome to Code Review! The current question title, which states your concerns about the code, is too general to be useful here. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How to get the best value out of Code Review: Asking Questions for guidance on writing good question titles. At the moment it's uninformative - a console app which does what? \$\endgroup\$ Feb 10, 2021 at 13:46

1 Answer 1

3
\$\begingroup\$

I'm thinking there are a few different approaches you could take here.

The stack was inside you all along

It may not be the most flexible of approaches, but the way things are right now, your pages are just functions. And guess what? Your program already has a stack of functions -- you navigate it by calling functions and returning from them! You don't get a whole lot of information about where you'll end up if you return, but if that's not a problem, you might be able to just get rid of menu_checker and menu_stack altogether -- if the user wants to go back, you just return.

For example, your page_one could simply look like

def page_one():
    while True:
        clear_terminal()

        print('Page One\n')

        option = input('Page 2, back\n')

        if option in ['2', 'two']:
            page_two()
        elif option in ['b', 'back']:
            return
        else:
            print('Invalid input, please try again\n')
            sleep(1)

It's not the fanciest-looking perhaps, but it's easy to implement, easy to understand and does the job just fine.

Where do you want to go?

But maybe that isn't good enough. It might be nice if the "back" option could say something like "Back (to page 1)" depending on what the stack looks like, for example. Or maybe you'd like to also have a "forward" option in case the user accidentally presses "back" too many times. Or maybe there's another reason you don't want to use the call stack for this. That's fine too.

Another option could be to move the navigation logic from the individual menus into a single core loop. Each menu then returns which menu it thinks you should go to next.

For example, we might be looking at something like this:

def main():
    menu_stack = [main_menu]

    while menu_stack:
        current_menu = menu_stack[-1]

        next = current_menu(option)

        if next:
            menu_stack.append(next)
        else:
            menu_stack.pop()

With menus looking a lot like the previous ones, but instead of calling the next menu like page_two() they return it like return page_two. Or perhaps return None if we should go back.

Classy

Personally, though, I think there are some tasks (like asking for input) that should be the responsiblity of the app itself rather than the menus. The menu can know what it looks like and how to navigate based on input, but the core loop asks it for help and then handles those things on its own terms.

def main():
    menu_stack = [main_menu]

    while menu_stack:
        current_menu = menu_stack[-1]

        clear_terminal()

        print(current_menu.text)

        option = input(current_menu.prompt)

        try:
            next = current_menu.get_next_menu(option)

            if next:
                menu_stack.append(next)
            else:
                menu_stack.pop()
        except ValueError as e:
            print("Invalid input, please try again")
            sleep(1)

Defining a nice Menu class is left as an exercise for the reader.

\$\endgroup\$
1
  • \$\begingroup\$ Wow, thanks, Sara! Really cool stuff :p This is exactly what I was looking for and more. I really appreciate the help. Stay safe :) \$\endgroup\$ Feb 10, 2021 at 11:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.