#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')
Это приводит к следующему графику:
Вы видите, что я также собрал цвет края и узла, чтобы визуализировать последний переход, но graph.clear()
удалил все атрибуты стиля «по умолчанию». Они также могут быть скопированы и восстановлены, или мы можем удалить только узлы, ребра и подграфы. Это зависит от того, насколько вы готовы возиться с (py)graphviz.