Sign in to follow this  

Finite state machine menu design

This topic is 679 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I post here because the same concepts can be applied to a game:

 

I'm creating a FSM in python (it's a step sequencer and sample pad based on a Raspberry Pi 2).

Right now there are two states and the third is the Menu. This is handled by a class System which handles all the states. The Menu state has to edit the attributes of the other states, so I passed the other states to its constructor.

class State(object):
  def buttonPress(self, numberButton):
    raise NotImplementedError

class StepSequencer(State):
  def buttonPress(self, numberButton):
    ...

class SamplePad(State):
  def buttonPress(self, numberButton):
    ...

class Menu(State):
  def __init__(self,stepSequencer,samplePad):
    self.stepsequencer = stepSequencer
    self.samplepad = samplePad
  def buttonPress(self, numberButton):
    ...
  def setMenuItem(self, currentMenuItem):
    self.currentMenuItem = currentMenuItem

class MenuItem(object):
  def __init__(self, text):
    self.text = text

class System(object):
  def __init__(self):
    self.stepsequencer = StepSequencer()
    self.samplepad = SamplePad()
    self.menu = Menu(self.stepsequencer, self.samplepad)
  def setState(self,state):
    self.state = state
  def buttonPress(self, numberButton):
    self.state.buttonPress(numberButton)

I can't figure out how to create the structure for the menu. I thought of creating a class MenuItem for every menu item so the Menu state has all these objects and can change the current MenuItem, however there are some things that I cannot overcome:

  • how can I declare all the menu items and pass them to the Menu state to create dynamically the structure of the menu?

  • having this this menu:

    Step Sequencer settings:
       Set samples
       Set volume
    Sample Pad setting:
       Set samples
       Set volume

    for example if I want to have a slider that sets the volume, do I have to create another state to handle this? This state can be another state of the System or it must be a substate of the Menu state?

  • can I execute the code of each state using this logic? :

    while(system.state = system.stepsequencer):
      ...
    while(system.state = system.menu):
      ...

    The state is changed by a listener in another thread. This seems a very unorthodox way of handling states but it seems to work. If this is effective, how can I handle the Menu substates?

Share this post


Link to post
Share on other sites

I have a really hard time understanding what you're doing. Deriving a menu from State is particularly confusing to me. The best I can think of is that you have a program where you only show one sub-menu, so each sub-menu becomes a state, and you jump between them to switch sub-menus?

 

If so, you are basically duplicating menu structure onto the state structure, which explains why you are having difficulties in having a dynamic menu structure (since that would imply a dynamic state structure, which is counter-intuitive in most forms of state machines).

 

In addition, your code looks very heavy class oriented, with classes for almost everything. While this is normal in languages like Java or C#(?), it's not very "python". Python tends to be much more lightweight.

 

 

 

Since I am just speculating here, I cannot really give you an answer to the questions. Instead I'll just open a discussion.

 

One thing that may be helpful, is to let go of the idea that a menu is a state. A menu is a data structure with sub-menus and items, and perhaps some callback functions, but it's not a state. How you display a menu in your program has nothing to do with the content of the menu.

 

You will likely have one or more program states that display a menu, and those states probably point into the menu data structure, but that makes the menu a data member of the state rather than being a state. (In other words, composition rather than inheritance.)

 

 

Finally, an alternative way to write a menu structure. Have a look at it, maybe you can use some parts of it.

class MenuItem:
    def __init__(self, text, cb_func, param):
        self.text = text
        self.cb_func = cb_func
        self.param = param

    def click(self):
        """ User clicked the item, call the function. """
        self.cb_func(self.param)

class Menu:
    def __init__(self, text, items):
        self.text = text
        self.items = items

def sequencer_steps(val):
    # Handle sequencer steps click

def sequencer_volume(val):
    # Handle sequencer volume click

seq_items = [MenuItem("seq steps up",    sequencer_steps,   1),
             MenuItem("seq steps down",  sequencer_steps,  -1),
             MenuItem("seq volume down", sequencer_volume,  1),
             MenuItem("seq volume down", sequencer_volume, -1),
            ]

seq_menu = Menu("sequencer", seq_items)

pad_items = [ .... ]
pad_menu = Menu("pad", pad_items)


main_items = [seq_menu, pad_menu]
main_menu = Menu("main", main_items)

Share this post


Link to post
Share on other sites

Thank you for your answer. The project is structured so it has exactly 1 menu, so I thought that implementing it as a state should have sense. In fact if it isn't a state, how can the system know if the menu is active or not?

 

I must use this heavy class oriented approach, I know it's not a python strong point, however python is the best language for dealing with a Rasp Pi.

Share this post


Link to post
Share on other sites

Hmm, how to explain the difference between displaying a menu and the menu itself....

 

Right, let's say a book. It's a collection of pages with text, and if you're lucky, pictures as well.

There is also reading, the activity, where you read the content of the book.

 

Now, are you a book when you are reading?

 

 

Inheritance means "is a". If you inherit a menu from a state, a menu is a state.

 

I think a better way to express this is a state "reading", or a state "displaying the menu", and a separate data structure "book", or "menu".

Share this post


Link to post
Share on other sites

It may be a simple matter of renaming the state to "DisplayMenuState" or something.

 

 

Reviewing your questions under this assumption, I am afraid I didn't get much further.

 

how can I declare all the menu items and pass them to the Menu state to create dynamically the structure of the menu?

In standard Python you'd use a lightweight structure, and define the tree in data, as I have shown. You said you need the heavy class structure for some reason. I don't understand that, but if that's what you need, you need to find something list-like in your classes. Maybe a linked list?

 

 

for example if I want to have a slider that sets the volume, do I have to create another state to handle this? This state can be another state of the System or it must be a substate of the Menu state?

Both options would be feasible, I can't judge which solution is better under your class constraints.

 

 

can I execute the code of each state using this logic?

You already proved it was possible right? :)

 

The normal idea however is that you let the state itself execute its own code, ie  "current_state.run_tick()". I don't know if that is feasible for you.

Share this post


Link to post
Share on other sites

This topic is 679 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this