Skip to content

Instantly share code, notes, and snippets.

@discipolo
Created October 30, 2015 21:29
Show Gist options
  • Save discipolo/e1721df8312bddf25a7a to your computer and use it in GitHub Desktop.
Save discipolo/e1721df8312bddf25a7a to your computer and use it in GitHub Desktop.
# -*- 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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment