Add new ancestor and descendant gramplets

svn: r23244
This commit is contained in:
Nick Hall 2013-10-03 18:06:50 +00:00
parent ac35859e27
commit 543fda3d5d
7 changed files with 336 additions and 471 deletions

View File

@ -0,0 +1,161 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2013 Nick Hall
#
# 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$
"""Descendant Gramplet"""
#------------------------------------------------------------------------
#
# Python modules
#
#------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
#-------------------------------------------------------------------------
#
# GTK modules
#
#-------------------------------------------------------------------------
from gi.repository import Gtk
#------------------------------------------------------------------------
#
# Gramps modules
#
#------------------------------------------------------------------------
from gramps.gen.plug import Gramplet
from gramps.gui.listmodel import ListModel, NOSORT
from gramps.gui.editors import EditPerson
from gramps.gen.errors import WindowActiveError
from gramps.gen.display.name import displayer as name_displayer
from gramps.gen.datehandler import get_date
from gramps.gen.utils.db import get_birth_or_fallback, get_death_or_fallback
class Ancestor(Gramplet):
def init(self):
self.gui.WIDGET = self.build_gui()
self.gui.get_container_widget().remove(self.gui.textview)
self.gui.get_container_widget().add_with_viewport(self.gui.WIDGET)
self.gui.WIDGET.show()
def build_gui(self):
"""
Build the GUI interface.
"""
self.view = Gtk.TreeView()
self.view.set_tooltip_column(3)
titles = [(_('Name'), 0, 230),
(_('Birth'), 2, 100),
('', NOSORT, 1),
('', NOSORT, 1), # tooltip
('', NOSORT, 100)] # handle
self.model = ListModel(self.view, titles, list_mode="tree",
event_func=self.cb_double_click)
return self.view
def get_has_data(self, active_handle):
"""
Return True if the gramplet has data, else return False.
"""
if active_handle is None:
return False
person = self.dbstate.db.get_person_from_handle(active_handle)
family_handle = person.get_main_parents_family_handle()
family = self.dbstate.db.get_family_from_handle(family_handle)
if family and (family.get_father_handle() or
family.get_mother_handle()):
return True
return False
def cb_double_click(self, treeview):
"""
Handle double click on treeview.
"""
(model, iter_) = treeview.get_selection().get_selected()
if not iter_:
return
try:
handle = model.get_value(iter_, 4)
person = self.dbstate.db.get_person_from_handle(handle)
EditPerson(self.dbstate, self.uistate, [], person)
except WindowActiveError:
pass
def db_changed(self):
self.update()
def active_changed(self, handle):
self.update()
def update_has_data(self):
active_handle = self.get_active('Person')
self.set_has_data(self.get_has_data(active_handle))
def main(self):
active_handle = self.get_active('Person')
self.model.clear()
if active_handle:
self.add_to_tree(None, active_handle)
self.view.expand_all()
self.set_has_data(self.get_has_data(active_handle))
else:
self.set_has_data(False)
def add_to_tree(self, parent_id, person_handle):
person = self.dbstate.db.get_person_from_handle(person_handle)
name = name_displayer.display(person)
birth = get_birth_or_fallback(self.dbstate.db, person)
death = get_death_or_fallback(self.dbstate.db, person)
birth_text = birth_date = birth_sort = ''
if birth:
birth_date = get_date(birth)
birth_sort = '%012d' % birth.get_date_object().get_sort_value()
birth_text = _('%(abbr)s %(date)s') % \
{'abbr': birth.type.get_abbreviation(),
'date': birth_date}
death_date = death_sort = death_text = ''
if death:
death_date = get_date(death)
death_sort = '%012d' % death.get_date_object().get_sort_value()
death_text = _('%(abbr)s %(date)s') % \
{'abbr': death.type.get_abbreviation(),
'date': death_date}
tooltip = name + '\n' + birth_text + '\n' + death_text
item_id = self.model.add([name, birth_date, birth_sort,
tooltip, person_handle], node=parent_id)
family_handle = person.get_main_parents_family_handle()
family = self.dbstate.db.get_family_from_handle(family_handle)
if family:
if family.get_father_handle():
self.add_to_tree(item_id, family.get_father_handle())
if family.get_mother_handle():
self.add_to_tree(item_id, family.get_mother_handle())
return item_id

View File

