Search code examples
pythonfsm

state do not change in a FSM in python using transitions library


I'm trying to replicate the code from this talk:

class Video:
    ## Define the states
    PLAYING = "playing"
    PAUSED = "paused"
    STOPPED = "stopped"
    
    
    def __init__(self,source):
        self.source = source
        
        transitions = [
            
            {"trigger":"play","source":self.PAUSED, "dest":self.PLAYING},
            {"trigger":"play","source":self.STOPPED, "dest":self.PLAYING},
            ##
            {"trigger":"pause","source":self.PAUSED, "dest":self.PAUSED},
            ##
            {"trigger":"stop","source":self.PAUSED, "dest":self.STOPPED},
            {"trigger":"stop","source":self.PLAYING, "dest":self.STOPPED}
            
        ]
        
        self.machine = Machine( 
        model = self,
        transitions = transitions,
        initial = self.STOPPED)
        
        
    def pause(self):
        print ("pause")
        
    def play(self):
        print ("play")
        
    def stop(self):
        print ("stop")

But one I call it, it does not work:

test = Video("some text")

that returns a warning:

2020-09-27 17:25:50,255 [11472] WARNING  transitions.core:828: [JupyterRequire] Model already contains an attribute 'play'. Skip binding.
2020-09-27 17:25:50,259 [11472] WARNING  transitions.core:828: [JupyterRequire] Model already contains an attribute 'pause'. Skip binding.
2020-09-27 17:25:50,260 [11472] WARNING  transitions.core:828: [JupyterRequire] Model already contains an attribute 'stop'. Skip binding.

But the main issue is that the state does not change:

enter image description here

This is the code from the original talk:

enter image description here

enter image description here


Solution

  • I am posting the modified code over here which gives you the expected output. Apologies for code formatting, I am really struggling with the markup editor here.

    The things which the problematic code seems to be missing are

    1. you are not passing the list of states to the FSM machine.

    2. Also, As it looks, the machine modifies the model with the trigger functions. You having the same named functions seems to be overriding those[1]. I assume the right way to trigger any function would be to use "after" attribute and the function name. Most probably there will be before too.

      from transitions import Machine
      
      class Video:
      
          ## Define the states
          PLAYING = "playing"
          PAUSED = "paused"
          STOPPED = "stopped"
      
          states = [PLAYING, PAUSED, STOPPED]
      
      
          def __init__(self,source):
              self.source = source
      
              transitions = [ 
      
                  {"trigger":"play","source":self.PAUSED, "dest":self.PLAYING, "after": "log_play"},
                  {"trigger":"play","source":self.STOPPED, "dest":self.PLAYING, "after": "log_play"},
                   ##  
                  {"trigger":"pause","source":self.PAUSED, "dest":self.PAUSED},
                  ##  
                  {"trigger":"stop","source":self.PAUSED, "dest":self.STOPPED},
                  {"trigger":"stop","source":self.PLAYING, "dest":self.STOPPED}
      
              ]   
      
              self.machine = Machine( 
                  model = self,
                  transitions = transitions,
                  states = self.states,
                  initial = self.STOPPED)
      
      
          def log_pause(self):
              print ("pause")
      
          def log_play(self):
              print ("play")
      
          def log_stop(self):
              print ("stop")
      

    Ref:

    1. https://github.com/pytransitions/transitions/blob/master/transitions/core.py#L594