gramps/gramps2/src/plugins/GraphViz.py

397 lines
15 KiB
Python
Raw Normal View History

2002-10-20 19:55:16 +05:30
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000 Donald N. Allingham
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"Generate files/Relationship graph"
import os
import string
import intl
import Utils
import gtk
_ = intl.gettext
from Report import *
from TextDoc import *
_scaled = 0
_single = 1
_multiple = 2
_pagecount_map = {
_("Single (scaled)") : _scaled,
_("Single") : _single,
_("Multiple") : _multiple,
}
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
class GraphVizDialog(ReportDialog):
def __init__(self,database,person):
ReportDialog.__init__(self,database,person)
#------------------------------------------------------------------------
#
# Customization hooks
#
#------------------------------------------------------------------------
def get_title(self):
"""The window title for this dialog"""
return "%s - %s - GRAMPS" % (_("Relationship Graph"),
_("Graphical Reports"))
def get_target_browser_title(self):
"""The title of the window created when the 'browse' button is
clicked in the 'Save As' frame."""
return _("Graphviz File")
def get_print_pagecount_map(self):
"""Set up the list of possible page counts."""
return (_pagecount_map, _("Single (scaled)"))
def get_report_generations(self):
"""Default to 10 generations, no page breaks."""
return (10, 0)
def get_report_filters(self):
"""Set up the list of possible content filters."""
name = self.person.getPrimaryName().getName()
all = GenericFilter.GenericFilter()
all.set_name(_("Entire Database"))
all.add_rule(GenericFilter.Everyone([]))
des = GenericFilter.GenericFilter()
des.set_name(_("Descendants of %s") % name)
des.add_rule(GenericFilter.IsDescendantOf([self.person.getId()]))
ans = GenericFilter.GenericFilter()
ans.set_name(_("Ancestors of %s") % name)
ans.add_rule(GenericFilter.IsAncestorOf([self.person.getId()]))
return [all,des,ans]
def add_user_options(self):
self.arrowstyle_optionmenu = gtk.GtkOptionMenu()
menu = gtk.GtkMenu()
menuitem = gtk.GtkMenuItem(_("Descendants <- Ancestors"))
menuitem.set_data('t', ('none', 'normal'))
menuitem.show()
menu.append(menuitem)
menuitem = gtk.GtkMenuItem(_("Descendants -> Ancestors"))
menuitem.set_data('t', ('normal', 'none'))
menuitem.show()
menu.append(menuitem)
menuitem = gtk.GtkMenuItem(_("Descendants <-> Ancestors"))
menuitem.set_data('t', ('normal', 'normal'))
menuitem.show()
menu.append(menuitem)
menuitem = gtk.GtkMenuItem(_("Descendants - Ancestors"))
menuitem.set_data('t', ('none', 'none'))
menuitem.show()
menu.append(menuitem)
menu.set_active(0)
self.arrowstyle_optionmenu.set_menu(menu)
self.add_frame_option(_("GraphViz Options"),
_("Arrowhead Options"),
self.arrowstyle_optionmenu,
_("Choose the direction that the arrows point."))
msg = _("Include Birth and Death Dates")
self.includedates_cb = gtk.GtkCheckButton(msg)
self.includedates_cb.set_active(1)
self.add_frame_option(_("GraphViz Options"), '',
self.includedates_cb,
_("Include the years that the individual "
"was born and/or died in the graph node "
"labels."))
self.includeurl_cb = gtk.GtkCheckButton(_("Include URLs"))
self.includeurl_cb.set_active(1)
self.add_frame_option(_("GraphViz Options"), '',
self.includeurl_cb,
_("Include a URL in each graph node so "
"that PDF and imagemap files can be "
"generated that contain active links "
"to the files generated by the 'Generate "
"Web Site' report."))
self.colorize_cb = gtk.GtkCheckButton(_("Colorize Graph"))
self.colorize_cb.set_active(1)
self.add_frame_option(_("GraphViz Options"),
'',
self.colorize_cb,
_("Males will be outlined in blue, females "
"will be outlined in pink. If the sex of "
"an individual is unknown it will be "
"outlined in black."))
self.adoptionsdashed_cb = gtk.GtkCheckButton(_("Indicate non-birth relationships with dashed lines"))
self.adoptionsdashed_cb.set_active(1)
self.add_frame_option(_("GraphViz Options"),
'',
self.adoptionsdashed_cb,
_("Non-birth relationships will show up "
"as dashed lines in the graph."))
tb_margin_adj = gtk.GtkAdjustment(value=0.5, lower=0.25,
upper=100.0, step_incr=0.25)
lr_margin_adj = gtk.GtkAdjustment(value=0.5, lower=0.25,
upper=100.0, step_incr=0.25)
self.tb_margin_sb = gtk.GtkSpinButton(adj=tb_margin_adj, digits=2)
self.lr_margin_sb = gtk.GtkSpinButton(adj=lr_margin_adj, digits=2)
self.add_frame_option(_("GraphViz Options"),
_("Top & Bottom Margins"),
self.tb_margin_sb)
self.add_frame_option(_("GraphViz Options"),
_("Left & Right Margins"),
self.lr_margin_sb)
hpages_adj = gtk.GtkAdjustment(value=1, lower=1, upper=25, step_incr=1)
vpages_adj = gtk.GtkAdjustment(value=1, lower=1, upper=25, step_incr=1)
self.hpages_sb = gtk.GtkSpinButton(adj=hpages_adj, digits=0)
self.vpages_sb = gtk.GtkSpinButton(adj=vpages_adj, digits=0)
self.add_frame_option(_("GraphViz Options"),
_("Number of Horizontal Pages"),
self.hpages_sb,
_("GraphViz can create very large graphs by "
"spreading the graph across a rectangular "
"array of pages. This controls the number "
"pages in the array horizontally."))
self.add_frame_option(_("GraphViz Options"),
_("Number of Vertical Pages"),
self.vpages_sb,
_("GraphViz can create very large graphs "
"by spreading the graph across a "
"rectangular array of pages. This "
"controls the number pages in the array "
"vertically."))
#------------------------------------------------------------------------
#
# Functions related to selecting/changing the current file format
#
#------------------------------------------------------------------------
def make_doc_menu(self):
"""Build a one item menu of document types that are
appropriate for this report."""
map = {"Graphviz (dot)" : None}
myMenu = Utils.build_string_optmenu(map, None)
self.format_menu.set_menu(myMenu)
def make_document(self):
"""Do Nothing. This document will be created in the
make_report routine."""
pass
#------------------------------------------------------------------------
#
# Functions related to setting up the dialog window
#
#------------------------------------------------------------------------
def setup_style_frame(self):
"""The style frame is not used in this dialog."""
pass
#------------------------------------------------------------------------
#
# Functions related to retrieving data from the dialog window
#
#------------------------------------------------------------------------
def parse_style_frame(self):
"""The style frame is not used in this dialog."""
pass
def parse_other_frames(self):
menu = self.arrowstyle_optionmenu.get_menu()
self.arrowheadstyle, self.arrowtailstyle = menu.get_active().get_data('t')
self.includedates = self.includedates_cb.get_active()
self.includeurl = self.includeurl_cb.get_active()
self.tb_margin = self.tb_margin_sb.get_value_as_float()
self.lr_margin = self.lr_margin_sb.get_value_as_float()
self.colorize = self.colorize_cb.get_active()
self.adoptionsdashed = self.adoptionsdashed_cb.get_active()
self.hpages = self.hpages_sb.get_value_as_int()
self.vpages = self.vpages_sb.get_value_as_int()
#------------------------------------------------------------------------
#
# Functions related to creating the actual report document.
#
#------------------------------------------------------------------------
def make_report(self):
"""Create the object that will produce the GraphViz file."""
width = self.paper.get_width_inches()
height = self.paper.get_height_inches()
file = open(self.target_path,"w")
ind_list = self.filter.apply(self.db.getPersonMap().values())
write_dot(file, ind_list, self.orien, width, height,
self.tb_margin, self.lr_margin, self.hpages,
self.vpages, self.includedates, self.includeurl,
self.colorize, self.adoptionsdashed, self.arrowheadstyle, self.arrowtailstyle)
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
def report(database,person):
GraphVizDialog(database,person)
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
def write_dot(file, ind_list, orien, width, height, tb_margin,
lr_margin, hpages, vpages, includedates, includeurl,
colorize, adoptionsdashed, arrowheadstyle, arrowtailstyle):
file.write("digraph g {\n")
file.write("bgcolor=white;\n")
file.write("rankdir=LR;\n")
file.write("center=1;\n")
file.write("margin=0.5;\n")
file.write("ratio=fill;\n")
file.write("size=\"%3.1fin,%3.1fin\";\n" % ((width*hpages)-(lr_margin*2)-((hpages-1)*1.0),
(height*vpages)-(tb_margin*2)-((vpages-1)*1.0)))
file.write("page=\"%3.1fin,%3.1fin\";\n" % (width,height))
if orien == PAPER_LANDSCAPE:
file.write("rotate=90;\n")
if len(ind_list) > 1:
dump_index(ind_list,file,includedates,includeurl,colorize)
dump_person(ind_list,file,adoptionsdashed,arrowheadstyle,arrowtailstyle)
file.write("}\n")
file.close()
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
def dump_person(person_list,file,adoptionsdashed,arrowheadstyle,arrowtailstyle):
for person in person_list:
pid = string.replace(person.getId(),'-','_')
family, mrel, frel = person.getMainParentsRel()
if family == None:
continue
father = family.getFather()
if father and father in person_list:
fid = string.replace(father.getId(),'-','_')
file.write('p%s -> p%s [' % (pid, fid))
file.write('arrowhead=%s, arrowtail=%s, ' % (arrowheadstyle, arrowtailstyle))
if adoptionsdashed and frel != _("Birth"):
file.write('style=dashed')
else:
file.write('style=solid')
file.write('];\n')
mother = family.getMother()
if mother and mother in person_list:
mid = string.replace(mother.getId(),'-','_')
file.write('p%s -> p%s [' % (pid, mid))
file.write('arrowhead=%s, arrowtail=%s, ' % (arrowheadstyle, arrowtailstyle))
if adoptionsdashed and mrel != _("Birth"):
file.write('style=dashed')
else:
file.write('style=solid')
file.write('];\n')
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
def dump_index(person_list,file,includedates,includeurl,colorize):
for person in person_list:
label = person.getPrimaryName().getName()
id = string.replace(person.getId(),'-','_')
if includedates:
if person.getBirth().getDateObj().getYearValid():
birth = '%i' % person.getBirth().getDateObj().getYear()
else:
birth = ''
if person.getDeath().getDateObj().getYearValid():
death = '%i' % person.getDeath().getDateObj().getYear()
else:
death = ''
label = label + '\\n(%s - %s)' % (birth, death)
file.write('p%s [shape=box, ' % id)
if includeurl:
file.write('URL="%s.html", ' % id)
if colorize:
gender = person.getGender()
if gender == person.male:
file.write('color=dodgerblue4, ')
elif gender == person.female:
file.write('color=deeppink, ')
else:
file.write('color=black, ')
file.write('fontname="Arial", label="%s"];\n' % label)
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
def get_description():
return _("Generates relationship graphs, currently only in GraphViz "
"format. GraphViz (dot) can transform the graph into "
"postscript, jpeg, png, vrml, svg, and many other formats. "
"For more information or to get a copy of GraphViz, "
"goto http://www.graphviz.org")
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
from Plugins import register_report
register_report(
report,
_("Relationship Graph"),
status=(_("Beta")),
category=_("Graphical Reports"),
description=get_description()
)