@ -0,0 +1,156 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2013 Nick Hall
#
# 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$
"""Descendant Gramplet"""
#------------------------------------------------------------------------
#
# Python modules
#
#------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
#-------------------------------------------------------------------------
#
# GTK modules
#
#-------------------------------------------------------------------------
from gi.repository import Gtk
#------------------------------------------------------------------------
#
# Gramps modules
#
#------------------------------------------------------------------------
from gramps.gen.plug import Gramplet
from gramps.gui.listmodel import ListModel, NOSORT
from gramps.gui.editors import EditPerson
from gramps.gen.errors import WindowActiveError
from gramps.gen.display.name import displayer as name_displayer
from gramps.gen.datehandler import get_date
from gramps.gen.utils.db import get_birth_or_fallback, get_death_or_fallback
class Descendant(Gramplet):
def init(self):
self.gui.WIDGET = self.build_gui()
self.gui.get_container_widget().remove(self.gui.textview)
self.gui.get_container_widget().add_with_viewport(self.gui.WIDGET)
self.gui.WIDGET.show()
def build_gui(self):
"""
Build the GUI interface.
"""
self.view = Gtk.TreeView()
self.view.set_tooltip_column(3)
titles = [(_('Name'), 0, 230),
(_('Birth'), 2, 100),
('', NOSORT, 1),
('', NOSORT, 1), # tooltip
('', NOSORT, 100)] # handle
self.model = ListModel(self.view, titles, list_mode="tree",
event_func=self.cb_double_click)
return self.view
def get_has_data(self, active_handle):
"""
Return True if the gramplet has data, else return False.
"""
if active_handle is None:
return False
person = self.dbstate.db.get_person_from_handle(active_handle)
for family_handle in person.get_family_handle_list():
family = self.dbstate.db.get_family_from_handle(family_handle)
for child_ref in family.get_child_ref_list():
return True
return False
def cb_double_click(self, treeview):
"""
Handle double click on treeview.
"""
(model, iter_) = treeview.get_selection().get_selected()
if not iter_:
return
try:
handle = model.get_value(iter_, 4)
person = self.dbstate.db.get_person_from_handle(handle)
EditPerson(self.dbstate, self.uistate, [], person)
except WindowActiveError:
pass
def db_changed(self):
self.update()
def active_changed(self, handle):
self.update()
def update_has_data(self):
active_handle = self.get_active('Person')
self.set_has_data(self.get_has_data(active_handle))
def main(self):
active_handle = self.get_active('Person')
self.model.clear()
if active_handle:
self.add_to_tree(None, active_handle)
self.view.expand_all()
self.set_has_data(self.get_has_data(active_handle))
else:
self.set_has_data(False)
def add_to_tree(self, parent_id, person_handle):
person = self.dbstate.db.get_person_from_handle(person_handle)
name = name_displayer.display(person)
birth = get_birth_or_fallback(self.dbstate.db, person)
death = get_death_or_fallback(self.dbstate.db, person)
birth_text = birth_date = birth_sort = ''
if birth:
birth_date = get_date(birth)
birth_sort = '%012d' % birth.get_date_object().get_sort_value()
birth_text = _('%(abbr)s %(date)s') % \
{'abbr': birth.type.get_abbreviation(),
'date': birth_date}
death_date = death_sort = death_text = ''
if death:
death_date = get_date(death)
death_sort = '%012d' % death.get_date_object().get_sort_value()
death_text = _('%(abbr)s %(date)s') % \
{'abbr': death.type.get_abbreviation(),
'date': death_date}
tooltip = name + '\n' + birth_text + '\n' + death_text
item_id = self.model.add([name, birth_date, birth_sort,
tooltip, person_handle], node=parent_id)
for family_handle in person.get_family_handle_list():
family = self.dbstate.db.get_family_from_handle(family_handle)
for child_ref in family.get_child_ref_list():
self.add_to_tree(item_id, child_ref.ref)
return item_id

View File

