Иерархическая графомашина, скрывающая вложенные состояния

#pytransitions

Вопрос:

Я экспериментировал с классом HierarchicalGraphMachine, чтобы помочь визуализировать структуры машин при их редактировании.

 from transitions.extensions import HierarchicalGraphMachine as Machine

count_states = ['1', '2', '3', 'done']
count_trans = [
    ['increase', '1', '2'],
    ['increase', '2', '3'],
    ['decrease', '3', '2'],
    ['decrease', '2', '1'],
    ['done', '3', 'done'],
    ['reset', '*', '1']
]
counter = Machine(states=count_states, transitions=count_trans, initial='1')

states = ['waiting', 'collecting', {'name': 'counting', 'children': counter, 'initial': '1'}]

transitions = [
    ['collect', '*', 'collecting'],
    ['wait', '*', 'waiting'],
    ['count', 'collecting', 'counting']
]

collector = Machine(states=states, transitions=transitions, initial='waiting')
collector.get_graph(show_roi=False).draw('count1.png', prog='dot')
 

Это создает ожидаемую графику, показывающую как родительское, так и вложенное состояния в полном объеме (я еще не уполномочен загружать графику).
Есть ли способ создать полную графику родительского состояния машины без расширения вложенных состояний? Например, сведение вложенных состояний к пустому окну.

Я пробовал «show_roi=True», но это показывает только текущее событие перехода и удаляет все остальные состояния.

Ответ №1:

В зависимости от того, используете ли вы pygraphviz (по умолчанию в 0.8.8 и более ранних версиях) или graphviz серверную часть, get_graph может быть возвращен pygraphiv.Объект аграфа или обычай transitions.Graph . An AGraph легче манипулировать, в то время как второй-это в основном чистая графическая нотация в точке. Однако вы можете манипулировать и тем, и другим в соответствии со своими потребностями. Например, вы можете отфильтровать края и узлы из AGraph и перестроить их «плоскую» версию:

 # your code here ...

collector.collect()

graph = collector.get_graph()

# iterate over all edges; We know that parent and child states are connected
# with an underscore. We just collect the root element of each source
# and target element of each edge. Furthermore, we collect the edge color,
# and the label which is stored either in 'label', 'taillabel' or 'headlabel' 
new_edges = [(edge[0].split('_')[0],
              edge[1].split('_')[0],
              edge.attr['color'],
              edge.attr['label']
              or edge.attr['taillabel']
              or edge.attr['headlabel']) for edge in graph.edges()]

# States with children are noted as subgraphs. We collect their name and their
# current color.
new_nodes = [(sgraph.graph_attr['label'], sgraph.graph_attr['color'])
             for sgraph in graph.subgraphs()]
# We add all states that have no children and also do not contain an
# underscore in their name. An underscore would suggest that this node/state
# is a child/substate.
new_nodes  = [(node.name, node.attr['color'])
              for node in graph.nodes() if '_' not in node.name]

# remove everything from the graph obeject
graph.clear()

# and add nodes and edges again
for name, color in new_nodes:
    graph.add_node(name, color=color)

for start, target, color, label in new_edges:
    if label:
        graph.add_edge(start, target, color=color, label=label)

graph.draw('agraph.png', prog='dot')
 

Это приводит к следующему графику:

График HSM

Вы видите, что я также собрал цвет края и узла, чтобы визуализировать последний переход, но graph.clear() удалил все атрибуты стиля «по умолчанию». Они также могут быть скопированы и восстановлены, или мы можем удалить только узлы, ребра и подграфы. Это зависит от того, насколько вы готовы возиться с (py)graphviz.