vcgutils.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
  2. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
  3. #
  4. # This file is part of logilab-common.
  5. #
  6. # logilab-common is free software: you can redistribute it and/or modify it under
  7. # the terms of the GNU Lesser General Public License as published by the Free
  8. # Software Foundation, either version 2.1 of the License, or (at your option) any
  9. # later version.
  10. #
  11. # logilab-common is distributed in the hope that it will be useful, but WITHOUT
  12. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  13. # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  14. # details.
  15. #
  16. # You should have received a copy of the GNU Lesser General Public License along
  17. # with logilab-common. If not, see <http://www.gnu.org/licenses/>.
  18. """Functions to generate files readable with Georg Sander's vcg
  19. (Visualization of Compiler Graphs).
  20. You can download vcg at http://rw4.cs.uni-sb.de/~sander/html/gshome.html
  21. Note that vcg exists as a debian package.
  22. See vcg's documentation for explanation about the different values that
  23. maybe used for the functions parameters.
  24. """
  25. __docformat__ = "restructuredtext en"
  26. import string
  27. ATTRS_VAL = {
  28. 'algos': ('dfs', 'tree', 'minbackward',
  29. 'left_to_right', 'right_to_left',
  30. 'top_to_bottom', 'bottom_to_top',
  31. 'maxdepth', 'maxdepthslow', 'mindepth', 'mindepthslow',
  32. 'mindegree', 'minindegree', 'minoutdegree',
  33. 'maxdegree', 'maxindegree', 'maxoutdegree'),
  34. 'booleans': ('yes', 'no'),
  35. 'colors': ('black', 'white', 'blue', 'red', 'green', 'yellow',
  36. 'magenta', 'lightgrey',
  37. 'cyan', 'darkgrey', 'darkblue', 'darkred', 'darkgreen',
  38. 'darkyellow', 'darkmagenta', 'darkcyan', 'gold',
  39. 'lightblue', 'lightred', 'lightgreen', 'lightyellow',
  40. 'lightmagenta', 'lightcyan', 'lilac', 'turquoise',
  41. 'aquamarine', 'khaki', 'purple', 'yellowgreen', 'pink',
  42. 'orange', 'orchid'),
  43. 'shapes': ('box', 'ellipse', 'rhomb', 'triangle'),
  44. 'textmodes': ('center', 'left_justify', 'right_justify'),
  45. 'arrowstyles': ('solid', 'line', 'none'),
  46. 'linestyles': ('continuous', 'dashed', 'dotted', 'invisible'),
  47. }
  48. # meaning of possible values:
  49. # O -> string
  50. # 1 -> int
  51. # list -> value in list
  52. GRAPH_ATTRS = {
  53. 'title': 0,
  54. 'label': 0,
  55. 'color': ATTRS_VAL['colors'],
  56. 'textcolor': ATTRS_VAL['colors'],
  57. 'bordercolor': ATTRS_VAL['colors'],
  58. 'width': 1,
  59. 'height': 1,
  60. 'borderwidth': 1,
  61. 'textmode': ATTRS_VAL['textmodes'],
  62. 'shape': ATTRS_VAL['shapes'],
  63. 'shrink': 1,
  64. 'stretch': 1,
  65. 'orientation': ATTRS_VAL['algos'],
  66. 'vertical_order': 1,
  67. 'horizontal_order': 1,
  68. 'xspace': 1,
  69. 'yspace': 1,
  70. 'layoutalgorithm': ATTRS_VAL['algos'],
  71. 'late_edge_labels': ATTRS_VAL['booleans'],
  72. 'display_edge_labels': ATTRS_VAL['booleans'],
  73. 'dirty_edge_labels': ATTRS_VAL['booleans'],
  74. 'finetuning': ATTRS_VAL['booleans'],
  75. 'manhattan_edges': ATTRS_VAL['booleans'],
  76. 'smanhattan_edges': ATTRS_VAL['booleans'],
  77. 'port_sharing': ATTRS_VAL['booleans'],
  78. 'edges': ATTRS_VAL['booleans'],
  79. 'nodes': ATTRS_VAL['booleans'],
  80. 'splines': ATTRS_VAL['booleans'],
  81. }
  82. NODE_ATTRS = {
  83. 'title': 0,
  84. 'label': 0,
  85. 'color': ATTRS_VAL['colors'],
  86. 'textcolor': ATTRS_VAL['colors'],
  87. 'bordercolor': ATTRS_VAL['colors'],
  88. 'width': 1,
  89. 'height': 1,
  90. 'borderwidth': 1,
  91. 'textmode': ATTRS_VAL['textmodes'],
  92. 'shape': ATTRS_VAL['shapes'],
  93. 'shrink': 1,
  94. 'stretch': 1,
  95. 'vertical_order': 1,
  96. 'horizontal_order': 1,
  97. }
  98. EDGE_ATTRS = {
  99. 'sourcename': 0,
  100. 'targetname': 0,
  101. 'label': 0,
  102. 'linestyle': ATTRS_VAL['linestyles'],
  103. 'class': 1,
  104. 'thickness': 0,
  105. 'color': ATTRS_VAL['colors'],
  106. 'textcolor': ATTRS_VAL['colors'],
  107. 'arrowcolor': ATTRS_VAL['colors'],
  108. 'backarrowcolor': ATTRS_VAL['colors'],
  109. 'arrowsize': 1,
  110. 'backarrowsize': 1,
  111. 'arrowstyle': ATTRS_VAL['arrowstyles'],
  112. 'backarrowstyle': ATTRS_VAL['arrowstyles'],
  113. 'textmode': ATTRS_VAL['textmodes'],
  114. 'priority': 1,
  115. 'anchor': 1,
  116. 'horizontal_order': 1,
  117. }
  118. # Misc utilities ###############################################################
  119. def latin_to_vcg(st):
  120. """Convert latin characters using vcg escape sequence.
  121. """
  122. for char in st:
  123. if char not in string.ascii_letters:
  124. try:
  125. num = ord(char)
  126. if num >= 192:
  127. st = st.replace(char, r'\fi%d'%ord(char))
  128. except:
  129. pass
  130. return st
  131. class VCGPrinter:
  132. """A vcg graph writer.
  133. """
  134. def __init__(self, output_stream):
  135. self._stream = output_stream
  136. self._indent = ''
  137. def open_graph(self, **args):
  138. """open a vcg graph
  139. """
  140. self._stream.write('%sgraph:{\n'%self._indent)
  141. self._inc_indent()
  142. self._write_attributes(GRAPH_ATTRS, **args)
  143. def close_graph(self):
  144. """close a vcg graph
  145. """
  146. self._dec_indent()
  147. self._stream.write('%s}\n'%self._indent)
  148. def node(self, title, **args):
  149. """draw a node
  150. """
  151. self._stream.write('%snode: {title:"%s"' % (self._indent, title))
  152. self._write_attributes(NODE_ATTRS, **args)
  153. self._stream.write('}\n')
  154. def edge(self, from_node, to_node, edge_type='', **args):
  155. """draw an edge from a node to another.
  156. """
  157. self._stream.write(
  158. '%s%sedge: {sourcename:"%s" targetname:"%s"' % (
  159. self._indent, edge_type, from_node, to_node))
  160. self._write_attributes(EDGE_ATTRS, **args)
  161. self._stream.write('}\n')
  162. # private ##################################################################
  163. def _write_attributes(self, attributes_dict, **args):
  164. """write graph, node or edge attributes
  165. """
  166. for key, value in args.items():
  167. try:
  168. _type = attributes_dict[key]
  169. except KeyError:
  170. raise Exception('''no such attribute %s
  171. possible attributes are %s''' % (key, attributes_dict.keys()))
  172. if not _type:
  173. self._stream.write('%s%s:"%s"\n' % (self._indent, key, value))
  174. elif _type == 1:
  175. self._stream.write('%s%s:%s\n' % (self._indent, key,
  176. int(value)))
  177. elif value in _type:
  178. self._stream.write('%s%s:%s\n' % (self._indent, key, value))
  179. else:
  180. raise Exception('''value %s isn\'t correct for attribute %s
  181. correct values are %s''' % (value, key, _type))
  182. def _inc_indent(self):
  183. """increment indentation
  184. """
  185. self._indent = ' %s' % self._indent
  186. def _dec_indent(self):
  187. """decrement indentation
  188. """
  189. self._indent = self._indent[:-2]