1717# for instructions
1818
1919import argparse
20- import xml .etree .ElementTree
20+ import xml .etree .ElementTree as ET
2121
2222import graphviz # pip3 install graphviz
2323
7575 'SubTree' ,
7676]
7777
78- global xml_tree
7978
80-
81- def main ():
82- global xml_tree
79+ def main () -> None :
8380 args = parse_command_line ()
84- xml_tree = xml . etree . ElementTree .parse (args .behavior_tree )
81+ xml_tree = ET .parse (args .behavior_tree )
8582 root_tree_name = find_root_tree_name (xml_tree )
8683 behavior_tree = find_behavior_tree (xml_tree , root_tree_name )
87- dot = convert2dot (behavior_tree )
84+ dot = convert2dot (behavior_tree , xml_tree )
8885 if args .legend :
8986 legend = make_legend ()
9087 legend .format = 'png'
@@ -96,7 +93,7 @@ def main():
9693 dot .render (args .image_out , view = args .display )
9794
9895
99- def parse_command_line ():
96+ def parse_command_line () -> argparse . Namespace :
10097 parser = argparse .ArgumentParser (
10198 description = 'Convert a behavior tree XML file to an image'
10299 )
@@ -124,11 +121,15 @@ def parse_command_line():
124121 return parser .parse_args ()
125122
126123
127- def find_root_tree_name (xml_tree ):
128- return xml_tree .getroot ().get ('main_tree_to_execute' )
124+ def find_root_tree_name (xml_tree : ET .ElementTree ) -> str :
125+ root = xml_tree .getroot ()
126+ main_tree = root .get ('main_tree_to_execute' )
127+ if main_tree is None :
128+ raise RuntimeError ('No main_tree_to_execute attribute found in XML root' )
129+ return main_tree
129130
130131
131- def find_behavior_tree (xml_tree , tree_name ) :
132+ def find_behavior_tree (xml_tree : ET . ElementTree , tree_name : str ) -> ET . Element :
132133 trees = xml_tree .findall ('BehaviorTree' )
133134 if len (trees ) == 0 :
134135 raise RuntimeError ('No behavior trees were found in the XML file' )
@@ -141,33 +142,50 @@ def find_behavior_tree(xml_tree, tree_name):
141142
142143
143144# Generate a dot description of the root of the behavior tree.
144- def convert2dot (behavior_tree ) :
145+ def convert2dot (behavior_tree : ET . Element , xml_tree : ET . ElementTree ) -> graphviz . Digraph :
145146 dot = graphviz .Digraph ()
146147 root = behavior_tree
147148 parent_dot_name = str (hash (root ))
148149 dot .node (parent_dot_name , root .get ('ID' ), shape = 'box' )
149- convert_subtree (dot , root , parent_dot_name )
150+ convert_subtree (dot , root , parent_dot_name , xml_tree )
150151 return dot
151152
152153
153154# Recursive function. We add the children to the dot file, and then recursively
154155# call this function on the children. Nodes are given an ID that is the hash
155156# of the node to ensure each is unique.
156- def convert_subtree (dot , parent_node , parent_dot_name ):
157+ def convert_subtree (
158+ dot : graphviz .Digraph ,
159+ parent_node : ET .Element ,
160+ parent_dot_name : str ,
161+ xml_tree : ET .ElementTree ,
162+ ) -> None :
157163 if parent_node .tag == 'SubTree' :
158- add_sub_tree (dot , parent_dot_name , parent_node )
164+ add_sub_tree (dot , parent_dot_name , parent_node , xml_tree )
159165 else :
160- add_nodes (dot , parent_dot_name , parent_node )
166+ add_nodes (dot , parent_dot_name , parent_node , xml_tree )
161167
162168
163- def add_sub_tree (dot , parent_dot_name , parent_node ):
169+ def add_sub_tree (
170+ dot : graphviz .Digraph ,
171+ parent_dot_name : str ,
172+ parent_node : ET .Element ,
173+ xml_tree : ET .ElementTree ,
174+ ) -> None :
164175 root_tree_name = parent_node .get ('ID' )
176+ if root_tree_name is None :
177+ raise RuntimeError ('SubTree node has no ID attribute' )
165178 dot .node (parent_dot_name , root_tree_name , shape = 'box' )
166179 behavior_tree = find_behavior_tree (xml_tree , root_tree_name )
167- convert_subtree (dot , behavior_tree , parent_dot_name )
180+ convert_subtree (dot , behavior_tree , parent_dot_name , xml_tree )
168181
169182
170- def add_nodes (dot , parent_dot_name , parent_node ):
183+ def add_nodes (
184+ dot : graphviz .Digraph ,
185+ parent_dot_name : str ,
186+ parent_node : ET .Element ,
187+ xml_tree : ET .ElementTree ,
188+ ) -> None :
171189 for node in list (parent_node ):
172190 label = make_label (node )
173191 dot .node (
@@ -179,12 +197,12 @@ def add_nodes(dot, parent_dot_name, parent_node):
179197 )
180198 dot_name = str (hash (node ))
181199 dot .edge (parent_dot_name , dot_name )
182- convert_subtree (dot , node , dot_name )
200+ convert_subtree (dot , node , dot_name , xml_tree )
183201
184202
185203# The node label contains the:
186204# type, the name if provided, and the parameters.
187- def make_label (node ) :
205+ def make_label (node : ET . Element ) -> str :
188206 label = "< <table border='0' cellspacing='0' cellpadding='0'>"
189207 label += f"<tr><td align='text'><i>{ node .tag } </i></td></tr>"
190208 name = node .get ('name' )
@@ -197,7 +215,7 @@ def make_label(node):
197215 return label
198216
199217
200- def node_color (node_type ) :
218+ def node_color (node_type : str ) -> str :
201219 if node_type in control_nodes :
202220 return 'chartreuse4'
203221 if node_type in action_nodes :
@@ -213,7 +231,7 @@ def node_color(node_type):
213231
214232
215233# creates a legend which can be provided with the other images.
216- def make_legend ():
234+ def make_legend () -> graphviz . Digraph :
217235 legend = graphviz .Digraph (graph_attr = {'rankdir' : 'LR' })
218236 legend .attr (label = 'Legend' )
219237 legend .node ('Unknown' , shape = 'box' , style = 'filled' , color = 'grey' )
0 commit comments