Closed badiku closed 4 years ago
I think this'd be added to GraphMachine, but first I tried to add label using subClass:
from transitions import State
from transitions.extensions.diagrams import TransitionGraphSupport
from transitions.extensions import GraphMachine
class StateLabelSupport(State):
""" add label support
"""
def __init__(self, *args, **kwargs):
label = kwargs.pop('label', None)
super().__init__(*args, **kwargs)
if label: self.label = label
def __repr__(self):
return "<%s('%s')@%s> %s" % (type(self).__name__,
self.name, id(self),
self.label if hasattr(self, 'label') else "")
class TransitionLabelSupport(TransitionGraphSupport):
""" add label support
"""
def __init__(self, *args, **kwargs):
label = kwargs.pop('label', None)
super().__init__(*args, **kwargs)
if label: self.label = label
def __repr__(self):
return "<%s('%s', '%s')@%s> %s" % (type(self).__name__,
self.source, self.dest, id(self),
self.label if hasattr(self, 'label') else "")
class LabelGraphMachine(GraphMachine):
'''add label support'''
state_attributes = GraphMachine.state_attributes + ['label']
transition_attributes = GraphMachine.transition_attributes + ['label']
state_cls = StateLabelSupport
transition_cls = TransitionLabelSupport
# build machine
machine = LabelGraphMachine(states=dict(name='test', label='test'))
print(machine.get_graph())
strict digraph { graph [label="State Machine", rankdir=LR ]; node [color=black, fillcolor=white, label="\N", peripheries=1, shape=rectangle, style="rounded, filled" ]; edge [color=black]; test [label=test]; initial [color=red, fillcolor=darksalmon, label=initial, peripheries=2]; }
to support updating label, I changed function _change_state and _get_graph:
from transitions import State
from transitions.extensions.diagrams import TransitionGraphSupport
from transitions.extensions import GraphMachine
class StateLabelSupport(State):
""" add label support
"""
def __init__(self, *args, **kwargs):
label = kwargs.pop('label', None)
super().__init__(*args, **kwargs)
if label: self.label = label
def __repr__(self):
return "<%s('%s')@%s> %s" % (type(self).__name__,
self.name, id(self),
self.label if hasattr(self, 'label') else "")
class TransitionLabelSupport(TransitionGraphSupport):
""" add label support
"""
def __init__(self, *args, **kwargs):
label = kwargs.pop('label', None)
super().__init__(*args, **kwargs)
if label: self.label = label
def _change_state(self, event_data):
super()._change_state(event_data)
event_data.model.previous = self.source
def __repr__(self):
return "<%s('%s', '%s')@%s> %s" % (type(self).__name__,
self.source, self.dest, id(self),
self.label if hasattr(self, 'label') else "")
class LabelGraphMachine(GraphMachine):
'''add label support
'''
state_attributes = GraphMachine.state_attributes + ['label']
transition_attributes = GraphMachine.transition_attributes + ['label']
state_cls = StateLabelSupport
transition_cls = TransitionLabelSupport
def _get_graph(self, model, title=None, force_new=False, show_roi=False):
'''force update label, and reserve previous status
'''
if force_new:
self._needs_update = True
grph = super()._get_graph(model, title, force_new, show_roi)
if force_new and hasattr(self.model, 'previous'):
self.model_graphs[self.model].set_previous_transition(self.model.previous, self.model.state)
return grph
# build machine
machine = LabelGraphMachine(states=dict(name='test', label='test'))
# change label
machine.get_state('test').label = 'NewLabel'
print(machine.get_graph())
# use force_new=True to update label
print(machine.get_graph(force_new=True))
strict digraph { graph [label="State Machine", rankdir=LR ]; node [color=black, fillcolor=white, label="\N", peripheries=1, shape=rectangle, style="rounded, filled" ]; edge [color=black]; test [label=test]; initial [color=red, fillcolor=darksalmon, label=initial, peripheries=2]; }
strict digraph { graph [label="State Machine", rankdir=LR ]; node [color=black, fillcolor=white, label="\N", peripheries=1, shape=rectangle, style="rounded, filled" ]; edge [color=black]; test [label=NewLabel]; initial [color=red, fillcolor=darksalmon, label=initial, peripheries=2]; }
Hi @badiku,
I see there're some basic label codes in diagrams.py for supporting label, but when building a machine with label, got this error:
the line you discovered in diagrams.py is intended to support custom state definitions. State
never supported a label
attribute as far as I remember. So subclassing is the suggested way to use a graph label which differs from the state's name. As far as I can tell, this is also the road you have taken.
However, it sucks that you have to do this:
state_attributes = GraphMachine.state_attributes + ['label']
having label
in diagrams.py but not in markup.py sends indeed mixed messages. I will add 'label' to state_attributes
and transition_attributes
in MarkupMachine
. This won't do any harm to states/transitions not using labels. Since every transition already has to be derived from TransitionGraphSupport
, I will add the label
attribute to it as well. This way, only State
needs to be explicitely subclassed when labels are required.
When I get your last post correctly, using labels instead of named will mess with the graph styling. This looks buggy to me. I will check if this can be handled internally as well.
With the recent push, this should be sufficient:
from transitions import State
from transitions.extensions import GraphMachine
class StateLabelSupport(State):
def __init__(self, *args, **kwargs):
label = kwargs.pop('label', None)
super().__init__(*args, **kwargs)
if label: self.label = label
def __repr__(self):
return "<%s('%s')@%s> %s" % (type(self).__name__,
self.name, id(self),
self.label if hasattr(self, 'label') else "")
class LabelGraphMachine(GraphMachine):
state_cls = StateLabelSupport
def set_label(self, state_name, label):
self.get_state(state_name).label = label
for model in self.models:
model.get_graph().get_node(state_name).attr['label'] = label
machine = LabelGraphMachine(states=[dict(name='A', label='LabelA'), dict(name='B', label='LabelB')], initial='A')
machine.to_B()
machine.set_label('A', 'NewLabel')
machine.to_A()
machine.get_graph().draw('diagram.png', prog='dot')
Seems like labels are handled alright. I added a set_label
method to your LabelGraphMachine
. However, this will only work well with the pygraphviz
backend. If you are looking for a more versatile solution, your attempt looks more generic but may require more graph generations.
Since there is no new feedback I will close this issue. Feel free to comment anyway. I will reopen it if necessary.
hi, I like GraphMachine, and I'd like to add some label to nodes and edges.
I see there're some basic label codes in
diagrams.py
for supporting label, but when building a machine with label, got this error:so, the problem is :
diagrams.py
support label, but by default incore.py
State and Transition class do not support label argument.old codes for getting label: in diagrams.py:
does author forget these codes?
to support label, I tried to add label argument in
core.py
:and in
markup.py
, add label to attributes list:seemed ok. but there'd be some better method.
related issue: #442