TreeMap plugin (converted Text2mindmap custom tool)

NorfCran edited this page Jul 9, 2015 · 4 revisions

The TreeMap plugin is a transformed version of the custom tool Text2mindmap-custom-tool.

The only drawback of the plugin over the custom tool is an inaccessibility allowing the image resizing.

Seasons
	Spring
		March
		__April__
			2
			11
			9
		May
	__Summer__
		June
		July
		August
	**Autumn**
		September
		October
		November
			z
			x
			y
	Winter
	aaa
		//December//
		January
		February
	a
	b
	__c__
	__d__
	e

Output of this TreeMap plugin:

Recommended to download the plugin from the following link: treemapeditor.py

The code is listed on the following lines:

# -*- coding: utf-8 -*-
#
# treemapeditor.py
#
# This is a plugin for Zim, which creates treeMap graph based on tabbed keywords.
# Initial version comes from the following source: http://blog.ynema.com/?p=192
#
#
# Author: NorfCran <norfcran@gmail.com>
# Date: 2015-07-09
# Copyright (c) 2015, released under the GNU GPL v2 or higher
#
#

import logging
try:
  import pydot
  has_dotmod = True
except ImportError:
  has_dotmod = False
     

from zim.plugins.base.imagegenerator import ImageGeneratorPlugin, ImageGeneratorClass
from zim.fs import File, TmpFile
from zim.config import data_file
from zim.applications import Application, ApplicationError

# TODO put these commands in preferences
dotcmd = ('twopi', '-Tpng', '-o')

logger = logging.getLogger('zim.plugins')

class InsertTreeMapPlugin(ImageGeneratorPlugin):

  plugin_info = {
    'name': _('Insert TreeMap'), # T: plugin name
    'description': _('''\
This plugin provides a hierachical editor for zim based on Twopi (part of Graphviz).
'''), # T: plugin description
        'help': 'Plugins:TreeMap Editor',
    'author': 'NorfCran',
  }

  object_type = 'treemap'
  short_label = _('TreeMap') # T: menu item
  insert_label = _('Insert TreeMap') # T: menu item
  edit_label = _('_Edit TreeMap') # T: menu item
  syntax = None

  #@classmethod
  #def check_dependencies(klass):
  #  has_dotmod = True
  #  try:
  #      import pydot
  #  except ImportError:
  #      has_dotmod = True
  #  return has_dotmod, [("GraphViz", has_dotmod, True)]

  @classmethod
  def check_dependencies(klass):
     has_dotcmd = Application(dotcmd).tryexec()
     return (has_dotmod and has_dotcmd), \
        [("pydot", has_dotmod, True), ("GraphViz", has_dotcmd, True)]


