Plot state machine chart
Description
It would be nice, mainly for design docs and educational use, to have a way to plot the state machine. It can be something like using the mermaid library.
Example:

Or something simpler like an ascii plot (http://www.algorithm.co.il/blogs/ascii-plotter/)
It would be nice! But I don't like the idea of adding a required dependency for this. Maybe this should be in a contrib module.
This way, what do you think about doing an intermediate graph representation, in a way that can be consumed easily for specific parsers?
Hello @caiolopes, @fgmacedo,
I have implemented this solution:
from graphviz import Digraph
def plot_state_machine(state_machine, name='state_machine'):
dg = Digraph(comment=name)
for s in state_machine.states:
for t in s.transitions:
dg.edge(t.source.name, t.destinations[0].name)
dg.render('folder/{}.gv'.format(name), format='png')
I can make a PR if you want. If not, maybe adding this example in the docs.
@claverru nice snippet, thanks. graphviz is not a very good dependency to have as required to this lib, IMHO.
I think we can follow two approaches:
- as you suggested, we can just have an example on the README. I would suggest to do it with
TrafficLightMachineto be friendly with the reader. - or, having this code as a method that raises an error if graphviz is not available. This is a common approach like with
pandaswhen you read an excel file without the required dependency.
I am really tempted to say that we can do both, since they are not mutual exclusive 🎉
What do you think @fgmacedo?
Thanks for pointing out this idea @caiolopes . Thanks for your snippet @claverru !
Let me share what I'm thinking about.
I'm reading about statecharts since SCXML (Statechart XML), is a W3C standard and it defines a lot of the semantics and specifies how to deal with certain edge cases. A statechart is essentially a state machine with extra-powers, that allows any state to include more machines, in a hierarchical fashion. This is already a feature request at https://github.com/fgmacedo/python-statemachine/issues/246.
With that in mind, my goal to this library may turn to be as much as possible compatible with statecharts but using a different syntax. A pythonic interface to statecharts.
A SCXML representation may be the answer. Or even an xstate compatible object representation to leverage their nice statechart viz tool .
Hello @caiolopes, @fgmacedo,
I have implemented this solution:
from graphviz import Digraph def plot_state_machine(state_machine, name='state_machine'): dg = Digraph(comment=name) for s in state_machine.states: for t in s.transitions: dg.edge(t.source.name, t.destinations[0].name) dg.render('folder/{}.gv'.format(name), format='png')I can make a PR if you want. If not, maybe adding this example in the docs.
@claverru thanks for sharing. Is there a way to add the name of the transition ?
Hello @hwerbi !
I think this should work (over the former snippet):
def plot_state_machine(state_machine, name='state_machine'):
dg = Digraph(comment=name)
for s in state_machine.states:
for t in s.transitions:
dg.edge(t.source.name, t.destinations[0].name, label=t.identifier)
dg.render('folder/{}.gv'.format(name), format='png')
I just added label=t.identifier when calling dg.edge.
Cheers!
EDIT:
Whole script:
from graphviz import Digraph
from statemachine import StateMachine, State
class TrafficLightMachine(StateMachine):
green = State('Green', initial=True)
yellow = State('Yellow')
red = State('Red')
slowdown = green.to(yellow)
stop = yellow.to(red)
go = red.to(green)
def plot_state_machine(state_machine, name='state_machine'):
dg = Digraph(comment=name)
for s in state_machine.states:
for t in s.transitions:
dg.edge(t.source.name, t.destinations[0].name, label=t.identifier)
dg.render('folder/{}.gv'.format(name), format='png')
if __name__ == '__main__':
st = TrafficLightMachine()
plot_state_machine(st, 'my_state_machine')
I packaged the plot function into a program that plots the statemachine from a separate module, creating the graph folder if not present and writing the plots to ./graphs/modname.machinename.png and .gv
https://github.com/slowrunner/Carl/blob/master/Examples/statemachine/plotsm.py
usage: plotsm.py [-h] -m MODULE -sm STATEMACHINE
optional arguments:
-h, --help show this help message and exit
-m MODULE, --module MODULE
module name to import from
-sm STATEMACHINE, --statemachine STATEMACHINE
State Machine Type Name
./plotsm.py -m stoplight -sm TraficLightMachine
will output ./graphs/stoplight.TrafficLightMachine.gv and
./graphs/stoplight.TrafficLightMachine.png
for the module stoplight.py containing the class TrafficLightMachine(StateMachine)
This issue seems to have stalled for while. Think about closing it.
Hello! Nice updates! I've just merged support for diagrams at https://github.com/fgmacedo/python-statemachine/pull/300
You can see the preview on the develop branch: https://python-statemachine.readthedocs.io/en/develop/diagram.html
I'm planning a release soon.