text: """
  This generate diagram in .png and .svg from neo.core
  
  
  Author: sgarcia
  """
  
  from datetime import datetime
  
  import numpy as np
  import quantities as pq
  from matplotlib import pyplot
  from matplotlib.patches import Rectangle, ArrowStyle, FancyArrowPatch
  from matplotlib.font_manager import FontProperties
  
  from neo.test.generate_datasets import fake_neo
  
  line_heigth = .22
  fontsize = 10.5
  left_text_shift = .1
  dpi = 100
  
  
  def get_rect_height(name, obj):
      '''
      calculate rectangle height
      '''
      nlines = 1.5
      nlines += len(getattr(obj, '_all_attrs', []))
      nlines += len(getattr(obj, '_single_child_objects', []))
      nlines += len(getattr(obj, '_multi_child_objects', []))
      nlines += len(getattr(obj, '_multi_parent_objects', []))
      return nlines * line_heigth
  
  
  def annotate(ax, coord1, coord2, connectionstyle, color, alpha):
      arrowprops = dict(arrowstyle='fancy',
                        # ~ patchB=p,
                        shrinkA=.3, shrinkB=.3,
                        fc=color, ec=color,
                        connectionstyle=connectionstyle,
                        alpha=alpha)
      bbox = dict(boxstyle="square", fc="w")
      a = ax.annotate('', coord1, coord2,
                      # xycoords="figure fraction",
                      # textcoords="figure fraction",
                      ha="right", va="center",
                      size=fontsize,
                      arrowprops=arrowprops,
                      bbox=bbox)
      a.set_zorder(-4)
  
  
  def calc_coordinates(pos, height):
      x = pos[0]
      y = pos[1] + height - line_heigth * .5
  
      return pos[0], y
  
  
  def generate_diagram(filename, rect_pos, rect_width, figsize):
      rw = rect_width
  
      fig = pyplot.figure(figsize=figsize)
      ax = fig.add_axes([0, 0, 1, 1])
  
      all_h = {}
      objs = {}
      for name in rect_pos:
          objs[name] = fake_neo(name)
          all_h[name] = get_rect_height(name, objs[name])
  
      # draw connections
      color = ['c', 'm', 'y']
      alpha = [1., 1., 0.3]
      for name, pos in rect_pos.items():
          obj = objs[name]
          relationships = [getattr(obj, '_single_child_objects', []),
                           getattr(obj, '_multi_child_objects', []),
                           getattr(obj, '_child_properties', [])]
  
          for r in range(3):
              for ch_name in relationships[r]:
                  x1, y1 = calc_coordinates(rect_pos[ch_name], all_h[ch_name])
                  x2, y2 = calc_coordinates(pos, all_h[name])
  
                  if r in [0, 2]:
                      x2 += rect_width
                      connectionstyle = "arc3,rad=-0.2"
                  elif y2 >= y1:
                      connectionstyle = "arc3,rad=0.7"
                  else:
                      connectionstyle = "arc3,rad=-0.7"
  
                  annotate(ax=ax, coord1=(x1, y1), coord2=(x2, y2),
                           connectionstyle=connectionstyle,
                           color=color[r], alpha=alpha[r])
  
      # draw boxes
      for name, pos in rect_pos.items():
          htotal = all_h[name]
          obj = objs[name]
          allrelationship = (list(getattr(obj, '_child_containers', []))
                             + list(getattr(obj, '_multi_parent_containers', [])))
  
          rect = Rectangle(pos, rect_width, htotal,
                           facecolor='w', edgecolor='k', linewidth=2.)
          ax.add_patch(rect)
  
          # title green
          pos2 = pos[0], pos[1] + htotal - line_heigth * 1.5
          rect = Rectangle(pos2, rect_width, line_heigth * 1.5,
                           facecolor='g', edgecolor='k', alpha=.5, linewidth=2.)
          ax.add_patch(rect)
  
          # single relationship
          relationship = getattr(obj, '_single_child_objects', [])
          pos2 = pos[1] + htotal - line_heigth * (1.5 + len(relationship))
          rect_height = len(relationship) * line_heigth
  
          rect = Rectangle((pos[0], pos2), rect_width, rect_height,
                           facecolor='c', edgecolor='k', alpha=.5)
          ax.add_patch(rect)
  
          # multi relationship
          relationship = (list(getattr(obj, '_multi_child_objects', []))
                          + list(getattr(obj, '_multi_parent_containers', [])))
          pos2 = (pos[1] + htotal - line_heigth * (1.5 + len(relationship))
                  - rect_height)
          rect_height = len(relationship) * line_heigth
  
          rect = Rectangle((pos[0], pos2), rect_width, rect_height,
                           facecolor='m', edgecolor='k', alpha=.5)
          ax.add_patch(rect)
  
          # necessary attr
          pos2 = (pos[1] + htotal
                  - line_heigth * (1.5 + len(allrelationship) + len(obj._necessary_attrs)))
          rect = Rectangle((pos[0], pos2), rect_width,
                           line_heigth * len(obj._necessary_attrs),
                           facecolor='r', edgecolor='k', alpha=.5)
          ax.add_patch(rect)
  
          # name
          if hasattr(obj, '_quantity_attr'):
              post = '* '
          else:
              post = ''
          ax.text(pos[0] + rect_width / 2., pos[1] + htotal - line_heigth * 1.5 / 2.,
                  name + post,
                  horizontalalignment='center', verticalalignment='center',
                  fontsize=fontsize + 2,
                  fontproperties=FontProperties(weight='bold'),
                  )
  
          # relationship
          for i, relat in enumerate(allrelationship):
              ax.text(pos[0] + left_text_shift, pos[1] + htotal - line_heigth * (i + 2),
                      relat + ': list',
                      horizontalalignment='left', verticalalignment='center',
                      fontsize=fontsize,
                      )
          # attributes
          for i, attr in enumerate(obj._all_attrs):
              attrname, attrtype = attr[0], attr[1]
              t1 = attrname
              if (hasattr(obj, '_quantity_attr')
                      and obj._quantity_attr == attrname):
                  t1 = attrname + '(object itself)'
              else:
                  t1 = attrname
  
              if attrtype == pq.Quantity:
                  if attr[2] == 0:
                      t2 = 'Quantity scalar'
                  else:
                      t2 = 'Quantity %dD' % attr[2]
              elif attrtype == np.ndarray:
                  t2 = "np.ndarray %dD dt='%s'" % (attr[2], attr[3].kind)
              elif attrtype == datetime:
                  t2 = 'datetime'
              else:
                  t2 = attrtype.__name__
  
              t = t1 + ' :  ' + t2
              ax.text(pos[0] + left_text_shift,
                      pos[1] + htotal - line_heigth * (i + len(allrelationship) + 2),
                      t,
                      horizontalalignment='left', verticalalignment='center',
                      fontsize=fontsize,
                      )
  
      xlim, ylim = figsize
      ax.set_xlim(0, xlim)
      ax.set_ylim(0, ylim)
  
      ax.set_xticks([])
      ax.set_yticks([])
      fig.savefig(filename, dpi=dpi)
  
  
  def generate_diagram_simple():
      figsize = (18, 12)
      rw = rect_width = 3.
      bf = blank_fact = 1.2
      rect_pos = {'Block': (.5 + rw * bf * 0, 4),
                  'Segment': (.5 + rw * bf * 1, .5),
                  'Event': (.5 + rw * bf * 4, 3.0),
                  'Epoch': (.5 + rw * bf * 4, 1.0),
                  'Group': (.5 + rw * bf * 1, 7.5),
                  'ChannelView': (.5 + rw * bf * 2., 9.9),
                  'SpikeTrain': (.5 + rw * bf * 3, 7.5),
                  'IrregularlySampledSignal': (.5 + rw * bf * 3, 0.5),
                  'AnalogSignal': (.5 + rw * bf * 3, 4.9),
                  }
      # todo: add ImageSequence, RegionOfInterest
      generate_diagram('simple_generated_diagram.svg',
                       rect_pos, rect_width, figsize)
      generate_diagram('simple_generated_diagram.png',
                       rect_pos, rect_width, figsize)
  
  
  if __name__ == '__main__':
      generate_diagram_simple()
      pyplot.show()
  