* src/plugins/rel_it.py: Italian relationship calculator
added * src/plugins/RelGraph.py: Advanced relationship graph report generator svn: r2232
This commit is contained in:
		
							
								
								
									
										912
									
								
								src/plugins/RelGraph.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										912
									
								
								src/plugins/RelGraph.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,912 @@
 | 
			
		||||
#
 | 
			
		||||
# Gramps - a GTK+/GNOME based genealogy program
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2000-2003  Donald N. Allingham
 | 
			
		||||
# Contributions by Lorenzo Cappelletti <lorenzo.cappelletti@email.it>
 | 
			
		||||
#
 | 
			
		||||
# 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"
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# python modules
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
import os
 | 
			
		||||
import string
 | 
			
		||||
 | 
			
		||||
from sets import Set
 | 
			
		||||
from time import asctime
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# GNOME/gtk
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
import gtk
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# GRAMPS modules
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
import Utils
 | 
			
		||||
import Report
 | 
			
		||||
import BaseDoc
 | 
			
		||||
import GenericFilter
 | 
			
		||||
import Errors
 | 
			
		||||
 | 
			
		||||
from RelLib import Event
 | 
			
		||||
from gettext import gettext as _
 | 
			
		||||
from latin_utf8 import utf8_to_latin
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# constants
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
_scaled = 0
 | 
			
		||||
_single = 1
 | 
			
		||||
_multiple = 2
 | 
			
		||||
 | 
			
		||||
