diff --git a/ChangeLog b/ChangeLog index b294b4fc3..31617ff73 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2008-02-02 Brian Matherly + * src/plugins/GraphViz.py: Deleted - superceeded by GVRelGraph.py. + 2008-02-02 Brian Matherly * src/plugins/WebCal.py: 0001663: webcal options show Mos. 1-6 Notes and Mos. 7-12 Notes diff --git a/src/plugins/GraphViz.py b/src/plugins/GraphViz.py deleted file mode 100644 index 760245c58..000000000 --- a/src/plugins/GraphViz.py +++ /dev/null @@ -1,1348 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2007 Donald N. Allingham -# Copyright (C) 2007 Johan Gonqvist -# Contributions by Lorenzo Cappelletti -# -# 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 -# - -# $Id$ - -""" -Generate graphviz dot files --- create a relationship graph -""" - -#------------------------------------------------------------------------ -# -# python modules -# -#------------------------------------------------------------------------ -import os -from gettext import gettext as _ -from time import asctime -import tempfile - -#------------------------------------------------------------------------ -# -# Set up logging -# -#------------------------------------------------------------------------ -import logging -log = logging.getLogger(".GraphViz") - -#------------------------------------------------------------------------ -# -# GNOME/gtk -# -#------------------------------------------------------------------------ -import gtk -import gobject - -#------------------------------------------------------------------------ -# -# GRAMPS modules -# -#------------------------------------------------------------------------ -from PluginUtils import register_report -from ReportBase import Report, ReportUtils, ReportOptions, \ - CATEGORY_CODE, CATEGORY_DRAW, MODE_GUI, MODE_CLI -from ReportBase._ReportDialog import ReportDialog -from ReportBase._CommandLineReport import CommandLineReport -from Filters import GenericFilter, Rules -import gen.lib -import DateHandler -from BasicUtils import name_displayer -import const -from BaseDoc import PAPER_LANDSCAPE -from QuestionDialog import ErrorDialog -import Errors -import Utils -import Mime -import ThumbNails - -#------------------------------------------------------------------------ -# -# Constant options items -# -#------------------------------------------------------------------------ - -class _options: - # internal ID, english option name (for cli), localized option name (for gui) - formats = ( - ("ps", "Postscript", _("Postscript"), "application/postscript"), - ("svg", "Structured Vector Graphics (SVG)", _("Structured Vector Graphics (SVG)"), "image/svg"), - ("svgz", "Compressed Structured Vector Graphics (SVG)", _("Compressed Structured Vector Graphs (SVG)"), "image/svgz"), - ("png", "PNG image", _("PNG image"), "image/png"), - ("jpg", "JPEG image", _("JPEG image"), "image/jpeg"), - ("gif", "GIF image", _("GIF image"), "image/gif"), - ) - fonts = ( - # Last items tells whether strings need to be converted to Latin1 - ("", "Default", _("Default")), - ("Helvetica", "Postscript / Helvetica", _("Postscript / Helvetica")), - ("FreeSans", "Truetype / FreeSans", _("Truetype / FreeSans")), - ) - colors = ( - ("outline", "B&W Outline", _("B&W outline")), - ("colored", "Colored outline", _("Colored outline")), - ("filled", "Color fill", _("Color fill")), - ) - ratio = ( - ("compress", "Minimal size", _("Minimal size")), - ("fill", "Fill the given area", _("Fill the given area")), - ("expand", "Automatically use optimal number of pages", - _("Automatically use optimal number of pages")) - ) - rankdir = ( - ("TB", "Vertical", _("Vertical")), - ("LR", "Horizontal", _("Horizontal")), - ) - pagedir = ( - ("BL", "Bottom, left", _("Bottom, left")), - ("BR", "Bottom, right", _("Bottom, right")), - ("TL", "Top, left", _("Top, left")), - ("TR", "Top, right", _("Top, Right")), - ("RB", "Right, bottom", _("Right, bottom")), - ("RT", "Right, top", _("Right, top")), - ("LB", "Left, bottom", _("Left, bottom")), - ("LT", "Left, top", _("Left, top")), - ) - noteloc = ( - ("t", "Top", _("Top")), - ("b", "Bottom", _("Bottom")), - ) - arrowstyles = ( - ('d', "Descendants <- Ancestors", _("Descendants <- Ancestors")), - ('a', "Descendants -> Ancestors", _("Descendants -> Ancestors")), - ('da',"Descendants <-> Ancestors", _("Descendants <-> Ancestors")), - ('', "Descendants - Ancestors", _("Descendants - Ancestors")), - ) - -gs_cmd = "" - -if os.sys.platform == "win32": - _dot_found = Utils.search_for("dot.exe") - - if Utils.search_for("gswin32c.exe") == 1: - gs_cmd = "gswin32c.exe" - elif Utils.search_for("gswin32.exe") == 1: - gs_cmd = "gswin32.exe" -else: - _dot_found = Utils.search_for("dot") - - if Utils.search_for("gs") == 1: - gs_cmd = "gs" - -if gs_cmd != "": - _options.formats += (("pdf", "PDF", _("PDF"), "application/pdf"),) - -#------------------------------------------------------------------------ -# -# Report class -# -#------------------------------------------------------------------------ -class GraphViz: - - def __init__(self,database,person,options_class): - """ - Creates ComprehensiveAncestorsReport object that produces the report. - - The arguments are: - - database - the GRAMPS database instance - person - currently selected person - options_class - instance of the Options class for this report - - This report needs the following parameters (class variables) - that come in the options class. - - filter - Filter to be applied to the people of the database. - The option class carries its number, and the function - returning the list of filters. - font - Font to use. - fontsize - Size of the font in points - latin - Set if text needs to be converted to latin-1 - arrow - Arrow styles for heads and tails. - showfamily - Whether to show family nodes. - incid - Whether to include IDs. - incdate - Whether to include dates. - justyears - Use years only. - placecause - Whether to replace missing dates with place or cause - url - Whether to include URLs. - inclimg - Include images or not - imgpos - Image position, above/beside name - rankdir - Graph direction, LR or RL - ratio - Output aspect ration, fill/compress/auto - color - Whether to use outline, colored outline or filled color in graph - dashedl - Whether to use dashed lines for non-birth relationships. - margin - Margins, in cm. - pagesh - Number of pages in horizontal direction. - pagesv - Number of pages in vertical direction. - pagedir - Paging direction - note - Note to add to the graph - notesize - Note font size (in points) - noteloc - Note location t/b - """ - colored = { - 'male': 'dodgerblue4', - 'female': 'deeppink', - 'unknown': 'black', - 'family': 'darkgreen' - } - filled = { - 'male': 'lightblue', - 'female': 'lightpink', - 'unknown': 'lightgray', - 'family': 'lightyellow' - } - self.database = database - self.start_person = person - - self.paper = options_class.handler.get_paper() - self.orient = options_class.handler.get_orientation() - self.width = self.paper.get_width_inches() - self.height = self.paper.get_height_inches() - - options = options_class.handler.options_dict - self.pagedir = options['pagedir'] - self.hpages = options['pagesh'] - self.vpages = options['pagesv'] - margin_cm = options['margin'] - self.margin = round(margin_cm/2.54,2) - if margin_cm > 0.1: - # GraphViz has rounding errors so have to make the real - # margins slightly smaller than (page - content size) - self.margin_small = round((margin_cm-0.1)/2.54,2) - else: - self.margin_small = 0 - self.includeid = options['incid'] - self.includedates = options['incdate'] - self.includeurl = options['url'] - self.includeimg = options['includeImages'] - self.imgpos = options['imageOnTheSide'] - self.adoptionsdashed = options['dashedl'] - self.show_families = options['showfamily'] - self.just_years = options['justyears'] - self.placecause = options['placecause'] - self.rankdir = options['rankdir'] - self.ratio = options['ratio'] - self.fontname = options['font'] - self.fontsize = options['fontsize'] - self.colorize = options['color'] - if self.colorize == 'colored': - self.colors = colored - elif self.colorize == 'filled': - self.colors = filled - arrow_str = options['arrow'] - if arrow_str.find('a') + 1: - self.arrowheadstyle = 'normal' - else: - self.arrowheadstyle = 'none' - if arrow_str.find('d') + 1: - self.arrowtailstyle = 'normal' - else: - self.arrowtailstyle = 'none' - - self.latin = options['latin'] - self.noteloc = options['noteloc'] - self.notesize = options['notesize'] - self.note = options['note'] - - filter_num = options_class.handler.options_dict['filter'] - filters = ReportUtils.get_person_filters(person,include_single=False) - self.filter = filters[filter_num] - - the_buffer = self.get_report() - - self.f = open(options_class.get_output(),'w') - if self.latin: - try: - self.f.write(the_buffer.encode('iso-8859-1', 'strict')) - except UnicodeEncodeError: - self.f = open(options_class.get_output(),'w') - self.f.write(the_buffer.encode('iso-8859-1', 'replace')) - ErrorDialog( - _("Your data contains characters that cannot be " - "converted to latin-1. These characters were " - "replaced with the question marks in the output. " - "To get these characters properly displayed, " - "unselect latin-1 option and try again.")) - else: - self.f.write(the_buffer) - self.f.close() - - def get_report(self): - "return string of the .dot file contents" - self.person_handles = self.filter.apply(self.database, - self.database.get_person_handles(sort_handles=False)) - - # graph size - if self.orient == PAPER_LANDSCAPE: - rotate = 90 - sizew = (self.height - self.margin*2) * self.hpages - sizeh = (self.width - self.margin*2) * self.vpages - else: - rotate = 0 - sizew = (self.width - self.margin*2) * self.hpages - sizeh = (self.height - self.margin*2) * self.vpages - - - buffer = self.get_comment_header() - buffer += """ -digraph GRAMPS_relationship_graph { -/* whole graph attributes */ -bgcolor=white; -center=1; -ratio=%s; -rankdir=%s; -mclimit=2.0; -margin="%3.2f"; -pagedir="%s"; -page="%3.2f,%3.2f"; -size="%3.2f,%3.2f"; -rotate=%d; -/* default node and edge attributes */ -nodesep=0.25; -edge [style=solid, arrowhead=%s arrowtail=%s]; -""" % ( - self.ratio, - self.rankdir, - self.margin_small, - self.pagedir, - self.width, self.height, - sizew, sizeh, - rotate, - self.arrowheadstyle, - self.arrowtailstyle - ) - - if self.fontname: - font = 'fontname="%s" ' % self.fontname - else: - font = '' - font += 'fontsize="%d"' % self.fontsize - if self.colorize == 'filled': - buffer += 'node [style=filled %s];\n' % font - else: - buffer += 'node [%s];\n' % font - if self.latin: - # GraphViz default is UTF-8 - buffer += 'charset="iso-8859-1";\n' - - if len(self.person_handles) > 1: - buffer += "/* persons and their families */\n" - buffer += self.get_persons_and_families() - buffer += "/* link children to families */\n" - buffer += self.get_child_links_to_families() - - if self.note: - buffer += 'labelloc="%s";\n' % self.noteloc - buffer += 'label="%s";\n' % self.note.replace('\n', '\\n').replace('"', '\\\"') - buffer += 'fontsize="%d";\n' % self.notesize # in points - - return buffer + "}\n" - - - def get_comment_header(self): - "return comment of Gramps options which are not Graphviz options" - return """/* -GRAMPS - Relationship graph - -Generated on %s. - -More info: -http://www.gramps-project.org/wiki/index.php?title=Howto:_Make_a_relationship_chart - -Report content options: - include URLs : %s - IDs : %s - dates : %s - just year : %s - place or cause : %s - colorize : %s - dotted adoptions : %s - show family nodes : %s - pages horizontal : %s - vertical : %s - -For other options, see graph settings below. - -If you need to switch between iso-8859-1 and utf-8 text encodings, -e.g. because you're using different font or -T output format, -just use iconv: - iconv -f iso-8859-1 -t utf-8 old.dot > new.dot - iconv -t utf-8 -f iso-8859-1 old.dot > new.dot -*/ -""" % ( - asctime(), - bool(self.includeurl), - bool(self.includeid), - bool(self.includedates), - bool(self.just_years), - bool(self.placecause), - bool(self.colorize), - bool(self.adoptionsdashed), - bool(self.show_families), - self.hpages, self.vpages - ) - - - def get_child_links_to_families(self): - "returns string of GraphViz edges linking parents to families or children" - person_dict = {} - # Hash people in a dictionary for faster inclusion checking - for person_handle in self.person_handles: - person_dict[person_handle] = 1 - the_buffer = "" - for person_handle in self.person_handles: - person = self.database.get_person_from_handle(person_handle) - p_id = person.get_gramps_id() - for fam_handle in person.get_parent_family_handle_list(): - family = self.database.get_family_from_handle(fam_handle) - father_handle = family.get_father_handle() - mother_handle = family.get_mother_handle() - for child_ref in family.get_child_ref_list(): - if child_ref.ref == person_handle: - frel = child_ref.frel - mrel = child_ref.mrel - break - if (self.show_families and - ((father_handle and father_handle in person_dict) or - (mother_handle and mother_handle in person_dict))): - # Link to the family node if either parent is in graph - the_buffer += self.get_family_link(p_id,family,frel,mrel) - else: - # Link to the parents' nodes directly, if they are in graph - if father_handle and father_handle in person_dict: - the_buffer += self.get_parent_link(p_id,father_handle,frel) - if mother_handle and mother_handle in person_dict: - the_buffer += self.get_parent_link(p_id,mother_handle,mrel) - return the_buffer - - def get_family_link(self, p_id, family, frel, mrel): - "returns string of GraphViz edge linking child to family" - style = '' - adopted = ((int(frel) != gen.lib.ChildRefType.BIRTH) or - (int(mrel) != gen.lib.ChildRefType.BIRTH)) - if adopted and self.adoptionsdashed: - style = 'style=dotted' - return '"p%s" -> "f%s" [%s];\n' % (p_id, - family.get_gramps_id(), style) - - def get_parent_link(self, p_id, parent_handle, rel): - "returns string of GraphViz edge linking child to parent" - style = '' - if (int(rel) != gen.lib.ChildRefType.BIRTH) and self.adoptionsdashed: - style = 'style=dotted' - parent = self.database.get_person_from_handle(parent_handle) - return '"p%s" -> "p%s" [%s];\n' % (p_id, parent.get_gramps_id(), style) - - def get_persons_and_families(self): - "returns string of GraphViz nodes for persons and their families" - # variable to communicate with get_person_label - self.bUseHtmlOutput = False - - # The list of families for which we have output the node, - # so we don't do it twice - buffer = "" - families_done = {} - for person_handle in self.person_handles: - # determine per person if we use HTML style label - if self.includeimg: - self.bUseHtmlOutput = True - person = self.database.get_person_from_handle(person_handle) - p_id = person.get_gramps_id() - # Output the person's node - label = self.get_person_label(person) - style = self.get_gender_style(person) - url = "" - if self.includeurl: - h = person_handle - dirpath = "ppl/%s/%s" % (h[0], h[1]) - if os.sys.platform == "win32": - dirpath = dirpath.lower() - url = ', URL="%s/%s.html", ' % (dirpath,h) - - if self.bUseHtmlOutput: - label = '<%s>' % label - else: - label = '"%s"' % label - - buffer += '"p%s" [label=%s, %s%s];\n' % (p_id, label, style, url) - - # Output families where person is a parent - if self.show_families: - family_list = person.get_family_handle_list() - for fam_handle in family_list: - fam = self.database.get_family_from_handle(fam_handle) - fam_id = fam.get_gramps_id() - if fam_handle not in families_done: - families_done[fam_handle] = 1 - label = "" - for event_ref in fam.get_event_ref_list(): - event = self.database.get_event_from_handle( - event_ref.ref) - if int(event.get_type()) == gen.lib.EventType.MARRIAGE: - label = self.get_event_string(event) - break - if self.includeid: - label = "%s (%s)" % (label, fam_id) - color = "" - if self.colorize == 'colored': - color = ', color="%s"' % self.colors['family'] - elif self.colorize == 'filled': - color = ', fillcolor="%s"' % self.colors['family'] - buffer += '"f%s" [shape=ellipse, label="%s"%s];\n' % (fam_id, label, color) - # Link this person to all his/her families. - buffer += '"f%s" -> "p%s";\n' % (fam_id, p_id) - return buffer - - def get_gender_style(self, person): - "return gender specific person style" - gender = person.get_gender() - if gender == person.MALE: - shape = 'shape="box"' - elif gender == person.FEMALE: - shape = 'shape="box", style="rounded"' - else: - shape = 'shape="hexagon"' - if self.colorize == 'outline': - return shape - else: - if gender == person.MALE: - color = self.colors['male'] - elif gender == person.FEMALE: - color = self.colors['female'] - else: - color = self.colors['unknown'] - if self.colorize == 'filled': - # In current GraphViz boxes cannot be both rounded and filled - return 'shape="box", fillcolor="%s"' % color - else: - return '%s, color="%s"' % (shape, color) - - def get_person_label(self, person): - "return person label string" - # see if we have an image to use for this person - imagePath = None - if self.bUseHtmlOutput: - mediaList = person.get_media_list() - if len(mediaList) > 0: - mediaHandle = mediaList[0].get_reference_handle() - media = self.database.get_object_from_handle(mediaHandle) - mediaMimeType = media.get_mime_type() - if mediaMimeType[0:5] == "image": - imagePath = ThumbNails.get_thumbnail_path(media.get_path()) - #test if thumbnail actually exists in thumbs (import of data means media files might not be present - imagePath = Utils.find_file(imagePath) - - label = u"" - lineDelimiter = '\\n' - - # if we have an image, then start an HTML table; remember to close the table afterwards! - if self.bUseHtmlOutput and imagePath: - lineDelimiter = '
' - label += '' % imagePath - if self.imgpos == 0: - #trick it into not stretching the image - label += '
' - else : - label += '' - else : - #no need for html label with this person - self.bUseHtmlOutput = False - - # at the very least, the label must have the person's name - nm = name_displayer.display_name(person.get_primary_name()) - if self.bUseHtmlOutput : - # avoid < and > in the name, as this is html text - label += nm.replace('<','<').replace('>','>') - else : - label += nm - p_id = person.get_gramps_id() - if self.includeid: - label += " (%s)" % p_id - if self.includedates: - birth, death = self.get_date_strings(person) - label = label + '%s(%s - %s)' % (lineDelimiter,birth, death) - - # see if we have a table that needs to be terminated - if self.bUseHtmlOutput: - label += '
' - return label - else : - # non html label is enclosed by "" so excape other " - return label.replace('"', '\\\"') - - def get_date_strings(self, person): - "returns tuple of birth/christening and death/burying date strings" - birth_ref = person.get_birth_ref() - if birth_ref: - birth_event = self.database.get_event_from_handle(birth_ref.ref) - birth = self.get_event_string(birth_event) - else: - birth = '' - death_ref = person.get_death_ref() - if death_ref: - death_event = self.database.get_event_from_handle(death_ref.ref) - death = self.get_event_string(death_event) - else: - death = '' - if birth and death: - return (birth, death) - # missing info, use (first) christening/burial instead - for event_ref in person.get_primary_event_ref_list(): - event = self.database.get_event_from_handle(event_ref.ref) - if int(event.get_type()) == gen.lib.EventType.CHRISTEN: - if not birth: - birth = self.get_event_string(event) - elif int(event.get_type()) == gen.lib.EventType.BURIAL: - if not death: - death = self.get_event_string(event) - return (birth, death) - - def get_event_string(self, event): - """ - return string for for an event label. - - Based on the data availability and preferences, we select one - of the following for a given event: - year only - complete date - place name - cause - empty string - """ - if event: - if event.get_date_object().get_year_valid(): - if self.just_years: - return '%i' % event.get_date_object().get_year() - else: - return DateHandler.get_date(event) - elif self.placecause: - place_handle = event.get_place_handle() - place = self.database.get_place_from_handle(place_handle) - if place and place.get_title(): - return place.get_title() - else: - return '' #event.get_cause() - return '' - - -#------------------------------------------------------------------------ -# -# Options class -# -#------------------------------------------------------------------------ -class GraphVizOptions(ReportOptions): - - """ - Defines options and provides handling interface. - """ - - def __init__(self,name,person_id=None): - ReportOptions.__init__(self,name,person_id) - - # Options specific for this report - self.options_dict = { - 'filter' : 0, - 'font' : "", - 'fontsize' : 14, - 'latin' : 1, - 'arrow' : 'd', - 'showfamily' : 1, - 'incdate' : 1, - 'incid' : 0, - 'justyears' : 0, - 'placecause' : 1, - 'url' : 1, - 'ratio' : "compress", - 'rankdir' : "LR", - 'color' : "filled", - 'dashedl' : 1, - 'margin' : 1.0, - 'pagedir' : 'BL', - 'pagesh' : 1, - 'pagesv' : 1, - 'note' : '', - 'noteloc' : 'b', - 'notesize' : 32, - 'gvof' : 'ps', - 'includeImages' : 1, - 'imageOnTheSide' : 1, - } - filters = ReportUtils.get_person_filters(None,include_single=False) - self.options_help = { - 'filter' : ("=num","Filter number.", - [ filt.get_name() for filt in filters ], - True ), - 'font' : ("=str","Font to use in the report.", - [ "%s\t%s" % (item[0],item[1]) for item in _options.fonts ], - False), - 'fontsize' : ("=num","Font size (in points).", - "Integer values"), - 'latin' : ("=0/1","Needs to be set if font doesn't support unicode.", - ["Supports unicode","Supports only Latin1"], - True), - 'arrow' : ("=str","Arrow styles for heads and tails.", - [ "%s\t%s" % (item[0],item[1]) for item in _options.arrowstyles ], - False), - 'showfamily': ("=0/1","Whether to show family nodes.", - ["Do not show family nodes","Show family nodes"], - True), - 'incdate' : ("=0/1","Whether to include dates.", - ["Do not include dates","Include dates"], - True), - 'incid' : ("=0/1","Whether to include IDs.", - ["Do not include IDs","Include IDs"], - True), - 'justyears' : ("=0/1","Whether to use years only.", - ["Do not use years only","Use years only"], - True), - 'placecause': ("=0/1","Whether to replace missing dates with place/cause.", - ["Do not replace blank dates","Replace blank dates"], - True), - 'url' : ("=0/1","Whether to include URLs.", - ["Do not include URLs","Include URLs"], - True), - 'ratio' : ("=str","Graph aspect ratio.", - [ "%s\t%s" % (item[0],item[1]) for item in _options.ratio ], - False), - 'rankdir' : ("=str","Graph direction.", - [ "%s\t%s" % (item[0],item[1]) for item in _options.rankdir ], - False), - 'color' : ("=str","Whether and how to colorize graph.", - [ "%s\t%s" % (item[0],item[1]) for item in _options.colors ], - False), - 'dashedl' : ("=0/1","Whether to use dotted lines for non-birth relationships.", - ["Do not use dotted lines","Use dotted lines"], - True), - 'margin' : ("=num","Margin size.", - "Floating point value, in cm"), - 'pagedir' : ("=str","Paging direction.", - [ "%s\t%s" % (item[0],item[1]) for item in _options.pagedir ], - False), - 'pagesh' : ("=num","Number of pages in horizontal direction.", - "Integer values"), - 'pagesv' : ("=num","Number of pages in vertical direction.", - "Integer values"), - 'note' : ("=str","Note to add to the graph.", - "Text"), - 'notesize' : ("=num","Note size (in points).", - "Integer values"), - 'noteloc' : ("=str","Note location.", - [ "%s\t%s" % (item[0],item[1]) for item in _options.noteloc ], - False), - 'gvof' : ("=str","Output format to convert dot file into.", - [ "%s\t%s" % (item[0],item[1]) for item in _options.formats ], - False), - 'includeImages' : ("=0/1","Whether to include the default person image.", - ["Do not include image","include image"], - True), - 'imageOnTheSide' : ("=0/1","Where to put the image if present.", - ["Image above the name","Image besides the name"], - True), - - } - - def add_list(self, options, default): - "returns compobox of given options and default value" - box = gtk.ComboBox() - store = gtk.ListStore(gobject.TYPE_STRING) - box.set_model(store) - cell = gtk.CellRendererText() - box.pack_start(cell,True) - box.add_attribute(cell,'text',0) - index = 0 - for item in options: - store.append(row=[item[2]]) - if item[0] == default: - box.set_active(index) - index = index + 1 - return box - - def add_user_options(self,dialog): - if self.handler.module_name == "rel_graph2": - dialog.make_doc_menu = self.make_doc_menu - dialog.format_menu = GraphicsFormatComboBox() - dialog.format_menu.set(self.options_dict['gvof']) - - filter_index = self.options_dict['filter'] - filter_list = ReportUtils.get_person_filters(dialog.person,include_single=False) - self.filter_menu = gtk.combo_box_new_text() - for filter in filter_list: - self.filter_menu.append_text(filter.get_name()) - if filter_index > len(filter_list): - filter_index = 0 - self.filter_menu.set_active(filter_index) - dialog.add_option(_('Filter'),self.filter_menu) - - # Content options tab - msg = _("Include Birth, Marriage and Death dates") - self.includedates_cb = gtk.CheckButton(msg) - self.includedates_cb.set_active(self.options_dict['incdate']) - dialog.add_option(None, - self.includedates_cb, - _("Include the dates that the individual " - "was born, got married and/or died " - "in the graph labels.")) - - self.just_years_cb = gtk.CheckButton(_("Limit dates to years only")) - self.just_years_cb.set_active(self.options_dict['justyears']) - dialog.add_option(None, - self.just_years_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.options_dict['placecause']) - dialog.add_option(None, - self.place_cause_cb, - _("When no birth, marriage, or death date " - "is available, the correspondent place field " - "(or cause field when blank place) will be used.")) - - # disable other date options if no dates - self.includedates_cb.connect('toggled',self.toggle_date) - self.toggle_date(self.includedates_cb) - - self.includeurl_cb = gtk.CheckButton(_("Include URLs")) - self.includeurl_cb.set_active(self.options_dict['url']) - dialog.add_option(None, - 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.includeid_cb = gtk.CheckButton(_("Include IDs")) - self.includeid_cb.set_active(self.options_dict['incid']) - dialog.add_option(None, - self.includeid_cb, - _("Include individual and family IDs.")) - - self.includeimg_cb = gtk.CheckButton(_('Include thumbnail images of people')) - self.includeimg_cb.set_active(self.options_dict['includeImages']) - dialog.add_option(None, self.includeimg_cb, - _("Whether to include thumbnails of people.")) - - self.imageLocation = gtk.combo_box_new_text() - self.imageLocation.append_text(_('place the thumbnail image above the name')) - self.imageLocation.append_text(_('place the thumbnail image beside the name')) - self.imageLocation.set_active(self.options_dict['imageOnTheSide']) - dialog.add_option(None, self.imageLocation) - - # GraphViz output options tab - self.color_box = self.add_list(_options.colors, - self.options_dict['color']) - dialog.add_frame_option(_("GraphViz Options"), - _("Graph coloring"), - self.color_box, - _("Males will be shown with blue, females " - "with red. If the sex of an individual " - "is unknown it will be shown with gray.")) - - self.arrowstyle_box = self.add_list(_options.arrowstyles, - self.options_dict['arrow']) - dialog.add_frame_option(_("GraphViz Options"), - _("Arrowhead direction"), - self.arrowstyle_box, - _("Choose the direction that the arrows point.")) - - self.font_box = self.add_list(_options.fonts, - self.options_dict['font']) - dialog.add_frame_option(_("GraphViz Options"), - _("Font family"), - self.font_box, - _("Choose the font family. If international " - "characters don't show, use FreeSans font. " - "FreeSans is available from: " - "http://www.nongnu.org/freefont/")) - - fontsize_adj = gtk.Adjustment(value=self.options_dict['fontsize'], - lower=8, upper=128, step_incr=1) - self.fontsize_sb = gtk.SpinButton(adjustment=fontsize_adj, digits=0) - dialog.add_frame_option(_("GraphViz Options"), - _("Font size (in points)"), - self.fontsize_sb, - _("The font size, in points.")) - - self.latin_cb = gtk.CheckButton(_("Output format/font requires text as latin-1")) - self.latin_cb.set_active(self.options_dict['latin']) - dialog.add_frame_option(_("GraphViz Options"), '', - self.latin_cb, - _("If text doesn't show correctly in report, use this. " - "Required e.g. for default font with PS output. " - "Not typically required for SVG or JPG output.")) - - self.adoptionsdashed_cb = gtk.CheckButton(_("Indicate non-birth relationships with dotted lines")) - self.adoptionsdashed_cb.set_active(self.options_dict['dashedl']) - dialog.add_frame_option(_("GraphViz Options"), '', - self.adoptionsdashed_cb, - _("Non-birth relationships will show up " - "as dotted lines in the graph.")) - - self.show_families_cb = gtk.CheckButton(_("Show family nodes")) - self.show_families_cb.set_active(self.options_dict['showfamily']) - dialog.add_frame_option(_("GraphViz Options"), '', - self.show_families_cb, - _("Families will show up as ellipses, linked " - "to parents and children.")) - - # Page/layout options tab - self.rank_box = self.add_list(_options.rankdir, - self.options_dict['rankdir']) - dialog.add_frame_option(_("Layout Options"), - _("Graph direction"), - self.rank_box, - _("Whether generations go from top to bottom " - "or left to right.")) - - self.ratio_box = self.add_list(_options.ratio, - self.options_dict['ratio']) - dialog.add_frame_option(_("Layout Options"), - _("Aspect ratio"), - self.ratio_box, - _("Affects greatly how the graph is layed out " - "on the page. Multiple pages overrides the " - "pages settings below.")) - - margin_adj = gtk.Adjustment(value=self.options_dict['margin'], - lower=0.0, upper=10.0, step_incr=1.0) - - self.margin_sb = gtk.SpinButton(adjustment=margin_adj, digits=1) - - dialog.add_frame_option(_("Layout Options"), - _("Margin size"), - self.margin_sb) - - hpages_adj = gtk.Adjustment(value=self.options_dict['pagesh'], - lower=1, upper=25, step_incr=1) - vpages_adj = gtk.Adjustment(value=self.options_dict['pagesv'], - 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) - - dialog.add_frame_option(_("Layout 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.")) - dialog.add_frame_option(_("Layout 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.")) - self.pagedir_box = self.add_list(_options.pagedir, - self.options_dict['pagedir']) - dialog.add_frame_option(_("Layout Options"), - _("Paging direction"), - self.pagedir_box, - _("The order in which the graph pages are output.")) - - # Notes tab - self.textbox = gtk.TextView() - self.textbox.get_buffer().set_text(self.options_dict['note']) - self.textbox.set_editable(1) - swin = gtk.ScrolledWindow() - swin.set_shadow_type(gtk.SHADOW_IN) - swin.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) - swin.add(self.textbox) - dialog.add_frame_option(_("Notes"), - _("Note to add to the graph"), - swin, - _("This text will be added to the graph.")) - self.noteloc_box = self.add_list(_options.noteloc, - self.options_dict['noteloc']) - dialog.add_frame_option(_("Notes"), - _("Note location"), - self.noteloc_box, - _("Whether note will appear on top " - "or bottom of the page.")) - - notesize_adj = gtk.Adjustment(value=self.options_dict['notesize'], lower=8, upper=128, step_incr=1) - self.notesize_sb = gtk.SpinButton(adjustment=notesize_adj, digits=0) - dialog.add_frame_option(_("Notes"), - _("Note size (in points)"), - self.notesize_sb, - _("The size of note text, in points.")) - - - def toggle_date(self, obj): - self.just_years_cb.set_sensitive(self.includedates_cb.get_active()) - self.place_cause_cb.set_sensitive(self.includedates_cb.get_active()) - - def parse_user_options(self,dialog): - self.options_dict['filter'] = int(self.filter_menu.get_active()) - self.options_dict['incdate'] = int(self.includedates_cb.get_active()) - self.options_dict['url'] = int(self.includeurl_cb.get_active()) - self.options_dict['margin'] = self.margin_sb.get_value() - self.options_dict['dashedl'] = int(self.adoptionsdashed_cb.get_active()) - self.options_dict['pagesh'] = self.hpages_sb.get_value_as_int() - self.options_dict['pagesv'] = self.vpages_sb.get_value_as_int() - self.options_dict['showfamily'] = int(self.show_families_cb.get_active()) - self.options_dict['fontsize'] = self.fontsize_sb.get_value_as_int() - self.options_dict['incid'] = int(self.includeid_cb.get_active()) - self.options_dict['justyears'] = int(self.just_years_cb.get_active()) - self.options_dict['placecause'] = int(self.place_cause_cb.get_active()) - self.options_dict['latin'] = int(self.latin_cb.get_active()) - self.options_dict['ratio'] = \ - _options.ratio[self.ratio_box.get_active()][0] - self.options_dict['rankdir'] = \ - _options.rankdir[self.rank_box.get_active()][0] - self.options_dict['color'] = \ - _options.colors[self.color_box.get_active()][0] - self.options_dict['arrow'] = \ - _options.arrowstyles[self.arrowstyle_box.get_active()][0] - self.options_dict['font'] = \ - _options.fonts[self.font_box.get_active()][0] - self.options_dict['pagedir'] = \ - _options.pagedir[self.pagedir_box.get_active()][0] - self.options_dict['noteloc'] = \ - _options.noteloc[self.noteloc_box.get_active()][0] - self.options_dict['notesize'] = self.notesize_sb.get_value_as_int() - b = self.textbox.get_buffer() - self.options_dict['note'] = \ - b.get_text(b.get_start_iter(), b.get_end_iter(), False) - - if self.handler.module_name == "rel_graph2": - self.options_dict['gvof'] = dialog.format_menu.get_format_str() - - self.options_dict['includeImages' ] = int(self.includeimg_cb.get_active() ) - self.options_dict['imageOnTheSide' ] = int(self.imageLocation.get_active() ) - -#------------------------------------------------------------------------ -# -# Dialog class -# -#------------------------------------------------------------------------ -class GraphVizDialog(ReportDialog): - - def __init__(self,dbstate,uistate,person): - self.database = dbstate.db - self.person = person - name = "rel_graph" - translated_name = _("Relationship Graph") - self.options_class = GraphVizOptions(name) - self.category = CATEGORY_CODE - ReportDialog.__init__(self,dbstate,uistate,person,self.options_class, - name,translated_name) - response = self.window.run() - if response == gtk.RESPONSE_OK: - try: - self.make_report() - except (IOError,OSError),msg: - ErrorDialog(str(msg)) - elif response == gtk.RESPONSE_DELETE_EVENT: - return - self.close() - - def make_doc_menu(self,active=None): - """Build a one item menu of document types that are - appropriate for this report.""" - self.format_menu = FormatComboBox() - self.format_menu.set() - - 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 make_report(self): - """Create the object that will produce the GraphViz file.""" - GraphViz(self.database,self.person,self.options_class) - if self.print_report.get_active (): - try: - app = Mime.get_application("text/plain")[0] - Utils.launch(app,self.options_class.get_output()) - except: - pass - -#------------------------------------------------------------------------ -# -# Combo Box classes -# -#------------------------------------------------------------------------ -class FormatComboBox(gtk.ComboBox): - """ - Format combo box class. - - Trivial class supporting only one format. - """ - - def set(self,tables=0,callback=None,obj=None,active=None): - self.store = gtk.ListStore(gobject.TYPE_STRING) - self.set_model(self.store) - cell = gtk.CellRendererText() - self.pack_start(cell,True) - self.add_attribute(cell,'text',0) - self.store.append(row=["Graphviz (dot)"]) - self.set_active(0) - - def get_label(self): - return "Graphviz (dot)" - - def get_reference(self): - return None - - def get_paper(self): - return 1 - - def get_styles(self): - return 0 - - def get_ext(self): - return '.dot' - - def get_printable(self): - _apptype = "text/plain" - print_label = None - try: - mprog = Mime.get_application(_apptype) - - if Utils.search_for(mprog[0]): - print_label = _("Open in %(program_name)s") % { 'program_name': - mprog[1]} - else: - print_label = None - except: - print_label = None - - return print_label - - def get_clname(self): - return 'dot' - -class GraphicsFormatComboBox(gtk.ComboBox): - """ - Format combo box class for graphical (not codegen) report. - """ - - def set(self,active=None): - self.store = gtk.ListStore(gobject.TYPE_STRING) - self.set_model(self.store) - cell = gtk.CellRendererText() - self.pack_start(cell,True) - self.add_attribute(cell,'text',0) - active_index = 0 - index = 0 - for item in _options.formats: - self.store.append(row=[item[2]]) - if active == item[0]: - active_index = index - index = index + 1 - self.set_active(active_index) - - def get_label(self): - return _options.formats[self.get_active()][2] - - def get_reference(self): - return EmptyDoc - - def get_paper(self): - return 1 - - def get_styles(self): - return 0 - - def get_ext(self): - return '.%s' % _options.formats[self.get_active()][0] - - def get_format_str(self): - return _options.formats[self.get_active()][0] - - def get_printable(self): - _apptype = _options.formats[self.get_active()][3] - print_label = None - try: - mprog = Mime.get_application(_apptype) - if Utils.search_for(mprog[0]): - print_label = _("Open in %(program_name)s") % { 'program_name': - mprog[1] } - else: - print_label = None - except: - print_label = None - return print_label - - def get_clname(self): - return 'print' - -#------------------------------------------------------------------------ -# -# Empty class to keep the BaseDoc-targeted format happy -# -#------------------------------------------------------------------------ -class EmptyDoc: - def __init__(self,styles,type,template,source=None): - self.print_req = 0 - - def init(self): - pass - - def print_requested(self): - self.print_req = 1 - -#------------------------------------------------------------------------ -# -# -# -#------------------------------------------------------------------------ -def cl_report(database,name,category,options_str_dict): - - clr = CommandLineReport(database,name,category,GraphVizOptions, - options_str_dict) - - # Exit here if show option was given - if clr.show: - return - - GraphViz(database,clr.person,clr.option_class) - -#------------------------------------------------------------------------ -# -# -# -#------------------------------------------------------------------------ -class GraphVizGraphics(Report): - def __init__(self,database,person,options_class): - self.database = database - self.start_person = person - self.options_class = options_class - self.doc = options_class.get_document() - - self.user_output = options_class.get_output() - (handle,self.junk_output) = tempfile.mkstemp(".dot", "rel_graph" ) - os.close( handle ) - - self.the_format = self.options_class.handler.options_dict['gvof'] - self.the_font = self.options_class.handler.options_dict['font'] - - def begin_report(self): - self.options_class.set_output(self.junk_output) - - def write_report(self): - GraphViz(self.database,self.start_person,self.options_class) - - def end_report(self): - if self.the_format == "pdf": - # Create a temporary Postscript file - (handle,tmp_ps) = tempfile.mkstemp(".ps", "rel_graph" ) - os.close( handle ) - - # Generate Postscript using dot - command = 'dot -Tps -o"%s" "%s"' % ( tmp_ps, self.junk_output ) - os.system(command) - - paper = self.options_class.handler.get_paper() - # Add .5 to remove rounding errors. - width_pt = int( (paper.get_width_inches() * 72) + 0.5 ) - height_pt = int( (paper.get_height_inches() * 72) + 0.5 ) - - # Convert to PDF using ghostscript - command = '%s -q -sDEVICE=pdfwrite -dNOPAUSE -dDEVICEWIDTHPOINTS=%d -dDEVICEHEIGHTPOINTS=%d -sOutputFile="%s" "%s" -c quit' \ - % ( gs_cmd, width_pt, height_pt, self.user_output, tmp_ps ) - os.system(command) - - os.remove(tmp_ps) - - else: - os.system('dot -T%s -o"%s" "%s"' % - (self.the_format,self.user_output,self.junk_output) ) - - os.remove(self.junk_output) - - if self.doc.print_req: - _apptype = None - for format in _options.formats: - if format[0] == self.the_format: - _apptype = format[3] - break - if _apptype: - try: - app = Mime.get_application(_apptype) - Utils.launch(app[0],self.user_output) - except: - pass - -#------------------------------------------------------------------------ -# -# -# -#------------------------------------------------------------------------ -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") - -def get_description_graphics(): - return _("Generates relationship graphs using GraphViz (dot) program. " - "This report generates dot file behind the scene and then " - "uses dot to convert it into a graph. If you want the dot" - "file itself, please use the Code Generators category.") - -#------------------------------------------------------------------------ -# -# -# -#------------------------------------------------------------------------ -register_report( - name = 'rel_graph1', - category = CATEGORY_CODE, - report_class = GraphVizDialog, - options_class = cl_report, - modes = MODE_GUI | MODE_CLI, - translated_name = _("Relationship Graph (code)..."), - status = _("Stable"), - description= get_description(), - author_name="Donald N. Allingham", - author_email="don@gramps-project.org", - unsupported=True - ) - -if _dot_found: - register_report( - name = 'rel_graph2', - category = CATEGORY_DRAW, - report_class = GraphVizGraphics, - options_class = GraphVizOptions, - modes = MODE_GUI | MODE_CLI, - translated_name = _("Relationship Graph..."), - status = _("Stable"), - description= get_description_graphics(), - author_name="Donald N. Allingham", - author_email="don@gramps-project.org", - unsupported=True - )