20 import xml.etree.ElementTree
32 'BlackboardCheckDouble',
33 'BlackboardCheckString',
55 'ReinitializeGlobalLocalization',
61 'initialPoseReceived',
83 args = parse_command_line()
84 xml_tree = xml.etree.ElementTree.parse(args.behavior_tree)
85 root_tree_name = find_root_tree_name(xml_tree)
86 behavior_tree = find_behavior_tree(xml_tree, root_tree_name)
87 dot = convert2dot(behavior_tree)
89 legend = make_legend()
91 legend.render(args.legend)
94 print(f
'Saving dot to {args.save_dot}')
95 args.save_dot.write(dot.source)
96 dot.render(args.image_out, view=args.display)
99 def parse_command_line():
100 parser = argparse.ArgumentParser(
101 description=
'Convert a behavior tree XML file to an image'
106 help=
'the behavior tree XML file to convert to an image',
111 help=
'The name of the output image file. Leave off the .png extension',
116 help=
'If specified, opens the image in the default viewer',
120 type=argparse.FileType(
'w'),
121 help=
'Saves the intermediate dot source to the specified file',
123 parser.add_argument(
'--legend', help=
'Generate a legend image as well')
124 return parser.parse_args()
127 def find_root_tree_name(xml_tree):
128 return xml_tree.getroot().get(
'main_tree_to_execute')
131 def find_behavior_tree(xml_tree, tree_name):
132 trees = xml_tree.findall(
'BehaviorTree')
134 raise RuntimeError(
'No behavior trees were found in the XML file')
137 if tree_name == tree.get(
'ID'):
140 raise RuntimeError(f
'No behavior tree for name {tree_name} found in the XML file')
144 def convert2dot(behavior_tree):
145 dot = graphviz.Digraph()
147 parent_dot_name = str(hash(root))
148 dot.node(parent_dot_name, root.get(
'ID'), shape=
'box')
149 convert_subtree(dot, root, parent_dot_name)
156 def convert_subtree(dot, parent_node, parent_dot_name):
157 if parent_node.tag ==
'SubTree':
158 add_sub_tree(dot, parent_dot_name, parent_node)
160 add_nodes(dot, parent_dot_name, parent_node)
163 def add_sub_tree(dot, parent_dot_name, parent_node):
164 root_tree_name = parent_node.get(
'ID')
165 dot.node(parent_dot_name, root_tree_name, shape=
'box')
166 behavior_tree = find_behavior_tree(xml_tree, root_tree_name)
167 convert_subtree(dot, behavior_tree, parent_dot_name)
170 def add_nodes(dot, parent_dot_name, parent_node):
171 for node
in list(parent_node):
172 label = make_label(node)
176 color=node_color(node.tag),
180 dot_name = str(hash(node))
181 dot.edge(parent_dot_name, dot_name)
182 convert_subtree(dot, node, dot_name)
187 def make_label(node):
188 label =
"< <table border='0' cellspacing='0' cellpadding='0'>"
189 label += f
"<tr><td align='text'><i>{node.tag}</i></td></tr>"
190 name = node.get(
'name')
192 label += f
"<tr><td align='text'><b>{name}</b></td></tr>"
194 for param_name, value
in node.items():
195 label += f
"<tr><td align='left'><sub>{param_name}={value}</sub></td></tr>"
196 label +=
'</table> >'
200 def node_color(node_type):
201 if node_type
in control_nodes:
203 if node_type
in action_nodes:
204 return 'cornflowerblue'
205 if node_type
in condition_nodes:
207 if node_type
in decorator_nodes:
209 if node_type
in subtree_nodes:
217 legend = graphviz.Digraph(graph_attr={
'rankdir':
'LR'})
218 legend.attr(label=
'Legend')
219 legend.node(
'Unknown', shape=
'box', style=
'filled', color=
'grey')
221 'Action',
'Action Node', shape=
'box', style=
'filled', color=
'cornflowerblue'
224 'Condition',
'Condition Node', shape=
'box', style=
'filled', color=
'yellow2'
227 'Control',
'Control Node', shape=
'box', style=
'filled', color=
'chartreuse4'
233 if __name__ ==
'__main__':