class TreeMapGenerator(ImageGeneratorClass):

  uses_log_file = False

  object_type = 'treemap'
  scriptname = 'treemap.tab'
  imagename = 'treemap.png'

  def __init__(self, plugin):
    ImageGeneratorClass.__init__(self, plugin)
    self.dotfile = TmpFile(self.scriptname)
    self.dotfile.touch()
    self.pngfile = File(self.dotfile.path[:-4] + '.png') # len('.dot') == 4

  def generate_image(self, text):
    # string has to be treated differently, if list is passed do nothing
    if type(text) is str:
      text = text.split("\n")
    if len(text) == 1:
      text = [text[0],"\tnewline + TAB + keyword"]
    self.dotfile.write(self.createGraphFromEdges(self.createEdges(text)))
    # Call GraphViz
    try:
      dot = Application(dotcmd)
      dot.run((self.pngfile, self.dotfile))
    except ApplicationError:
      return None, None # Sorry, no log
    else:
      if self.pngfile.exists():
        return self.pngfile, None
      else:
        return None, None

  def cleanup(self):
    self.dotfile.remove()
    self.pngfile.remove()

  # Configuration variables for the final treemap
  ranksep = '2 equally' # distance between nodes (suggested values: 1-3) => (graphviz varialbe)
  overlap='true' # whether the nodes overlap each other (true or false) => (graphviz varialbe)
  splines='true' # whether the lines are straight or curved (true or false) => (graphviz varialbe)
  gen_dot = True # whether the dot file should be generated => (python varialbe)
  plain_txt = True

  # the list can be managed by a manually defined array of colors
  # http://www.graphviz.org/doc/info/colors.html
  def generateColors(self):
    colors_nodes = self.initArrayOfcolors('black', ['grey', '#fdd49e', '#fc8d59', '#d7301f', '#b00300', '#700f00'])
    colors_font = self.initArrayOfcolors('white', ['black', 'black', 'black'])
    return (colors_nodes, colors_font)

  def initArrayOfcolors(self, default, colors):
    # generates an array of 12 colors
    colors_nodes = [default for x in range(12)]
    return colors + colors_nodes[len(colors):]

  def getAttributes(self, node, level):
    # provides access to colors
    colors_nodes, colors_font = self.generateColors()
    # investigates edge
    # root node
    if level is 0:
      return {'shape':'box, filled',
      'fillcolor':'gray',
      'style':'"filled"',
      'penwidth':'3',
      'color':colors_nodes[level + 1],
      'fontcolor':'black',
      'fontsize':'32'}
    # default edge
    if type(node) is tuple:
      return {
      'penwidth':'2',
      'color':colors_nodes[level + 1]}
    # investigates node
    # remove whitespaces
    node = node.strip()
    # __underlined__
    if node.startswith("\"__") and node.endswith("__\""):
      return {'shape':'box',
      'style':'"rounded,filled"',
      'penwidth':'2',
      'label':node[2:-2],
      'fillcolor':'yellow',
      'color':colors_nodes[level],
      'fontcolor':'black'}
    # **bold**
    if node.startswith("\"**") and node.endswith("**\""):
      return {'shape':'box',
      'style':'"rounded, filled"',
      'penwidth':'2',
      'label':node[2:-2],
      'color':'black',
      'fillcolor':'red',
      'fontcolor':'black',
      'fontsize':'15'}
    # //italic//
    if node.startswith("\"//") and node.endswith("//\""):
      return {'shape':'box',
      'style':'"rounded, filled"',
      'penwidth':'2',
      'label':node[2:-2],
      'color':'black',
      'fillcolor':'green',
      'fontcolor':'black', }
    # standard node
    else:
      return {'shape':'box',
      'style':'filled',
      'penwidth':'2',
      'fillcolor':colors_nodes[level],
      'color':colors_nodes[level + 1],
      'fontcolor':colors_font[level]}

  def createEdges(self, lines):
    edge_list = ['' for x in range(50)]
    edges = []
    for line in lines:
      # identify right indent (tabs excluding tabs in the text)
      tabs_all = line.count('\t')
      line = line.strip().replace(":", "")
      pos = tabs_all - line.count('\t')
      # removes comments (tabbed text)
      index = line.find("\t")
      if index is not -1: line = line[:index]
      # assign extracted text from a line
      edge_list[pos] = "\"%s\"" % line
      if pos:
        edges.append((edge_list[pos - 1], edge_list[pos], pos - 1))
    return edges

  def createGraphFromEdges(self, edges):
    # defines graph (according to configuration variables)
    g = pydot.Dot(splines=self.splines, overlap=self.overlap, ranksep=self.ranksep, root="%s" % edges[0][0])
    # iterates through edges
    for edge in edges:
      g.add_node(pydot.Node(edge[0], **self.getAttributes(edge[0], edge[2])))
      g.add_node(pydot.Node(edge[1], **self.getAttributes(edge[1], edge[2] + 1)))
      g.add_edge(pydot.Edge(edge[0], edge[1], **self.getAttributes((edge[0], edge[0]), edge[2])))
    # in a case that there is only one mindmap on a page there is no reason for extension of a mm_title to a mm_title_rootnode
    return g.create_dot()
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.