@ -1,166 +0,0 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2007-2008 Brian G. Matherly
# Copyright (C) 2008 Douglas S. Blank
# Copyright (C) 2010 Jakim Friant
#
# 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$
"""Descendant Gramplet"""
#------------------------------------------------------------------------
#
# standard python modules
#
#------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
#------------------------------------------------------------------------
#
# GRAMPS modules
#
#------------------------------------------------------------------------
from gramps.gen.plug import Gramplet
from gramps.gen.plug.report import utils as ReportUtils
from gramps.gen.display.name import displayer as name_displayer
from gramps.gen.datehandler import get_date
from gramps.gen.utils.db import get_birth_or_fallback, get_death_or_fallback
class DescendantGramplet(Gramplet):
def init(self):
self.set_text(_("No Family Tree loaded."))
self.set_tooltip(_("Move mouse over links for options"))
self.set_use_markup(True)
self.no_wrap()
self.max_generations = 100
def db_changed(self):
self.update()
def active_changed(self, handle):
self.update()
def main(self):
active_handle = self.get_active('Person')
if not active_handle:
self.set_text(_("No Active Person selected."))
return
self.set_text("")
self.center_person = self.dbstate.db.get_person_from_handle(active_handle)
if self.center_person is None:
return
name = name_displayer.display(self.center_person)
# feature request 2356: avoid genitive form
title = _("Descendants of %s") % name
self.append_text(title)
self.append_text("\n\n")
self.dump(1,self.center_person)
self.append_text("", scroll_to="begin")
def dump_dates(self, person):
birth = get_birth_or_fallback(self.dbstate.db, person)
death = get_death_or_fallback(self.dbstate.db, person)
if birth:
birth_date = get_date(birth)
if death:
death_date = get_date(death)
if birth or death:
self.append_text(' (')
birth_place = ""
if birth:
bplace_handle = birth.get_place_handle()
if bplace_handle:
birth_place = self.dbstate.db.get_place_from_handle(
bplace_handle).get_title()
death_place = ""
if death:
dplace_handle = death.get_place_handle()
if dplace_handle:
death_place = self.dbstate.db.get_place_from_handle(
dplace_handle).get_title()
if birth:
if birth_place:
self.append_text("%(event_abbrev)s %(birth_date)s - %(place)s" % {
'event_abbrev': birth.type.get_abbreviation(),
'birth_date' : birth_date,
'place' : birth_place,
})
else:
self.append_text("%(event_abbrev)s %(birth_date)s" % {
'event_abbrev': birth.type.get_abbreviation(),
'birth_date' : birth_date
})
if death:
if birth:
self.append_text(', ')
if death_place:
self.append_text("%(event_abbrev)s %(death_date)s - %(place)s" % {
'event_abbrev': death.type.get_abbreviation(),
'death_date' : death_date,
'place' : death_place,
})
else:
self.append_text("%(event_abbrev)s %(death_date)s" % {
'event_abbrev': death.type.get_abbreviation(),
'death_date' : death_date
})
self.append_text(')')
def dump(self,level,person):
self.append_text(" " * (level - 1))
self.append_text("%s. " % level)
self.link(name_displayer.display_name(person.get_primary_name()),
'Person', person.handle,
tooltip=_("Click to make active\n") + \
_("Right-click to edit"))
self.dump_dates(person)
self.append_text("\n")
if level >= self.max_generations:
return
for family_handle in person.get_family_handle_list():
family = self.dbstate.db.get_family_from_handle(family_handle)
spouse_handle = ReportUtils.find_spouse(person,family)
if spouse_handle:
spouse = self.dbstate.db.get_person_from_handle(spouse_handle)
self.append_text(" " * (level - 1))
self.append_text(_(" sp. "))
self.link(name_displayer.display_name(spouse.get_primary_name()),
'Person', spouse.handle,
tooltip=_("Click to make active\n") + \
_("Right-click to edit"))
self.dump_dates(spouse)
self.append_text("\n")
childlist = family.get_child_ref_list()[:]
for child_ref in childlist:
child = self.dbstate.db.get_person_from_handle(child_ref.ref)
self.dump(level+1,child)

View File

