mriehl / fysom

Finite State Machine for Python (based on Jake Gordon's javascript-state-machine)
388 stars 40 forks source link

Enhancement: Integration with signalslot? #23

Open sjlongland opened 9 years ago

sjlongland commented 9 years ago

Just a thought, is it worth providing some integration into signalslot so the events generated by the state machine are exported to other objects as "signals"? I've been managing this by hand in my own code, and found it a very powerful way to interact with Fysom.

It could be an optional feature turned on by specifying what events you want signals created for in the constructor. e.g.

fsm = Fysom({ 'initial': 'green',
          'events': [
              {'name': 'warn', 'src': 'green', 'dst': 'yellow'},
              {'name': 'panic', 'src': 'yellow', 'dst': 'red'},
              {'name': 'calm', 'src': 'red', 'dst': 'yellow'},
              {'name': 'clear', 'src': 'yellow', 'dst': 'green'} ],
          'signals': ['onentergreen','onenteryellow','onenterred', ...]})

The signals could be properties with _sig appended to the name to avoid confusion.

signalslot is here: https://github.com/Numergy/signalslot

If there's interest I might look into implementing it proper.

mriehl commented 9 years ago

Seems like a good idea, it would alleviate the burden of dealing with many callbacks that simply defer to other objects.

Where would the callable slots be defined though? On the fsm? Or would you add a signal_slots dictionary to the constructor?

sjlongland commented 9 years ago

On 21/05/15 16:52, Maximilien Riehl wrote:

Seems like a good idea, it would alleviate the burden of dealing with many callbacks that simply defer to other objects.

Where would the callable slots be defined though? On the |fsm|? Or would you add a |signal_slots| dictionary to the constructor?

I was simply thinking you'd pass in an option in the constructor, and it would create Signal instances for each named signal and bind it as a callback.

If you wanted to pass a specific Signal instance for a given signal name, then you could conceivably make this parameter optionally a dict mapping the signal name (== callback name) to a signal instance.

Some untested code:

import fysom
import signalslot

class SignalFysom(fysom.Fysom):
    def __init__(self, *args, **kwargs):
        signals = kwargs.pop('signals',{})
        callbacks = kwargs.pop('callbacks',{})
        if signals is not None:
            if not isinstance(signals, dict)):
                signals = dict([(name, \
                    signalslot.Signal(name=name)) \
                        for name in signals])
            for name, signal in signals.items():
                setattr(self, '%s_sig' % name, signal)
                if name in callbacks:
                    signal.connect(callbacks.pop(name))
                callbacks[name] = signal.emit
        super(SignalFysom, self).__init__(*args, \
            callbacks=callbacks, **kwargs)

That would in theory, hook the emit method of the signal as the callback. If a callback of the same name is given, it gets added as a slot to the signal before the signal is added.

Regards,

Stuart Longland (aka Redhatter, VK4MSL)

I haven't lost my mind... ...it's backed up on a tape somewhere.

mriehl commented 9 years ago

I like the approach. If you have time to implement this, I'd definitely merge it. I have a few personal projects where the integration with signalslot would greatly simplify the code!

TTimo commented 9 years ago

+1 .. I use both fysom and signalslot, would be interesting to play with a tighter integration