Major updates to GraphViz plugin.

Re-factoring:
- split large functions to smaller ones to make code more readable
- renamed few methods and variables to what they actually do
- collect the report text to a buffer first so that it can be
  converted to latin-1 at one go
- Move common style stuff to GraphViz header and remove from .dot
  comments stuff that's obvious from first .dot file settings
Fixes:
- latin-1 conversion option is explicit, not depending on font
- set the GraphViz charset attribute according to latin1
- quote node and edge identifiers
- rankdir options correspond now to TB and LR (earlier LR and RL)
New features:
- Use christening/burial dates if birth/death dates are missing
- User can add a (e.g. copyright) note to the graph and set its
  location (newlines and double quotes are automatically escaped)
- User can give the paging direction
- User can set the GraphViz aspect ratio setting
- The weight option (either for children or parents)
- Women have rounded boxes, except in filled mode
  (current GraphViz doesn't support boxes that are both rounded and filled)
New default settings:
- set the ratio to "compress" instead of "fill"
- "mclimit=2.0", prevents crossed lines
- minimum node separation is 0.25


svn: r6010
This commit is contained in:
Eero Tamminen 2006-02-27 22:02:27 +00:00
parent 9209f7eca9
commit 2bf08ece8f
2 changed files with 436 additions and 237 deletions

View File

@ -1,3 +1,32 @@
2006-02-28 Eero Tamminen <eerot@sf>
* src/plugins/GraphViz.py: major changes:
Re-factoring:
- split large functions to smaller ones to make code more readable
- renamed few methods and variables to what they actually do
- collect the report text to a buffer first so that it can be
converted to latin-1 at one go
- Move common style stuff to GraphViz header and remove from .dot
comments stuff that's obvious from first .dot file settings
Fixes:
- latin-1 conversion option is explicit, not depending on font
- set the GraphViz charset attribute according to latin1
- quote node and edge identifiers
- rankdir options correspond now to TB and LR (earlier LR and RL)
New features:
- Use christening/burial dates if birth/death dates are missing
- User can add a (e.g. copyright) note to the graph and set its
location (newlines and double quotes are automatically escaped)
- User can give the paging direction
- User can set the GraphViz aspect ratio setting
- The weight option (either for children or parents)
- Women have rounded boxes, except in filled mode
(current GraphViz doesn't support boxes that are
both rounded and filled)
New default settings:
- set the ratio to "compress" instead of "fill"
- "mclimit=2.0", prevents crossed lines
- minimum node separation is 0.25
2006-02-27 Alex Roitman <shura@gramps-project.org>
* configure.in: Bump up release number.
* Release: Version 2.0.10 "Holy Hand Grenade of Antioch" released.

View File

@ -18,7 +18,6 @@
# 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 files/Relationship graph"
@ -68,19 +67,42 @@ class _options:
("gif", "GIF image", _("GIF image")),
)
fonts = (
# Last items tells whether strings need to be converted to Latin1
("", "Default", _("Default"), 1),
("Helvetica", "Postscript / Helvetica", _("Postscript / Helvetica"), 1),
("FreeSans", "Truetype / FreeSans", _("Truetype / FreeSans"), 0),
("", "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"))
)
attract = (
("parents", "Parents", _("Parents")),
("children", "Children", _("Children")),
("none", "Neither", _("Neither"))
)
rankdir = (
("TB", "Vertical", _("Vertical")),
("LR", "Horizontal", _("Horizontal")),
("RL", "Vertical", _("Vertical")),
)
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")),
@ -115,7 +137,7 @@ class GraphViz:
The option class carries its number, and the function
returning the list of filters.
font - Font to use.
latin - Set if font supports only Latin1
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.
@ -123,12 +145,16 @@ class GraphViz:
justyears - Use years only.
placecause - Whether to replace missing dates with place or cause
url - Whether to include URLs.
rankdir - Graph direction
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
noteloc - Note location t/b
"""
colored = {
'male': 'dodgerblue4',
@ -151,11 +177,14 @@ class GraphViz:
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
@ -167,8 +196,9 @@ class GraphViz:
self.just_years = options['justyears']
self.placecause = options['placecause']
self.rankdir = options['rankdir']
self.attract = options['attract']
self.ratio = options['ratio']
self.fontname = options['font']
self.latin = options['latin']
self.colorize = options['color']
if self.colorize == 'colored':
self.colors = colored
@ -184,185 +214,300 @@ class GraphViz:
else:
self.arrowtailstyle = 'none'
self.latin = options['latin']
self.noteloc = options['noteloc']
self.note = options['note']
filter_num = options_class.get_filter_number()
filters = options_class.get_report_filters(person)
filters.extend(GenericFilter.CustomFilters.get_filters())
self.filter = filters[filter_num]
buffer = self.get_report()
self.f = open(options_class.get_output(),'w')
self.write_report()
if self.latin:
self.f.write(buffer.encode('iso-8859-1'))
else:
self.f.write(buffer)
self.f.close()
def write_report(self):
self.ind_list = self.filter.apply(self.database,
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))
self.write_header()
self.f.write("digraph GRAMPS_relationship_graph {\n")
self.f.write("bgcolor=white;\n")
self.f.write("rankdir=%s;\n" % self.rankdir)
self.f.write("center=1;\n")
self.f.write("margin=%3.2f;\n" % self.margin_small)
self.f.write("ratio=fill;\n")
# graph size
if self.orient == PAPER_LANDSCAPE:
self.f.write("size=\"%3.2f,%3.2f\";\n" % (
(self.height-self.margin*2)*self.hpages,
(self.width-self.margin*2)*self.vpages
))
rotate = 90
sizew = (self.height - self.margin*2) * self.hpages
sizeh = (self.width - self.margin*2) * self.vpages
else:
self.f.write("size=\"%3.2f,%3.2f\";\n" % (
(self.width-self.margin*2)*self.hpages,
(self.height-self.margin*2)*self.vpages
))
self.f.write("page=\"%3.2f,%3.2f\";\n" % (self.width,self.height))
rotate = 0
sizew = (self.width - self.margin*2) * self.hpages
sizeh = (self.height - self.margin*2) * self.vpages
if self.orient == PAPER_LANDSCAPE:
self.f.write("rotate=90;\n")
if len(self.ind_list) > 1:
self.dump_index()
self.dump_person()
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 [syle=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
)
self.f.write("}\n")
if self.fontname:
font = 'fontname="%s"' % self.fontname
else:
font = ''
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'
def dump_person(self):
# Hash people in a dictionary for faster inclusion checking.
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="20";\n' # 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.
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 = {}
for p_id in self.ind_list:
person_dict[p_id] = 1
for person_handle in self.ind_list:
# Hash people in a dictionary for faster inclusion checking
for person_handle in self.person_handles:
person_dict[person_handle] = 1
buffer = ""
for person_handle in self.person_handles:
person = self.database.get_person_from_handle(person_handle)
pid = person.get_gramps_id().replace('-','_')
for family_handle, mrel, frel in person.get_parent_family_handle_list():
family = self.database.get_family_from_handle(family_handle)
p_id = person.get_gramps_id()
for fam_handle,mrel,frel 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()
fadopted = frel != RelLib.Person.CHILD_REL_BIRTH
madopted = mrel != RelLib.Person.CHILD_REL_BIRTH
famid = family.get_gramps_id().replace('-','_')
if (self.show_families and
(father_handle and person_dict.has_key(father_handle) or
mother_handle and person_dict.has_key(mother_handle))):
# Link to the family node.
self.f.write('p%s -> f%s [' % (pid, famid))
self.f.write('arrowhead=%s, arrowtail=%s, ' %
(self.arrowheadstyle, self.arrowtailstyle))
if self.adoptionsdashed and (fadopted or madopted):
self.f.write('style=dotted')
else:
self.f.write('style=solid')
self.f.write('];\n')
((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
buffer += self.get_family_link(p_id, family, frel, mrel)
else:
# Link to the parents' nodes directly.
if father_handle and person_dict.has_key(father_handle):
father = self.database.get_person_from_handle(father_handle)
fid = father.get_gramps_id().replace('-','_')
self.f.write('p%s -> p%s [' % (pid, fid))
self.f.write('arrowhead=%s, arrowtail=%s, ' %
(self.arrowheadstyle, self.arrowtailstyle))
if self.adoptionsdashed and fadopted:
self.f.write('style=dotted')
else:
self.f.write('style=solid')
self.f.write('];\n')
if mother_handle and person_dict.has_key(mother_handle):
mother = self.database.get_person_from_handle(mother_handle)
mid = mother.get_gramps_id().replace('-','_')
self.f.write('p%s -> p%s [' % (pid, mid))
self.f.write('arrowhead=%s, arrowtail=%s, ' %
(self.arrowheadstyle, self.arrowtailstyle))
if self.adoptionsdashed and madopted:
self.f.write('style=dotted')
else:
self.f.write('style=solid')
self.f.write('];\n')
# Link to the parents' nodes directly, if they are in graph
if father_handle and father_handle in person_dict:
buffer += self.get_parent_link(p_id, father_handle, frel)
if mother_handle and mother_handle in person_dict:
buffer += self.get_parent_link(p_id, mother_handle, mrel)
return buffer
def dump_index(self):
# The list of families for which we have output the node, so we
# don't do it twice.
families_done = []
for person_handle in self.ind_list:
def get_family_link(self, p_id, family, frel, mrel):
"returns string of GraphViz edge linking child to family"
weight = style = ''
adopted = ((frel != RelLib.Person.CHILD_REL_BIRTH) or
(mrel != RelLib.Person.CHILD_REL_BIRTH))
if adopted and self.adoptionsdashed:
style = 'style=dotted'
if self.attract == "children":
weight = 'weight="5"'
return '"p%s" -> "f%s" [%s %s];\n' % (p_id,
family.get_gramps_id(), style, weight)
def get_parent_link(self, p_id, parent_handle, rel):
"returns string of GraphViz edge linking child to parent"
style = ''
if (rel != RelLib.Person.CHILD_REL_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"
# 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:
person = self.database.get_person_from_handle(person_handle)
# Output the person's node.
label = person.get_primary_name().get_name()
the_id = person.get_gramps_id().replace('-','_')
if self.includeid:
label = label + " (%s)" % the_id
if self.includedates:
birth_handle = person.get_birth_handle()
if birth_handle:
birth_event = self.database.get_event_from_handle(birth_handle)
birth = self.dump_event(birth_event)
else:
birth = ''
death_handle = person.get_death_handle()
if death_handle:
death_event = self.database.get_event_from_handle(death_handle)
death = self.dump_event(death_event)
else:
death = ''
label = label + '\\n(%s - %s)' % (birth, death)
self.f.write('p%s [shape=box, ' % the_id)
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.get_handle()
self.f.write('URL="ppl/%s/%s/%s.html", ' % (h[0],h[1],h))
if self.colorize != 'outline':
if self.colorize == 'filled':
style = 'style=filled, fillcolor'
else:
style = 'color'
gender = person.get_gender()
if gender == person.MALE:
self.f.write('%s=%s, ' % (style, self.colors['male']))
elif gender == person.FEMALE:
self.f.write('%s=%s, ' % (style, self.colors['female']))
else:
self.f.write('%s=%s, ' % (style, self.colors['unknown']))
if self.latin:
label = label.encode('iso-8859-1')
self.f.write('fontname="%s", label="%s"];\n' % (self.fontname,label))
h = person_handle
url = ', URL="ppl/%s/%s/%s.html", ' % (h[0],h[1],h)
buffer += '"p%s" [label="%s", %s%s];\n' % (p_id, label, style, url)
# Output families's nodes.
# 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)
fid = fam.get_gramps_id().replace('-','_')
fam_id = fam.get_gramps_id()
if fam_handle not in families_done:
families_done.append(fam_handle)
self.f.write('f%s [shape=ellipse, ' % fid)
if self.colorize == 'colored':
self.f.write('color=%s, ' % self.colors['family'])
elif self.colorize == 'filled':
self.f.write('style=filled fillcolor=%s, ' % self.colors['family'])
marriage = ""
families_done[fam_handle] = 1
label = ""
for event_handle in fam.get_event_list():
if event_handle:
event = self.database.get_event_from_handle(event_handle)
if event.get_name() == "Marriage":
m = event
label = self.get_event_string(event)
break
else:
m = None
if m:
marriage = self.dump_event(m)
if self.includeid:
marriage = marriage + " (%s)" % fid
self.f.write('fontname="%s", label="%s"];\n'
% (self.fontname,marriage))
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.
self.f.write('f%s -> p%s [' % (fid, the_id))
self.f.write('arrowhead=%s, arrowtail=%s, ' %
(self.arrowheadstyle, self.arrowtailstyle))
self.f.write('style=solid];\n')
if self.attract == "parents":
weight = 'weight="5"'
else:
weight = ''
buffer += '"f%s" -> "p%s" [%s];\n' % (fam_id, p_id, weight)
return buffer
def dump_event(self,event):
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"
label = person.get_primary_name().get_name()
p_id = person.get_gramps_id()
if self.includeid:
label = label + " (%s)" % p_id
if self.includedates:
birth, death = self.get_date_strings(person)
label = label + '\\n(%s - %s)' % (birth, death)
return label.replace('"', '\\\"')
def get_date_strings(self, person):
"returns tuple of birth/christening and death/burying date strings"
birth_handle = person.get_birth_handle()
if birth_handle:
birth_event = self.database.get_event_from_handle(birth_handle)
birth = self.get_event_string(birth_event)
else:
birth = ''
death_handle = person.get_death_handle()
if death_handle:
death_event = self.database.get_event_from_handle(death_handle)
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_handle in person.get_event_list():
event = self.database.get_event_from_handle(event_handle)
if event.get_name() == "Christening":
if not birth:
birth = self.get_event_string(event)
elif event.get_name() == "Burial":
if not death:
death = self.get_event_string(event)
return (birth, death)
def get_event_string(self, event):
"""
Compile an event label.
return string for for an event label.
Based on the data availability and preferences, we select one
of the following for a given event:
@ -386,33 +531,6 @@ class GraphViz:
return event.get_cause()
return ''
def write_header(self):
"""
Write header listing the options used.
"""
self.f.write("/* GRAMPS - Relationship graph\n")
self.f.write(" *\n")
self.f.write(" * Report options:\n")
self.f.write(" * font style : %s\n" % self.fontname)
self.f.write(" * style arrow head : %s\n" % self.arrowheadstyle)
self.f.write(" * tail : %s\n" % self.arrowtailstyle)
self.f.write(" * graph direction : %s\n" % self.rankdir)
self.f.write(" * include URLs : %s\n" % bool(self.includeurl))
self.f.write(" * IDs : %s\n" % bool(self.includeid))
self.f.write(" * dates : %s\n" % bool(self.includedates))
self.f.write(" * just year : %s\n" % bool(self.just_years))
self.f.write(" * place or cause : %s\n" % bool(self.placecause))
self.f.write(" * colorize : %s\n" % bool(self.colorize))
self.f.write(" * dotted adoptions : %s\n" % bool(self.adoptionsdashed))
self.f.write(" * show family nodes : %s\n" % bool(self.show_families))
self.f.write(" * margin : %3.2fin\n" % self.margin_small)
self.f.write(" * pages horizontal : %s\n" % self.hpages)
self.f.write(" * vertical : %s\n" % self.vpages)
self.f.write(" * page width : %3.2fin\n" % self.width)
self.f.write(" * height : %3.2fin\n" % self.height)
self.f.write(" *\n")
self.f.write(" * Generated on %s by GRAMPS\n" % asctime())
self.f.write(" */\n\n")
#------------------------------------------------------------------------
#
@ -440,12 +558,17 @@ class GraphVizOptions(ReportOptions.ReportOptions):
'justyears' : 0,
'placecause' : 1,
'url' : 1,
'ratio' : "compress",
'attract' : "none",
'rankdir' : "LR",
'color' : "filled",
'dashedl' : 1,
'margin' : 1.0,
'pagedir' : 'BL',
'pagesh' : 1,
'pagesv' : 1,
'note' : '',
'noteloc' : 'b',
'gvof' : 'ps',
}
@ -477,7 +600,13 @@ class GraphVizOptions(ReportOptions.ReportOptions):
'url' : ("=0/1","Whether to include URLs.",
["Do not include URLs","Include URLs"],
True),
'rankdir' : ("=str","Graph direction.",
'ratio' : ("=str","Graph aspect ratio.",
[ "%s\t%s" % (item[0],item[1]) for item in _options.ratio ],
False),
'attract' : ("=str","What to group closer.",
[ "%s\t%s" % (item[0],item[1]) for item in _options.attract ],
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.",
@ -488,10 +617,18 @@ class GraphVizOptions(ReportOptions.ReportOptions):
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"),
'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),
@ -533,6 +670,22 @@ class GraphVizOptions(ReportOptions.ReportOptions):
def make_doc_menu(self,dialog,active=None):
pass
def add_list(self, options, default):
"returns compobox of given options and default value"
box = gtk.ComboBox()
store = gtk.ListStore(str)
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
@ -586,36 +739,16 @@ class GraphVizOptions(ReportOptions.ReportOptions):
_("Include individual and family IDs."))
# GraphViz output options tab
self.rank_box = gtk.ComboBox()
store = gtk.ListStore(str)
self.rank_box.set_model(store)
cell = gtk.CellRendererText()
self.rank_box.pack_start(cell,True)
self.rank_box.add_attribute(cell,'text',0)
index = 0
for item in _options.rankdir:
store.append(row=[item[2]])
if item[0] == self.options_dict['rankdir']:
self.rank_box.set_active(index)
index = index + 1
self.rank_box = self.add_list(_options.rankdir,
self.options_dict['rankdir'])
dialog.add_frame_option(_("GraphViz Options"),
_("Graph direction"),
self.rank_box,
_("Whether generations go from top to bottom "
"or left to right."))
self.color_box = gtk.ComboBox()
store = gtk.ListStore(str)
self.color_box.set_model(store)
cell = gtk.CellRendererText()
self.color_box.pack_start(cell,True)
self.color_box.add_attribute(cell,'text',0)
index = 0
for item in _options.colors:
store.append(row=[item[2]])
if item[0] == self.options_dict['color']:
self.color_box.set_active(index)
index = index + 1
self.color_box = self.add_list(_options.colors,
self.options_dict['color'])
dialog.add_frame_option(_("GraphViz Options"),
_("Graph coloring"),
self.color_box,
@ -623,39 +756,15 @@ class GraphVizOptions(ReportOptions.ReportOptions):
"with red. If the sex of an individual "
"is unknown it will be shown with gray."))
self.arrowstyle_box = gtk.ComboBox()
store = gtk.ListStore(str)
self.arrowstyle_box.set_model(store)
cell = gtk.CellRendererText()
self.arrowstyle_box.pack_start(cell,True)
self.arrowstyle_box.add_attribute(cell,'text',0)
index = 0
for item in _options.arrowstyles:
store.append(row=[item[2]])
if item[0] == self.options_dict['arrow']:
self.arrowstyle_box.set_active(index)
index = index + 1
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 = gtk.ComboBox()
store = gtk.ListStore(str)
self.font_box.set_model(store)
cell = gtk.CellRendererText()
self.font_box.pack_start(cell,True)
self.font_box.add_attribute(cell,'text',0)
index = 0
for item in _options.fonts:
if item[3]:
name = "%s (iso-latin1 font)" % item[2]
else:
name = item[2]
store.append(row=[name])
if item[0] == self.options_dict['font']:
self.font_box.set_active(index)
index = index + 1
self.font_box = self.add_list(_options.fonts,
self.options_dict['font'])
dialog.add_frame_option(_("GraphViz Options"),
_("Font family"),
self.font_box,
@ -664,6 +773,13 @@ class GraphVizOptions(ReportOptions.ReportOptions):
"FreeSans is available from: "
"http://www.nongnu.org/freefont/"))
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."))
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"), '',
@ -678,13 +794,31 @@ class GraphVizOptions(ReportOptions.ReportOptions):
_("Families will show up as ellipses, linked "
"to parents and children."))
# Page options tab
# Page/layout options tab
self.attract_box = self.add_list(_options.attract,
self.options_dict['attract'])
dialog.add_frame_option(_("Layout Options"),
_("What to group closer together"),
self.attract_box,
_("Whether to group parents or children "
"together or whether both have same "
"weights."))
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(_("Page Options"),
dialog.add_frame_option(_("Layout Options"),
_("Margin size"),
self.margin_sb)
@ -696,14 +830,14 @@ class GraphVizOptions(ReportOptions.ReportOptions):
self.hpages_sb = gtk.SpinButton(adjustment=hpages_adj, digits=0)
self.vpages_sb = gtk.SpinButton(adjustment=vpages_adj, digits=0)
dialog.add_frame_option(_("Page Options"),
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(_("Page Options"),
dialog.add_frame_option(_("Layout Options"),
_("Number of Vertical Pages"),
self.vpages_sb,
_("GraphViz can create very large graphs "
@ -711,6 +845,32 @@ class GraphVizOptions(ReportOptions.ReportOptions):
"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 on top or bottom of the page."))
def toggle_date(self, obj):
self.just_years_cb.set_sensitive(self.includedates_cb.get_active())
@ -727,6 +887,10 @@ class GraphVizOptions(ReportOptions.ReportOptions):
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['attract'] = \
_options.attract[self.attract_box.get_active()][0]
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'] = \
@ -735,8 +899,14 @@ class GraphVizOptions(ReportOptions.ReportOptions):
_options.arrowstyles[self.arrowstyle_box.get_active()][0]
self.options_dict['font'] = \
_options.fonts[self.font_box.get_active()][0]
self.options_dict['latin'] = \
_options.fonts[self.font_box.get_active()][3]
self.options_dict['pagedir'] = \
_options.pagedir[self.pagedir_box.get_active()][0]
self.options_dict['noteloc'] = \
_options.noteloc[self.noteloc_box.get_active()][0]
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()