@ -84,10 +84,10 @@ register(GRAMPLET,
name=_("Descendant"), name=_("Descendant"),
description = _("Gramplet showing active person's descendants"), description = _("Gramplet showing active person's descendants"),
status = STABLE, status = STABLE,
fname="descendgramplet.py", fname="descendant.py",
height=100, height=100,
expand=True, expand=True,
gramplet = 'DescendantGramplet', gramplet = 'Descendant',
gramplet_title=_("Descendants"), gramplet_title=_("Descendants"),
detached_width = 500, detached_width = 500,
detached_height = 500, detached_height = 500,
@ -96,6 +96,23 @@ register(GRAMPLET,
navtypes=["Person"], navtypes=["Person"],
) )
register(GRAMPLET,
id = "Ancestor",
name=_("Ancestor"),
description = _("Gramplet showing active person's ancestors"),
status = STABLE,
fname="ancestor.py",
height=100,
expand=True,
gramplet = 'Ancestor',
gramplet_title=_("Ancestors"),
detached_width = 500,
detached_height = 500,
version="1.0.0",
gramps_target_version="4.1",
navtypes=["Person"],
)
register(GRAMPLET, register(GRAMPLET,
id= "Fan Chart", id= "Fan Chart",
name=_("Fan Chart"), name=_("Fan Chart"),

View File

@ -1,131 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkDialog" id="desbrowser">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="default_width">450</property>
<property name="default_height">400</property>
<property name="type_hint">dialog</property>
<signal name="delete-event" handler="on_delete_event" swapped="no"/>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="button1">
<property name="label">gtk-close</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="destroy_passed_object" object="desbrowser" swapped="yes"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button2">
<property name="label">gtk-help</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_help_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="justify">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">10</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="tree1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Double-click on the row to edit personal information</property>
<attributes>
<attribute name="style" value="italic"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="0">button1</action-widget>
<action-widget response="-11">button2</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -1,149 +0,0 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2008 Brian G. Matherly
# Copyright (C) 2010 Jakim Friant
#
# 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$
"""Tools/Analysis and Exploration/Interactive Descendant Browser"""
#------------------------------------------------------------------------
#
# GTK/GNOME modules
#
#------------------------------------------------------------------------
from gi.repository import Gdk
from gi.repository import Gtk
#------------------------------------------------------------------------
#
# GRAMPS modules
#
#------------------------------------------------------------------------
from gramps.gen.const import URL_MANUAL_PAGE
from gramps.gen.display.name import displayer as name_displayer
from gramps.gui.plug import tool
from gramps.gui.display import display_help
from gramps.gui.managedwindow import ManagedWindow
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
from gramps.gui.glade import Glade
from gramps.gui.editors import EditPerson
#------------------------------------------------------------------------
#
# Constants
#
#------------------------------------------------------------------------
WIKI_HELP_PAGE = '%s_-_Tools' % URL_MANUAL_PAGE
WIKI_HELP_SEC = _('manual|Interactive_Descendant_Browser...')
class DesBrowse(tool.ActivePersonTool, ManagedWindow):
def __init__(self, dbstate, user, options_class, name, callback=None):
uistate = user.uistate
tool.ActivePersonTool.__init__(self, dbstate, uistate, options_class,
name)
if self.fail:
return
self.dbstate = dbstate
active_handle = uistate.get_active('Person')
self.active = dbstate.db.get_person_from_handle(active_handle)
self.callback = callback
self.active_name = _("Descendant Browser: %s") % (
name_displayer.display(self.active)
)
ManagedWindow.__init__(self, uistate, [], self)
self.glade = Glade()
self.glade.connect_signals({
"destroy_passed_object" : self.close,
"on_help_clicked" : self.on_help_clicked,
"on_delete_event" : self.close,
})
window = self.glade.toplevel
self.set_window(window,self.glade.get_object('title'),
self.active_name)
self.tree = self.glade.get_object("tree1")
col = Gtk.TreeViewColumn('',Gtk.CellRendererText(),text=0)
self.tree.append_column(col)
self.tree.set_rules_hint(True)
self.tree.set_headers_visible(False)
self.tree.connect('button-press-event', self.button_press_event)
self.make_new_model()
self.show()
def build_menu_names(self, obj):
return (self.active_name,_("Descendant Browser tool"))
def make_new_model(self):
self.model = Gtk.TreeStore(str, object)
self.tree.set_model(self.model)
self.add_to_tree(None, None, self.active.get_handle())
self.tree.expand_all()
def on_help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
display_help(webpage=WIKI_HELP_PAGE, section=WIKI_HELP_SEC)
def add_to_tree(self, parent_id, sib_id, person_handle):
item_id = self.model.insert_after(parent_id, sib_id)
person = self.db.get_person_from_handle(person_handle)
self.model.set(item_id, 0, name_displayer.display(person))
self.model.set(item_id, 1, person_handle)
prev_id = None
for family_handle in person.get_family_handle_list():
family = self.db.get_family_from_handle(family_handle)
for child_ref in family.get_child_ref_list():
prev_id = self.add_to_tree(item_id, prev_id, child_ref.ref)
return item_id
def button_press_event(self, obj, event):
if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1:
store, node = self.tree.get_selection().get_selected()
if node:
person_handle = store.get_value(node, 1)
person = self.db.get_person_from_handle(person_handle)
EditPerson(self.dbstate, self.uistate, self.track, person,
self.this_callback)
def this_callback(self, obj):
self.callback()
self.make_new_model()
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
class DesBrowseOptions(tool.ToolOptions):
"""
Defines options and provides handling interface.
"""
def __init__(self, name,person_id=None):
tool.ToolOptions.__init__(self, name,person_id)

View File

@ -93,29 +93,6 @@ optionclass = 'CheckOptions',
tool_modes = [TOOL_MODE_GUI, TOOL_MODE_CLI] tool_modes = [TOOL_MODE_GUI, TOOL_MODE_CLI]
) )
#------------------------------------------------------------------------
#
# Interactive Descendant Browser
#
#------------------------------------------------------------------------
register(TOOL,
id = 'dbrowse',
name = _("Interactive Descendant Browser"),
description = _("Provides a browsable hierarchy based on the active person"),
version = '1.0',
gramps_target_version = '4.1',
status = STABLE,
fname = 'desbrowser.py',
authors = ["Donald N. Allingham"],
authors_email = ["don@gramps-project.org"],
category = TOOL_ANAL,
toolclass = 'DesBrowse',
optionclass = 'DesBrowseOptions',
tool_modes = [TOOL_MODE_GUI]
)
#------------------------------------------------------------------------ #------------------------------------------------------------------------
# #
# Compare Individual Events # Compare Individual Events