_pagecount_map = {
 | 
			
		||||
    _("Single (scaled)") : _scaled,
 | 
			
		||||
    _("Single") : _single,
 | 
			
		||||
    _("Multiple") : _multiple,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# RelGraphDialog
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
class RelGraphDialog(Report.ReportDialog):
 | 
			
		||||
 | 
			
		||||
    # Default graph options
 | 
			
		||||
    File            = None
 | 
			
		||||
    IndividualSet   = Set()
 | 
			
		||||
    ShowAsStack     = 0
 | 
			
		||||
    ShowFamilies    = 0
 | 
			
		||||
    IncludeDates    = 1
 | 
			
		||||
    JustYear        = 0
 | 
			
		||||
    PlaceCause      = 1
 | 
			
		||||
    IncludeUrl      = 1
 | 
			
		||||
    IncludeId       = 0
 | 
			
		||||
    Colorize        = 1
 | 
			
		||||
    FontStyle       = 'Arial'
 | 
			
		||||
    ArrowHeadStyle  = 'none'
 | 
			
		||||
    ArrowTailStyle  = 'normal'
 | 
			
		||||
    AdoptionsDashed = 1
 | 
			
		||||
    Width           = 0
 | 
			
		||||
    Height          = 0
 | 
			
		||||
    HPages          = 1
 | 
			
		||||
    VPages          = 1
 | 
			
		||||
    TBMargin        = 0
 | 
			
		||||
    LRMargin        = 0
 | 
			
		||||
 | 
			
		||||
    def __init__(self,database,person):
 | 
			
		||||
        Report.ReportDialog.__init__(self,database,person)
 | 
			
		||||
 | 
			
		||||
    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()]))
 | 
			
		||||
 | 
			
		||||
        fam = GenericFilter.GenericFilter()
 | 
			
		||||
        fam.set_name(_("Descendant family members of %s") % name)
 | 
			
		||||
        fam.add_rule(GenericFilter.IsDescendantFamilyOf([self.person.getId()]))
 | 
			
		||||
 | 
			
		||||
        ans = GenericFilter.GenericFilter()
 | 
			
		||||
        ans.set_name(_("Ancestors of %s") % name)
 | 
			
		||||
        ans.add_rule(GenericFilter.IsAncestorOf([self.person.getId()]))
 | 
			
		||||
 | 
			
		||||
        com = GenericFilter.GenericFilter()
 | 
			
		||||
        com.set_name(_("People with common ancestor with %s") % name)
 | 
			
		||||
        com.add_rule(GenericFilter.HasCommonAncestorWith([self.person.getId()]))
 | 
			
		||||
 | 
			
		||||
        return [all, des, fam, ans, com]
 | 
			
		||||
 | 
			
		||||
    def add_user_options(self):
 | 
			
		||||
        self.arrowstyle_optionmenu = gtk.OptionMenu()
 | 
			
		||||
        menu = gtk.Menu()
 | 
			
		||||
 | 
			
		||||
        menuitem = gtk.MenuItem(_("Descendants <- Ancestors"))
 | 
			
		||||
        menuitem.set_data('t', ('none', 'normal'))
 | 
			
		||||
        menuitem.show()
 | 
			
		||||
        menu.append(menuitem)
 | 
			
		||||
 | 
			
		||||
        menuitem = gtk.MenuItem(_("Descendants -> Ancestors"))
 | 
			
		||||
        menuitem.set_data('t', ('normal', 'none'))
 | 
			
		||||
        menuitem.show()
 | 
			
		||||
        menu.append(menuitem)
 | 
			
		||||
 | 
			
		||||
        menuitem = gtk.MenuItem(_("Descendants <-> Ancestors"))
 | 
			
		||||
        menuitem.set_data('t', ('normal', 'normal'))
 | 
			
		||||
        menuitem.show()
 | 
			
		||||
        menu.append(menuitem)
 | 
			
		||||
 | 
			
		||||
        menuitem = gtk.MenuItem(_("Descendants - Ancestors"))
 | 
			
		||||
        menuitem.set_data('t', ('none', 'none'))
 | 
			
		||||
        menuitem.show()
 | 
			
		||||
        menu.append(menuitem)
 | 
			
		||||
 | 
			
		||||
        menu.set_active(0)
 | 
			
		||||
 | 
			
		||||
        self.arrowstyle_optionmenu.set_menu(menu)
 | 
			
		||||
 | 
			
		||||
        self.font_optionmenu = gtk.OptionMenu()
 | 
			
		||||
        menu = gtk.Menu()
 | 
			
		||||
 | 
			
		||||
        menuitem = gtk.MenuItem(_("TrueType"))
 | 
			
		||||
        menuitem.set_data('t', 'Arial')
 | 
			
		||||
        menuitem.show()
 | 
			
		||||
        menu.append(menuitem)
 | 
			
		||||
 | 
			
		||||
        menuitem = gtk.MenuItem(_("PostScript"))
 | 
			
		||||
        menuitem.set_data('t', 'Helvetica')
 | 
			
		||||
        menuitem.show()
 | 
			
		||||
        menu.append(menuitem)
 | 
			
		||||
 | 
			
		||||
        self.font_optionmenu.set_menu(menu)
 | 
			
		||||
 | 
			
		||||
        self.add_frame_option(_("GraphViz Options"),
 | 
			
		||||
                              _("Font Options"),
 | 
			
		||||
                              self.font_optionmenu,
 | 
			
		||||
                              _("Choose the font family."))
 | 
			
		||||
 | 
			
		||||
        self.add_frame_option(_("GraphViz Options"),
 | 
			
		||||
                              _("Arrowhead Options"),
 | 
			
		||||
                              self.arrowstyle_optionmenu,
 | 
			
		||||
                              _("Choose the direction that the arrows point."))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        self.show_as_stack_cb = gtk.CheckButton(_("Show family as a stack"))
 | 
			
		||||
        self.show_as_stack_cb.set_active(self.ShowAsStack)
 | 
			
		||||
        self.show_as_stack_cb.connect('toggled', self._grey_out_cb)
 | 
			
		||||
        self.add_frame_option(_("GraphViz Options"), '',
 | 
			
		||||
                              self.show_as_stack_cb,
 | 
			
		||||
                              _("The main individual is shown along with "
 | 
			
		||||
                                "their spuses in a stack."))
 | 
			
		||||
 | 
			
		||||
        self.show_families_cb = gtk.CheckButton(_("Show family nodes"))
 | 
			
		||||
        self.show_families_cb.set_active(self.ShowFamilies)
 | 
			
		||||
        self.show_families_cb.connect('toggled', self._grey_out_cb)
 | 
			
		||||
        self.add_frame_option(_("GraphViz Options"), '',
 | 
			
		||||
                              self.show_families_cb,
 | 
			
		||||
                              _("Families will show up as ellipses, linked "
 | 
			
		||||
                                "to parents and children."))
 | 
			
		||||
        msg = _("Include IDs")
 | 
			
		||||
        self.includeid_cb = gtk.CheckButton(msg)
 | 
			
		||||
        self.includeid_cb.set_active(self.IncludeId)
 | 
			
		||||
        self.add_frame_option(_("GraphViz Options"), '',
 | 
			
		||||
                              self.includeid_cb,
 | 
			
		||||
                              _("Include individual and family IDs."))
 | 
			
		||||
 | 
			
		||||
        msg = _("Include Birth, Marriage and Death Dates")
 | 
			
		||||
        self.includedates_cb = gtk.CheckButton(msg)
 | 
			
		||||
        self.includedates_cb.set_active(self.IncludeDates)
 | 
			
		||||
        self.add_frame_option(_("GraphViz Options"), '',
 | 
			
		||||
                              self.includedates_cb,
 | 
			
		||||
                              _("Include the dates that the individual "
 | 
			
		||||
                                "was born, got married and/or died "
 | 
			
		||||
                                "in the graph labels."))
 | 
			
		||||
 | 
			
		||||
        self.just_year_cb = gtk.CheckButton(_("Limit dates to years only"))
 | 
			
		||||
        self.just_year_cb.set_active(self.JustYear)
 | 
			
		||||
        self.add_frame_option(_("GraphViz Options"), '',
 | 
			
		||||
                              self.just_year_cb,
 | 
			
		||||
                              _("Prints just dates' year, neither "
 | 
			
		||||
                                "month or day nor date approximation "
 | 
			
		||||
                                "or interval are shown."))
 | 
			
		||||
 | 
			
		||||
        self.place_cause_cb = gtk.CheckButton(_("Place/cause when no date"))
 | 
			
		||||
        self.place_cause_cb.set_active(self.PlaceCause)
 | 
			
		||||
        self.includedates_cb.connect('toggled', self._grey_out_cb)
 | 
			
		||||
        self.add_frame_option(_("GraphViz Options"), '',
 | 
			
		||||
                              self.place_cause_cb,
 | 
			
		||||
                              _("When no birth, marriage, or death date "
 | 
			
		||||
                                "is available, the correspondent place field "
 | 
			
		||||
                                "(or cause field when blank) will be used."))
 | 
			
		||||
 | 
			
		||||
        self.includeurl_cb = gtk.CheckButton(_("Include URLs"))
 | 
			
		||||
        self.includeurl_cb.set_active(self.IncludeUrl)
 | 
			
		||||
        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.CheckButton(_("Colorize Graph"))
 | 
			
		||||
        self.colorize_cb.set_active(self.Colorize)
 | 
			
		||||
        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.CheckButton(_("Indicate non-birth relationships with dashed lines"))
 | 
			
		||||
        self.adoptionsdashed_cb.set_active(self.AdoptionsDashed)
 | 
			
		||||
        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.Adjustment(value=0.5, lower=0.25,
 | 
			
		||||
                                      upper=100.0, step_incr=0.25)
 | 
			
		||||
        lr_margin_adj = gtk.Adjustment(value=0.5, lower=0.25,
 | 
			
		||||
                                      upper=100.0, step_incr=0.25)
 | 
			
		||||
 | 
			
		||||
        self.tb_margin_sb = gtk.SpinButton(adjustment=tb_margin_adj, digits=2)
 | 
			
		||||
        self.lr_margin_sb = gtk.SpinButton(adjustment=lr_margin_adj, digits=2)
 | 
			
		||||
 | 
			
		||||
        self.add_frame_option(_("Page Options"),
 | 
			
		||||
                              _("Top & Bottom Margins"),
 | 
			
		||||
                              self.tb_margin_sb)
 | 
			
		||||
        self.add_frame_option(_("Page Options"),
 | 
			
		||||
                              _("Left & Right Margins"),
 | 
			
		||||
                              self.lr_margin_sb)
 | 
			
		||||
 | 
			
		||||
        hpages_adj = gtk.Adjustment(value=1, lower=1, upper=25, step_incr=1)
 | 
			
		||||
        vpages_adj = gtk.Adjustment(value=1, lower=1, upper=25, step_incr=1)
 | 
			
		||||
 | 
			
		||||
        self.hpages_sb = gtk.SpinButton(adjustment=hpages_adj, digits=0)
 | 
			
		||||
        self.vpages_sb = gtk.SpinButton(adjustment=vpages_adj, digits=0)
 | 
			
		||||
 | 
			
		||||
        self.add_frame_option(_("Page 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(_("Page 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."))
 | 
			
		||||
 | 
			
		||||
    def _grey_out_cb (self, button):
 | 
			
		||||
        if button == self.includedates_cb:
 | 
			
		||||
            if button.get_active():
 | 
			
		||||
                self.just_year_cb.set_sensitive(1)
 | 
			
		||||
                self.place_cause_cb.set_sensitive(1)
 | 
			
		||||
            else:
 | 
			
		||||
                self.just_year_cb.set_sensitive(0)
 | 
			
		||||
                self.place_cause_cb.set_sensitive(0)
 | 
			
		||||
        elif button == self.show_families_cb:
 | 
			
		||||
            if button.get_active():
 | 
			
		||||
                self.show_as_stack_cb.set_sensitive(0)
 | 
			
		||||
            else:
 | 
			
		||||
                self.show_as_stack_cb.set_sensitive(1)
 | 
			
		||||
        elif button == self.show_as_stack_cb:
 | 
			
		||||
            if button.get_active():
 | 
			
		||||
                self.show_families_cb.set_sensitive(0)
 | 
			
		||||
            else:
 | 
			
		||||
                self.show_families_cb.set_sensitive(1)
 | 
			
		||||
 | 
			
		||||
    def make_doc_menu(self):
 | 
			
		||||
        """Build a one item menu of document types that are
 | 
			
		||||
        appropriate for this report."""
 | 
			
		||||
        name = "Graphviz (dot)"
 | 
			
		||||
        menuitem = gtk.MenuItem (name)
 | 
			
		||||
        menuitem.set_data ("d", name)
 | 
			
		||||
        menuitem.set_data("paper",1)
 | 
			
		||||
        if os.system ("dot </dev/null 2>/dev/null") == 0:
 | 
			
		||||
            menuitem.set_data ("printable", _("Generate print output"))
 | 
			
		||||
        menuitem.show ()
 | 
			
		||||
        myMenu = gtk.Menu ()
 | 
			
		||||
        myMenu.append (menuitem)
 | 
			
		||||
        self.format_menu.set_menu(myMenu)
 | 
			
		||||
 | 
			
		||||
    def make_document(self):
 | 
			
		||||
        """Do Nothing.  This document will be created in the
 | 
			
		||||
        make_report routine."""
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def setup_style_frame(self):
 | 
			
		||||
        """The style frame is not used in this dialog."""
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def parse_style_frame(self):
 | 
			
		||||
        """The style frame is not used in this dialog."""
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def parse_other_frames(self):
 | 
			
		||||
        self.ShowAsStack = self.show_as_stack_cb.get_active()
 | 
			
		||||
        self.ShowFamilies = self.show_families_cb.get_active()
 | 
			
		||||
        self.IncludeDates = self.includedates_cb.get_active()
 | 
			
		||||
        self.JustYear = self.just_year_cb.get_active()
 | 
			
		||||
        self.PlaceCause = self.place_cause_cb.get_active()
 | 
			
		||||
        self.IncludeId = self.includeid_cb.get_active()
 | 
			
		||||
        self.IncludeUrl = self.includeurl_cb.get_active()
 | 
			
		||||
        self.Colorize = self.colorize_cb.get_active()
 | 
			
		||||
        self.FontStyle =\
 | 
			
		||||
            self.font_optionmenu.get_menu().get_active().get_data('t')
 | 
			
		||||
        self.ArrowHeadStyle, \
 | 
			
		||||
        self.ArrowTailStyle =\
 | 
			
		||||
            self.arrowstyle_optionmenu.get_menu().get_active().get_data('t')
 | 
			
		||||
        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()
 | 
			
		||||
        self.TBMargin = self.tb_margin_sb.get_value()
 | 
			
		||||
        self.LRMargin = self.lr_margin_sb.get_value()
 | 
			
		||||
 | 
			
		||||
    def make_report(self):
 | 
			
		||||
        """Create the object that will produce the GraphViz file."""
 | 
			
		||||
        self.Width = self.paper.get_width_inches()
 | 
			
		||||
        self.Height = self.paper.get_height_inches()
 | 
			
		||||
 | 
			
		||||
        self.File = open(self.target_path,"w")
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self.IndividualSet =\
 | 
			
		||||
                Set(self.filter.apply(self.db, self.db.getPersonMap().values()))
 | 
			
		||||
            self.IndividualSet.add(self.person)
 | 
			
		||||
        except Errors.FilterError, msg:
 | 
			
		||||
            from QuestionDialog import ErrorDialog
 | 
			
		||||
            (m1,m2) = msg.messages()
 | 
			
		||||
            ErrorDialog(m1,m2)
 | 
			
		||||
 | 
			
		||||
        _writeDot(self)
 | 
			
		||||
 | 
			
		||||
        if self.print_report.get_active ():
 | 
			
		||||
            os.environ["DOT"] = self.target_path
 | 
			
		||||
            os.system ('dot -Tps "$DOT" | %s &' %
 | 
			
		||||
                       Report.get_print_dialog_app ())
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def report(database,person):
 | 
			
		||||
    RelGraphDialog(database,person)
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _writeDot
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _writeDot(self):
 | 
			
		||||
    """Write out to a file a relationship graph in dot format"""
 | 
			
		||||
    self.File.write("/* GRAMPS - Relationship graph\n")
 | 
			
		||||
    self.File.write(" *\n")
 | 
			
		||||
    self.File.write(" * Report options:\n")
 | 
			
		||||
    self.File.write(" *   font style         : %s\n" % self.FontStyle)
 | 
			
		||||
    self.File.write(" *   style arrow head   : %s\n" % self.ArrowHeadStyle)
 | 
			
		||||
    self.File.write(" *               tail   : %s\n" % self.ArrowTailStyle)
 | 
			
		||||
    self.File.write(" *   include URLs       : %s\n" % self.IncludeUrl)
 | 
			
		||||
    self.File.write(" *           IDs        : %s\n" % self.IncludeId)
 | 
			
		||||
    self.File.write(" *           dates      : %s\n" % self.IncludeDates)
 | 
			
		||||
    self.File.write(" *   just year          : %s\n" % self.JustYear)
 | 
			
		||||
    self.File.write(" *   place or cause     : %s\n" % self.PlaceCause)
 | 
			
		||||
    self.File.write(" *   colorize           : %s\n" % self.Colorize)
 | 
			
		||||
    self.File.write(" *   dashed adoptions   : %s\n" % self.AdoptionsDashed)
 | 
			
		||||
    self.File.write(" *   show families      : %s\n" % self.ShowFamilies)
 | 
			
		||||
    self.File.write(" *        as stack      : %s\n" % self.ShowAsStack)
 | 
			
		||||
    self.File.write(" *   margins top/bottm  : %s\n" % self.TBMargin)
 | 
			
		||||
    self.File.write(" *           left/right : %s\n" % self.LRMargin)
 | 
			
		||||
    self.File.write(" *   pages horizontal   : %s\n" % self.HPages)
 | 
			
		||||
    self.File.write(" *         vertical     : %s\n" % self.VPages)
 | 
			
		||||
    self.File.write(" *   page width         : %sin\n" % self.Width)
 | 
			
		||||
    self.File.write(" *        height        : %sin\n" % self.Height)
 | 
			
		||||
    self.File.write(" *\n")
 | 
			
		||||
    self.File.write(" * Generated on %s by GRAMPS\n" % asctime())
 | 
			
		||||
    self.File.write(" */\n\n")
 | 
			
		||||
    self.File.write("digraph GRAMPS_relationship_graph {\n")
 | 
			
		||||
    self.File.write("bgcolor=white;\n")
 | 
			
		||||
    self.File.write("rankdir=LR;\n")
 | 
			
		||||
    self.File.write("center=1;\n")
 | 
			
		||||
    self.File.write("margin=0.5;\n")
 | 
			
		||||
    self.File.write("ratio=fill;\n")
 | 
			
		||||
    self.File.write("size=\"%3.1f,%3.1f\";\n"
 | 
			
		||||
                    % ((self.Width*self.HPages) - (self.LRMargin*2) -
 | 
			
		||||
                       ((self.HPages - 1)*1.0),
 | 
			
		||||
                       (self.Height*self.VPages) - (self.TBMargin*2) -
 | 
			
		||||
                       ((self.VPages - 1)*1.0)))
 | 
			
		||||
    self.File.write("page=\"%3.1f,%3.1f\";\n" % (self.Width, self.Height))
 | 
			
		||||
 | 
			
		||||
    if len(self.IndividualSet) > 1:
 | 
			
		||||
        if self.ShowAsStack:
 | 
			
		||||
            _writeGraphRecord(self)
 | 
			
		||||
        else:
 | 
			
		||||
            _writeGraphBox(self)
 | 
			
		||||
 | 
			
		||||
    self.File.write("}\n// File end")
 | 
			
		||||
    self.File.close()
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _writeGraphBox
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _writeGraphBox (self):
 | 
			
		||||
    """Write out a graph body where all individuals are separated boxes"""
 | 
			
		||||
    individualNodes = Set()  # list of individual graph nodes
 | 
			
		||||
    familyNodes = Set()      # list of family graph nodes
 | 
			
		||||
    # Writes out a not for each individual
 | 
			
		||||
    self.File.write('\n// Individual nodes (box graph)\n')
 | 
			
		||||
    _writeNode(self.File, shape='box', color='black', fontname=self.FontStyle)
 | 
			
		||||
    for individual in self.IndividualSet:
 | 
			
		||||
        individualNodes.add(individual)
 | 
			
		||||
        individualId = _getIndividualId(individual)
 | 
			
		||||
        (color, url) = _getIndividualData(self, individual)
 | 
			
		||||
        label = _getIndividualLabel(self, individual)
 | 
			
		||||
        _writeNode(self.File, individualId, label, color, url)
 | 
			
		||||
    # Writes out a node for each family
 | 
			
		||||
    if self.ShowFamilies:
 | 
			
		||||
        self.File.write('\n// Family nodes (box graph)\n')
 | 
			
		||||
        _writeNode(self.File, shape='ellipse', color='black',
 | 
			
		||||
                   fontname=self.FontStyle)
 | 
			
		||||
        for individual in individualNodes:
 | 
			
		||||
            for family in individual.getFamilyList():
 | 
			
		||||
                if family not in familyNodes:
 | 
			
		||||
                    familyNodes.add(family)
 | 
			
		||||
                    familyId = _getFamilyId(family)
 | 
			
		||||
                    label = _getFamilyLabel(self, family)
 | 
			
		||||
                    _writeNode(self.File, familyId, label)
 | 
			
		||||
    # Links each individual to their parents/family
 | 
			
		||||
    self.File.write('\n// Individual edges\n')
 | 
			
		||||
    _writeEdge(self.File, style="solid", arrowHead=self.ArrowHeadStyle,
 | 
			
		||||
               arrowTail=self.ArrowTailStyle)
 | 
			
		||||
    for individual in individualNodes:
 | 
			
		||||
        individualId = _getIndividualId(individual)
 | 
			
		||||
        for family, motherRelShip, fatherRelShip\
 | 
			
		||||
                in individual.getParentList():
 | 
			
		||||
            father = family.getFather()
 | 
			
		||||
            mother = family.getMother()
 | 
			
		||||
            if self.ShowFamilies and family in familyNodes:
 | 
			
		||||
                # edge from an individual to their family
 | 
			
		||||
                familyId = _getFamilyId(family)
 | 
			
		||||
                style = _getEdgeStyle(self, fatherRelShip, motherRelShip)
 | 
			
		||||
                _writeEdge(self.File, individualId, familyId, style)
 | 
			
		||||
            else:
 | 
			
		||||
                # edge from an individual to their parents
 | 
			
		||||
                if father and father in individualNodes:
 | 
			
		||||
                    fatherId = _getIndividualId(father)
 | 
			
		||||
                    _writeEdge(self.File, individualId, fatherId,
 | 
			
		||||
                               _getEdgeStyle(self, fatherRelShip))
 | 
			
		||||
                if mother and mother in individualNodes:
 | 
			
		||||
                    motherId = _getIndividualId(mother)
 | 
			
		||||
                    _writeEdge(self.File, individualId, motherId,
 | 
			
		||||
                               _getEdgeStyle(self, motherRelShip))
 | 
			
		||||
    # Links each family to its components
 | 
			
		||||
    if self.ShowFamilies:
 | 
			
		||||
        self.File.write('\n// Family edges (box graph)\n')
 | 
			
		||||
        _writeEdge(self.File, style="solid", arrowHead=self.ArrowHeadStyle,
 | 
			
		||||
                   arrowTail=self.ArrowTailStyle)
 | 
			
		||||
        for family in familyNodes:
 | 
			
		||||
            familyId = _getFamilyId(family)
 | 
			
		||||
            father = family.getFather()
 | 
			
		||||
            if father and father in individualNodes:
 | 
			
		||||
                fatherId = _getIndividualId(father)
 | 
			
		||||
                _writeEdge(self.File, familyId, fatherId)
 | 
			
		||||
            mother = family.getMother()
 | 
			
		||||
            if mother and mother in individualNodes:
 | 
			
		||||
                motherId = _getIndividualId(mother)
 | 
			
		||||
                _writeEdge(self.File, familyId, motherId)
 | 
			
		||||
    # Statistics
 | 
			
		||||
    males = 0
 | 
			
		||||
    females = 0
 | 
			
		||||
    unknowns = 0
 | 
			
		||||
    for individual in individualNodes:
 | 
			
		||||
        if individual.getGender() == individual.male:
 | 
			
		||||
            males = males + 1
 | 
			
		||||
        elif individual.getGender() == individual.female:
 | 
			
		||||
            females = females + 1
 | 
			
		||||
        else:
 | 
			
		||||
            unknowns = unknowns + 1
 | 
			
		||||
    _writeStats(self.File, males, females, unknowns, len(familyNodes))
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _writeGraphRecord
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _writeGraphRecord (self):
 | 
			
		||||
    """Write out a graph body where families are rendered as records"""
 | 
			
		||||
    # Builds a dictionary of family records.
 | 
			
		||||
    # Each record is made of an individual married with zero or
 | 
			
		||||
    # more individuals.
 | 
			
		||||
    familyNodes = {}
 | 
			
		||||
    if isinstance(self.filter.get_rules()[0],
 | 
			
		||||
                  GenericFilter.IsDescendantFamilyOf):
 | 
			
		||||
        # With the IsDescendantFamilyOf filter, the IndividualSet
 | 
			
		||||
        # includes people which are not direct descendants of the
 | 
			
		||||
        # active person (practically, spouses of direct
 | 
			
		||||
        # discendants). Because we want the graph to highlight the
 | 
			
		||||
        # consanguinity, IndividualSet is split in two subsets:
 | 
			
		||||
        # naturalRelatives (direct descendants) and its complementary
 | 
			
		||||
        # subset (in-law relatives).
 | 
			
		||||
        filter = GenericFilter.GenericFilter()
 | 
			
		||||
        filter.add_rule(GenericFilter.IsDescendantOf([self.person.getId()]))
 | 
			
		||||
        naturalRelatives =\
 | 
			
		||||
            Set(filter.apply(self.db, self.db.getPersonMap().values()))
 | 
			
		||||
        naturalRelatives.add(self.person)
 | 
			
		||||
    else:
 | 
			
		||||
        naturalRelatives = self.IndividualSet
 | 
			
		||||
    self.File.write('\n// Family nodes (record graph)\n')
 | 
			
		||||
    _writeNode(self.File, shape='record', color='black',
 | 
			
		||||
               fontname=self.FontStyle)
 | 
			
		||||
    for individual in naturalRelatives:
 | 
			
		||||
        familyId = _getIndividualId(individual)
 | 
			
		||||
        # If both husband and wife are members of the IndividualSet,
 | 
			
		||||
        # only one record, with the husband first, is displayed.
 | 
			
		||||
        if individual.getGender() == individual.female:
 | 
			
		||||
            # There are exactly three cases where a female node is added:
 | 
			
		||||
            family = None       # no family
 | 
			
		||||
            husbands = []       # filtered-in husbands (naturalRelatives)
 | 
			
		||||
            unknownHusbands = 0 # filtered-out/unknown husbands
 | 
			
		||||
            for family in individual.getFamilyList():
 | 
			
		||||
                husband = family.getFather()
 | 
			
		||||
                if husband and husband in self.IndividualSet:
 | 
			
		||||
                    if  husband not in naturalRelatives:
 | 
			
		||||
                        husbands.append(husband)
 | 
			
		||||
                else:
 | 
			
		||||
                    unknownHusbands = 1
 | 
			
		||||
            if not family or len(husbands) or unknownHusbands:
 | 
			
		||||
                familyNodes[familyId] = [individual] + husbands
 | 
			
		||||
        else:
 | 
			
		||||
            familyNodes[familyId] = [individual]
 | 
			
		||||
            for family in individual.getFamilyList():
 | 
			
		||||
                wife = family.getMother()
 | 
			
		||||
                if wife in self.IndividualSet:
 | 
			
		||||
                    familyNodes[familyId].append(wife)
 | 
			
		||||
    # Writes out all family records
 | 
			
		||||
    for familyId, family in familyNodes.items():
 | 
			
		||||
        (color, url) = _getIndividualData(self, familyNodes[familyId][0])
 | 
			
		||||
        label = _getFamilyRecordLabel(self, familyNodes[familyId])
 | 
			
		||||
        _writeNode(self.File, familyId, label, color, url)
 | 
			
		||||
    # Links individual's record to their parents' record
 | 
			
		||||
    # The arrow goes from the individual port of a family record
 | 
			
		||||
    # to the parent port of the parent's family record.
 | 
			
		||||
    self.File.write('\n// Individual edges\n')
 | 
			
		||||
    _writeEdge(self.File, style="solid", arrowHead=self.ArrowHeadStyle,
 | 
			
		||||
               arrowTail=self.ArrowTailStyle)
 | 
			
		||||
    for familyFromId, familyFrom in familyNodes.items():
 | 
			
		||||
        for individualFrom in familyFrom:
 | 
			
		||||
            individualFromId = _getIndividualId(individualFrom)
 | 
			
		||||
            for family, motherRelShip, fatherRelShip\
 | 
			
		||||
                    in individualFrom.getParentList():
 | 
			
		||||
                father = family.getFather()
 | 
			
		||||
                mother = family.getMother()
 | 
			
		||||
                # Things are complicated here because a parent may or
 | 
			
		||||
                # or may not exist.
 | 
			
		||||
                if father:
 | 
			
		||||
                    fatherId = _getIndividualId(father)
 | 
			
		||||
                else:
 | 
			
		||||
                    fatherId = ""
 | 
			
		||||
                if mother:
 | 
			
		||||
                    motherId = _getIndividualId(mother)
 | 
			
		||||
                else:
 | 
			
		||||
                    motherId = ""
 | 
			
		||||
                if familyNodes.has_key(fatherId):
 | 
			
		||||
                    if mother in familyNodes[fatherId]:
 | 
			
		||||
                        _writeEdge(self.File, familyFromId, fatherId,
 | 
			
		||||
                                   _getEdgeStyle(self, motherRelShip),
 | 
			
		||||
                                   portFrom=individualFromId, portTo=motherId)
 | 
			
		||||
                    else:
 | 
			
		||||
                        _writeEdge(self.File, familyFromId, fatherId,
 | 
			
		||||
                                   _getEdgeStyle(self, fatherRelShip),
 | 
			
		||||
                                   portFrom=individualFromId)
 | 
			
		||||
                if familyNodes.has_key(motherId):
 | 
			
		||||
                    if father in familyNodes[motherId]:
 | 
			
		||||
                        _writeEdge(self.File, familyFromId, motherId,
 | 
			
		||||
                                   _getEdgeStyle(self, fatherRelShip),
 | 
			
		||||
                                   portFrom=individualFromId, portTo=fatherId)
 | 
			
		||||
                    else:
 | 
			
		||||
                        _writeEdge(self.File, familyFromId, motherId,
 | 
			
		||||
                                   _getEdgeStyle(self, motherRelShip),
 | 
			
		||||
                                   portFrom=individualFromId)
 | 
			
		||||
    # Stats (unique individuals)
 | 
			
		||||
    males = Set()
 | 
			
		||||
    females = Set()
 | 
			
		||||
    unknowns = Set()
 | 
			
		||||
    marriages = 0
 | 
			
		||||
    for familyId, family in familyNodes.items():
 | 
			
		||||
        marriages = marriages + (len(family) - 1)
 | 
			
		||||
        for individual in family:
 | 
			
		||||
            if individual.getGender() == individual.male\
 | 
			
		||||
                   and individual not in males:
 | 
			
		||||
                males.add(individual)
 | 
			
		||||
            elif individual.getGender() == individual.female\
 | 
			
		||||
                     and individual not in females:
 | 
			
		||||
                females.add(individual)
 | 
			
		||||
            elif individual.getGender() == individual.unknown\
 | 
			
		||||
                     and individual not in unknowns:
 | 
			
		||||
                unknowns.add(individual)
 | 
			
		||||
    _writeStats(self.File, len(males), len(females), len(unknowns), marriages)
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _getIndividualId
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _getIndividualId (individual):
 | 
			
		||||
    """Returns an individual id suitable for dot"""
 | 
			
		||||
    return individual.getId()
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _getIndividualData
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _getIndividualData (self, individual):
 | 
			
		||||
    """Returns a tuple of individual data"""
 | 
			
		||||
    # color
 | 
			
		||||
    color = ''
 | 
			
		||||
    if self.Colorize:
 | 
			
		||||
        gender = individual.getGender()
 | 
			
		||||
        if gender == individual.male:
 | 
			
		||||
            color = 'dodgerblue4'
 | 
			
		||||
        elif gender == individual.female:
 | 
			
		||||
            color  = 'deeppink'
 | 
			
		||||
    # url
 | 
			
		||||
    url = ''
 | 
			
		||||
    if self.IncludeUrl:
 | 
			
		||||
        url = "%s.html" % _getIndividualId(individual)
 | 
			
		||||
 | 
			
		||||
    return (color, url)
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _getEventLabel
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _getEventLabel (self, event):
 | 
			
		||||
    """Returns a formatted string of event data suitable for a label"""
 | 
			
		||||
    if self.IncludeDates and event:
 | 
			
		||||
        dateObj = event.getDateObj()
 | 
			
		||||
        if dateObj.getYearValid():
 | 
			
		||||
            if self.JustYear:
 | 
			
		||||
                return "%i" % dateObj.getYear()
 | 
			
		||||
            else:
 | 
			
		||||
                return dateObj.getDate()
 | 
			
		||||
        elif self.PlaceCause:
 | 
			
		||||
            if event.getPlaceName():
 | 
			
		||||
                return event.getPlaceName()
 | 
			
		||||
            else:
 | 
			
		||||
                return event.getCause()
 | 
			
		||||
    return ''
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _getIndividualLabel
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _getIndividualLabel (self, individual, marriageEvent=None, family=None):
 | 
			
		||||
    """Returns a formatted string of individual data suitable for a label
 | 
			
		||||
 | 
			
		||||
    Returned string always includes individual's name and optionally
 | 
			
		||||
    individual's birth and death dates, individual's marriage date,
 | 
			
		||||
    individual's and family's IDs.
 | 
			
		||||
    """
 | 
			
		||||
    # Get data ready
 | 
			
		||||
    individualId = individual.getId()
 | 
			
		||||
    name = individual.getPrimaryName().getName()
 | 
			
		||||
    if self.IncludeDates:
 | 
			
		||||
        birth = _getEventLabel(self, individual.getBirth())
 | 
			
		||||
        death = _getEventLabel(self, individual.getDeath())
 | 
			
		||||
        if marriageEvent != None:
 | 
			
		||||
            familyId = family.getId()
 | 
			
		||||
            marriage = _getEventLabel(self, marriageEvent)
 | 
			
		||||
    # Id
 | 
			
		||||
    if self.IncludeId:
 | 
			
		||||
        if marriageEvent != None:
 | 
			
		||||
            label = "%s (%s)\\n" % (familyId, individualId)
 | 
			
		||||
        else:
 | 
			
		||||
            label = "%s\\n" % individualId
 | 
			
		||||
    else:
 | 
			
		||||
        label = ""
 | 
			
		||||
    # Marriage date
 | 
			
		||||
    if self.IncludeDates and (marriageEvent != None and marriage):
 | 
			
		||||
        label = label + "%s\\n" % marriage
 | 
			
		||||
    # Individual's name
 | 
			
		||||
    label = label + name
 | 
			
		||||
    # Birth and death
 | 
			
		||||
    if self.IncludeDates and (birth or death):
 | 
			
		||||
        label = label + "\\n%s - %s" % (birth, death)
 | 
			
		||||
    return label
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _getEdgeStyle
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _getEdgeStyle (self, fatherRelShip, motherRelShip="Birth"):
 | 
			
		||||
    """Returns a edge style that depends on the relationships with parents"""
 | 
			
		||||
    if self.AdoptionsDashed and \
 | 
			
		||||
           (fatherRelShip != "Birth" or motherRelShip != "Birth"):
 | 
			
		||||
        return "dashed"
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _getFamilyId
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _getFamilyId (family):
 | 
			
		||||
    """Returns a family id suitable for dot"""
 | 
			
		||||
    return family.getId()
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _getFamilyLabel
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _getFamilyLabel (self, family):
 | 
			
		||||
    """Returns a formatted string of family data suitable for a label"""
 | 
			
		||||
    marriage = _getEventLabel(self, family.getMarriage())
 | 
			
		||||
    if self.IncludeId:
 | 
			
		||||
        return "%s\\n%s" % (family.getId(), marriage)
 | 
			
		||||
    else:
 | 
			
		||||
        return marriage
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _getFamilyRecordLabel
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _getFamilyRecordLabel (self, record):
 | 
			
		||||
    """Returns a formatted string of a family record suitable for a label"""
 | 
			
		||||
    labels = []
 | 
			
		||||
    spouse = record[0]
 | 
			
		||||
    for individual in record:
 | 
			
		||||
        individualId = _getIndividualId(individual)
 | 
			
		||||
        if spouse == individual:
 | 
			
		||||
            label = _getIndividualLabel(self, individual)
 | 
			
		||||
        else:
 | 
			
		||||
            marriageEvent = Event()
 | 
			
		||||
            for individualFamily in individual.getFamilyList():
 | 
			
		||||
                if individualFamily in spouse.getFamilyList():
 | 
			
		||||
                    marriageEvent = individualFamily.getMarriage()
 | 
			
		||||
                    if not marriageEvent:
 | 
			
		||||
                        marriageEvent = Event()
 | 
			
		||||
                    break
 | 
			
		||||
            label = _getIndividualLabel(self, individual, marriageEvent,
 | 
			
		||||
                                        individualFamily)
 | 
			
		||||
        label = string.replace(label, "|", r"\|")
 | 
			
		||||
        label = string.replace(label, "<", r"\<")
 | 
			
		||||
        label = string.replace(label, ">", r"\>")
 | 
			
		||||
        labels.append("<%s>%s" % (individualId, label))
 | 
			
		||||
    return string.join(labels, "|")
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _writeNode
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _writeNode (file, node="node", label="", color="", url="", shape="",
 | 
			
		||||
               fontname=""):
 | 
			
		||||
    """Writes out an individual node"""
 | 
			
		||||
    file.write('%s [' % node)
 | 
			
		||||
    if label:
 | 
			
		||||
        file.write('label="%s" ' %
 | 
			
		||||
                   utf8_to_latin(string.replace(label, '"', r'\"')))
 | 
			
		||||
    if color:
 | 
			
		||||
        file.write('color=%s ' % color)
 | 
			
		||||
    if url:
 | 
			
		||||
        file.write('URL="%s" ' % string.replace(url, '"', r'\"'))
 | 
			
		||||
    if shape:
 | 
			
		||||
        file.write('shape=%s ' % shape)
 | 
			
		||||
    if fontname:
 | 
			
		||||
        file.write('fontname="%s" ' % fontname)
 | 
			
		||||
    file.write('];\n')
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _writeEdge
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _writeEdge (file, nodeFrom="", nodeTo="", style="",
 | 
			
		||||
               arrowHead="", arrowTail="", portFrom="", portTo=""):
 | 
			
		||||
    """Writes out an edge"""
 | 
			
		||||
    if nodeFrom and nodeTo:
 | 
			
		||||
        if portFrom:
 | 
			
		||||
            frm = nodeFrom + ":" + portFrom
 | 
			
		||||
        else:
 | 
			
		||||
            frm = nodeFrom
 | 
			
		||||
        if portTo:
 | 
			
		||||
            to = nodeTo + ":" + portTo
 | 
			
		||||
        else:
 | 
			
		||||
            to = nodeTo
 | 
			
		||||
        file.write('%s -> %s [' % (frm, to))
 | 
			
		||||
    else:
 | 
			
		||||
        file.write('edge [')  # default edge
 | 
			
		||||
    if style:
 | 
			
		||||
        file.write('style=%s ' % style)
 | 
			
		||||
    if arrowHead:
 | 
			
		||||
        file.write('arrowhead=%s ' % arrowHead)
 | 
			
		||||
    if arrowTail:
 | 
			
		||||
        file.write('arrowtail=%s ' % arrowTail)
 | 
			
		||||
    file.write('];\n')
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# _writeStats
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
def _writeStats (file, males, females, unknowns, marriages):
 | 
			
		||||
    file.write('\n/* Statistics\n')
 | 
			
		||||
    file.write(' *   individuals male    : % 4d\n' % males)
 | 
			
		||||
    file.write(' *               female  : % 4d\n' % females)
 | 
			
		||||
    file.write(' *               unknown : % 4d\n' % unknowns)
 | 
			
		||||
    file.write(' *               total   : % 4d\n' % (males+females+unknowns))
 | 
			
		||||
    file.write(' *   marriages           : % 4d\n' % marriages)
 | 
			
		||||
    file.write(' */\n')
 | 
			
		||||
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
#------------------------------------------------------------------------
 | 
			
		||||
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(),
 | 
			
		||||
    author_name="Donald N. Allingham",
 | 
			
		||||
    author_email="dallingham@users.sourceforge.net"
 | 
			
		||||
    )
 | 
			
		||||
		Reference in New Issue
	
	Block a user