2fbee469a7
2008-01-06 Douglas S. Blank <dblank@cs.brynmawr.edu> svn: r9736
679 lines
25 KiB
Python
679 lines
25 KiB
Python
# Gramps - a GTK+/GNOME based genealogy program
|
|
#
|
|
# Copyright (C) 2000-2007 Donald N. Allingham
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
__author__ = "Douglas Blank <dblank@cs.brynmawr.edu>"
|
|
__version__ = "$Revision: $"
|
|
|
|
import sys
|
|
import os
|
|
import re
|
|
import time
|
|
import urllib
|
|
|
|
import gen.lib
|
|
from DataViews import register, Gramplet
|
|
from BasicUtils import name_displayer
|
|
from QuickReports import run_quick_report_by_name
|
|
import DateHandler
|
|
from gettext import gettext as _
|
|
|
|
#
|
|
# Hello World, in Gramps Gramplets
|
|
#
|
|
# First, you need a function or class that takes a single argument
|
|
# a GuiGramplet:
|
|
|
|
#from DataViews import register
|
|
#def init(gui):
|
|
# gui.set_text("Hello world!")
|
|
|
|
# In this function, you can do some things to update the gramplet,
|
|
# like set text of the main scroll window.
|
|
|
|
# Then, you need to register the gramplet:
|
|
|
|
#register(type="gramplet", # case in-senstitive keyword "gramplet"
|
|
# name="Hello World Gramplet", # gramplet name, unique among gramplets
|
|
# height = 20,
|
|
# content = init, # function/class; takes guigramplet
|
|
# title="Sample Gramplet", # default title, user changeable
|
|
# )
|
|
|
|
# There are a number of arguments that you can provide, including:
|
|
# name, height, content, title, expand, state, data
|
|
|
|
# Here is a Gramplet object. It has a number of method possibilities:
|
|
# init- run once, on construction
|
|
# active_changed- run when active-changed is triggered
|
|
# db_changed- run when db-changed is triggered
|
|
# main- run once per db change, main process (a generator)
|
|
|
|
# You should call update() to run main; don't call main directly
|
|
|
|
class CalendarGramplet(Gramplet):
|
|
def init(self):
|
|
import gtk
|
|
self.tooltip = _("Double-click a day for details")
|
|
self.gui.calendar = gtk.Calendar()
|
|
self.gui.calendar.connect('day-selected-double-click', self.double_click)
|
|
self.gui.calendar.connect('month-changed', self.refresh)
|
|
db_signals = ['event-add',
|
|
'event-update',
|
|
'event-delete',
|
|
'event-rebuild',
|
|
]
|
|
for signal in db_signals:
|
|
self.dbstate.db.connect(signal, lambda *args: self.run_update(signal, *args))
|
|
|
|
self.gui.scrolledwindow.remove(self.gui.textview)
|
|
self.gui.scrolledwindow.add_with_viewport(self.gui.calendar)
|
|
self.gui.calendar.show()
|
|
self.birthdays = True
|
|
self.dates = {}
|
|
|
|
def db_changed(self):
|
|
self.update()
|
|
|
|
def run_update(self, signal, *args):
|
|
self.update()
|
|
|
|
def refresh(self, *obj):
|
|
self.gui.calendar.freeze()
|
|
self.gui.calendar.clear_marks()
|
|
year, month, day = self.gui.calendar.get_date()
|
|
for date in self.dates:
|
|
if ((date[0] == year) and
|
|
(date[1] == month + 1) and
|
|
(date[2] > 0 and date[2] <= day)):
|
|
self.gui.calendar.mark_day(date[2])
|
|
self.gui.calendar.thaw()
|
|
|
|
def main(self):
|
|
self.dates = {}
|
|
# for each day in events
|
|
people = self.gui.dbstate.db.get_person_handles(sort_handles=False)
|
|
cnt = 0
|
|
for person_handle in people:
|
|
if cnt % 350 == 0:
|
|
yield True
|
|
person = self.gui.dbstate.db.get_person_from_handle(person_handle)
|
|
birth_ref = person.get_birth_ref()
|
|
birth_date = None
|
|
if birth_ref:
|
|
birth_event = self.gui.dbstate.db.get_event_from_handle(birth_ref.ref)
|
|
birth_date = birth_event.get_date_object()
|
|
if self.birthdays and birth_date != None:
|
|
year = birth_date.get_year()
|
|
month = birth_date.get_month()
|
|
day = birth_date.get_day()
|
|
#age = self.year - year
|
|
self.dates[(year, month, day)] = birth_event.handle
|
|
cnt += 1
|
|
self.refresh()
|
|
|
|
def double_click(self, obj):
|
|
# bring up events on this day
|
|
year, month, day = self.gui.calendar.get_date()
|
|
month += 1
|
|
date = gen.lib.Date()
|
|
date.set_yr_mon_day(year, month, day)
|
|
run_quick_report_by_name(self.gui.dbstate,
|
|
self.gui.uistate,
|
|
'onthisday',
|
|
date)
|
|
|
|
class LogGramplet(Gramplet):
|
|
def init(self):
|
|
self.tooltip = _("Click name to change active\nDouble-click name to edit")
|
|
self.set_text(_("Log for this Session"))
|
|
self.append_text("\n--------------------\n")
|
|
self.history = {}
|
|
|
|
def db_changed(self):
|
|
self.dbstate.db.connect('person-add', self.log_person_add)
|
|
self.dbstate.db.connect('person-delete', self.log_person_delete)
|
|
self.dbstate.db.connect('person-update', self.log_person_update)
|
|
self.dbstate.db.connect('family-add', self.log_family_add)
|
|
self.dbstate.db.connect('family-delete', self.log_family_delete)
|
|
self.dbstate.db.connect('family-update', self.log_family_update)
|
|
|
|
def on_load(self):
|
|
if len(self.gui.data) > 0:
|
|
self.show_duplicates = self.gui.data[0]
|
|
|
|
def on_save(self):
|
|
self.gui.data = [self.show_duplicates]
|
|
|
|
def active_changed(self, handle):
|
|
self.log_active_changed(handle)
|
|
|
|
# FIXME: added support for family display and clicks
|
|
def log_person_add(self, handles):
|
|
self.get_person(handles, _("Added"))
|
|
def log_person_delete(self, handles):
|
|
self.get_person(handles, _("Deleted"))
|
|
def log_person_update(self, handles):
|
|
self.get_person(handles, _("Updated"))
|
|
def log_family_add(self, handles):
|
|
self.append_text(_("Added") + ": family" )
|
|
def log_family_delete(self, handles):
|
|
self.append_text(_("Deleted") + ": family" )
|
|
def log_family_update(self, handles):
|
|
self.append_text(_("Updated") + ": family" )
|
|
def log_active_changed(self, handles):
|
|
self.get_person([handles], "Selected")
|
|
|
|
def get_person(self, handles, ltype):
|
|
for person_handle in handles:
|
|
if ((self.show_duplicates == "no" and
|
|
ltype + ": " + person_handle not in self.history) or
|
|
self.show_duplicates == "yes"):
|
|
self.append_text("%s: " % ltype)
|
|
self.history[ltype + ": " + person_handle] = 1
|
|
person = self.dbstate.db.get_person_from_handle(person_handle)
|
|
if person:
|
|
self.link(name_displayer.display(person), 'Person',
|
|
person_handle)
|
|
else:
|
|
self.link(_("Unknown"), 'Person', person_handle)
|
|
self.append_text("\n")
|
|
|
|
class TopSurnamesGramplet(Gramplet):
|
|
def init(self):
|
|
self.tooltip = _("Double-click surname for details")
|
|
self.top_size = 10 # will be overwritten in load
|
|
self.set_text(_("No Family Tree loaded."))
|
|
|
|
def db_changed(self):
|
|
self.dbstate.db.connect('person-add', self.update)
|
|
self.dbstate.db.connect('person-delete', self.update)
|
|
self.dbstate.db.connect('person-update', self.update)
|
|
|
|
def on_load(self):
|
|
if len(self.gui.data) > 0:
|
|
self.top_size = int(self.gui.data[0])
|
|
|
|
def on_save(self):
|
|
self.gui.data = [self.top_size]
|
|
|
|
def main(self):
|
|
self.set_text(_("Processing...") + "\n")
|
|
people = self.dbstate.db.get_person_handles(sort_handles=False)
|
|
surnames = {}
|
|
representative_handle = {}
|
|
cnt = 0
|
|
for person_handle in people:
|
|
person = self.dbstate.db.get_person_from_handle(person_handle)
|
|
if person:
|
|
surname = person.get_primary_name().get_surname().strip()
|
|
surnames[surname] = surnames.get(surname, 0) + 1
|
|
representative_handle[surname] = person_handle
|
|
if cnt % 350 == 0:
|
|
yield True
|
|
cnt += 1
|
|
total_people = cnt
|
|
surname_sort = []
|
|
total = 0
|
|
cnt = 0
|
|
for surname in surnames:
|
|
surname_sort.append( (surnames[surname], surname) )
|
|
total += surnames[surname]
|
|
if cnt % 350 == 0:
|
|
yield True
|
|
cnt += 1
|
|
total_surnames = cnt
|
|
surname_sort.sort(lambda a,b: -cmp(a,b))
|
|
line = 0
|
|
### All done!
|
|
self.set_text("")
|
|
for (count, surname) in surname_sort:
|
|
self.append_text(" %d. " % (line + 1))
|
|
self.link(surname, 'Surname', representative_handle[surname])
|
|
self.append_text(", %d%% (%d)\n" %
|
|
(int((float(count)/total) * 100), count))
|
|
line += 1
|
|
if line >= self.top_size:
|
|
break
|
|
self.append_text(("\n" + _("Total unique surnames") + ": %d\n") %
|
|
total_surnames)
|
|
self.append_text((_("Total people") + ": %d") % total_people)
|
|
|
|
class StatsGramplet(Gramplet):
|
|
def init(self):
|
|
self.set_text(_("No Family Tree loaded."))
|
|
|
|
def db_changed(self):
|
|
self.dbstate.db.connect('person-add', self.update)
|
|
self.dbstate.db.connect('person-delete', self.update)
|
|
self.dbstate.db.connect('family-add', self.update)
|
|
self.dbstate.db.connect('family-delete', self.update)
|
|
|
|
def main(self):
|
|
self.set_text(_("Processing..."))
|
|
database = self.dbstate.db
|
|
personList = database.get_person_handles(sort_handles=False)
|
|
familyList = database.get_family_handles()
|
|
|
|
with_photos = 0
|
|
total_photos = 0
|
|
incomp_names = 0
|
|
disconnected = 0
|
|
missing_bday = 0
|
|
males = 0
|
|
females = 0
|
|
unknowns = 0
|
|
bytes = 0
|
|
namelist = []
|
|
notfound = []
|
|
|
|
pobjects = len(database.get_media_object_handles())
|
|
for photo_id in database.get_media_object_handles():
|
|
photo = database.get_object_from_handle(photo_id)
|
|
try:
|
|
bytes = bytes + posixpath.getsize(photo.get_path())
|
|
except:
|
|
notfound.append(photo.get_path())
|
|
|
|
cnt = 0
|
|
for person_handle in personList:
|
|
person = database.get_person_from_handle(person_handle)
|
|
if not person:
|
|
continue
|
|
length = len(person.get_media_list())
|
|
if length > 0:
|
|
with_photos = with_photos + 1
|
|
total_photos = total_photos + length
|
|
|
|
person = database.get_person_from_handle(person_handle)
|
|
name = person.get_primary_name()
|
|
if name.get_first_name() == "" or name.get_surname() == "":
|
|
incomp_names = incomp_names + 1
|
|
if ((not person.get_main_parents_family_handle()) and
|
|
(not len(person.get_family_handle_list()))):
|
|
disconnected = disconnected + 1
|
|
birth_ref = person.get_birth_ref()
|
|
if birth_ref:
|
|
birth = database.get_event_from_handle(birth_ref.ref)
|
|
if not DateHandler.get_date(birth):
|
|
missing_bday = missing_bday + 1
|
|
else:
|
|
missing_bday = missing_bday + 1
|
|
if person.get_gender() == gen.lib.Person.FEMALE:
|
|
females = females + 1
|
|
elif person.get_gender() == gen.lib.Person.MALE:
|
|
males = males + 1
|
|
else:
|
|
unknowns += 1
|
|
if name.get_surname() not in namelist:
|
|
namelist.append(name.get_surname())
|
|
if cnt % 200 == 0:
|
|
yield True
|
|
cnt += 1
|
|
|
|
text = _("Individuals") + "\n"
|
|
text = text + "----------------------------\n"
|
|
text = text + "%s: %d\n" % (_("Number of individuals"),len(personList))
|
|
text = text + "%s: %d\n" % (_("Males"),males)
|
|
text = text + "%s: %d\n" % (_("Females"),females)
|
|
text = text + "%s: %d\n" % (_("Individuals with unknown gender"),unknowns)
|
|
text = text + "%s: %d\n" % (_("Individuals with incomplete names"),incomp_names)
|
|
text = text + "%s: %d\n" % (_("Individuals missing birth dates"),missing_bday)
|
|
text = text + "%s: %d\n" % (_("Disconnected individuals"),disconnected)
|
|
text = text + "\n%s\n" % _("Family Information")
|
|
text = text + "----------------------------\n"
|
|
text = text + "%s: %d\n" % (_("Number of families"),len(familyList))
|
|
text = text + "%s: %d\n" % (_("Unique surnames"),len(namelist))
|
|
text = text + "\n%s\n" % _("Media Objects")
|
|
text = text + "----------------------------\n"
|
|
text = text + "%s: %d\n" % (_("Individuals with media objects"),with_photos)
|
|
text = text + "%s: %d\n" % (_("Total number of media object references"),total_photos)
|
|
text = text + "%s: %d\n" % (_("Number of unique media objects"),pobjects)
|
|
text = text + "%s: %d %s\n" % (_("Total size of media objects"),bytes,\
|
|
_("bytes"))
|
|
|
|
if len(notfound) > 0:
|
|
text = text + "\n%s\n" % _("Missing Media Objects")
|
|
text = text + "----------------------------\n"
|
|
for p in notfound:
|
|
text = text + "%s\n" % p
|
|
self.set_text(text)
|
|
|
|
class PythonGramplet(Gramplet):
|
|
def init(self):
|
|
self.tooltip = _("Enter Python expressions")
|
|
self.env = {"dbstate": self.gui.dbstate,
|
|
"uistate": self.gui.uistate,
|
|
"self": self,
|
|
"Date": gen.lib.Date,
|
|
}
|
|
# GUI setup:
|
|
self.gui.textview.set_editable(True)
|
|
self.set_text("Python %s\n> " % sys.version)
|
|
self.gui.textview.connect('key-press-event', self.on_key_press)
|
|
|
|
def format_exception(self, max_tb_level=10):
|
|
retval = ''
|
|
cla, exc, trbk = sys.exc_info()
|
|
retval += _("Error") + (" : %s %s" %(cla, exc))
|
|
return retval
|
|
|
|
def on_key_press(self, widget, event):
|
|
import gtk
|
|
if (event.keyval == gtk.keysyms.Home or
|
|
((event.keyval == gtk.keysyms.a and
|
|
event.get_state() & gtk.gdk.CONTROL_MASK))):
|
|
buffer = widget.get_buffer()
|
|
cursor_pos = buffer.get_property("cursor-position")
|
|
iter = buffer.get_iter_at_offset(cursor_pos)
|
|
line_cnt = iter.get_line()
|
|
start = buffer.get_iter_at_line(line_cnt)
|
|
start.forward_chars(2)
|
|
buffer.place_cursor(start)
|
|
return True
|
|
elif (event.keyval == gtk.keysyms.End or
|
|
(event.keyval == gtk.keysyms.e and
|
|
event.get_state() & gtk.gdk.CONTROL_MASK)):
|
|
buffer = widget.get_buffer()
|
|
end = buffer.get_end_iter()
|
|
buffer.place_cursor(end)
|
|
return True
|
|
elif event.keyval == gtk.keysyms.Return:
|
|
echo = False
|
|
buffer = widget.get_buffer()
|
|
cursor_pos = buffer.get_property("cursor-position")
|
|
iter = buffer.get_iter_at_offset(cursor_pos)
|
|
line_cnt = iter.get_line()
|
|
start = buffer.get_iter_at_line(line_cnt)
|
|
line_len = iter.get_chars_in_line()
|
|
buffer_cnt = buffer.get_line_count()
|
|
if (buffer_cnt - line_cnt) > 1:
|
|
line_len -= 1
|
|
echo = True
|
|
end = buffer.get_iter_at_line_offset(line_cnt, line_len)
|
|
line = buffer.get_text(start, end)
|
|
self.append_text("\n")
|
|
if line.startswith(">"):
|
|
line = line[1:].strip()
|
|
else:
|
|
self.append_text("> ")
|
|
return True
|
|
if echo:
|
|
self.append_text("> " + line)
|
|
end = buffer.get_end_iter()
|
|
buffer.place_cursor(end)
|
|
return True
|
|
# update states, in case of change:
|
|
self.env["dbstate"] = self.gui.dbstate
|
|
self.env["uistate"] = self.gui.uistate
|
|
_retval = None
|
|
if "_retval" in self.env:
|
|
del self.env["_retval"]
|
|
exp1 = """_retval = """ + line
|
|
exp2 = line.strip()
|
|
try:
|
|
_retval = eval(exp2, self.env)
|
|
except:
|
|
try:
|
|
exec exp1 in self.env
|
|
except:
|
|
try:
|
|
exec exp2 in self.env
|
|
except:
|
|
_retval = self.format_exception()
|
|
if "_retval" in self.env:
|
|
_retval = self.env["_retval"]
|
|
if _retval != None:
|
|
self.append_text("%s" % str(_retval))
|
|
self.append_text("\n> ")
|
|
end = buffer.get_end_iter()
|
|
buffer.place_cursor(end)
|
|
return True
|
|
return False
|
|
|
|
class TODOGramplet(Gramplet):
|
|
def init(self):
|
|
# GUI setup:
|
|
self.tooltip = _("Enter text")
|
|
self.gui.textview.set_editable(True)
|
|
self.append_text(_("Enter your TODO list here."))
|
|
|
|
def on_load(self):
|
|
self.load_data_to_text()
|
|
|
|
def on_save(self):
|
|
self.gui.data = [] # clear out old data
|
|
self.save_text_to_data()
|
|
|
|
def make_welcome_content(gui):
|
|
text = _(
|
|
'Welcome to GRAMPS!\n\n'
|
|
'GRAMPS is a software package designed for genealogical research.'
|
|
' Although similar to other genealogical programs, GRAMPS offers '
|
|
'some unique and powerful features.\n\n'
|
|
'GRAMPS is an Open Source Software package, which means you are '
|
|
'free to make copies and distribute it to anyone you like. It\'s '
|
|
'developed and maintained by a worldwide team of volunteers whose'
|
|
' goal is to make GRAMPS powerful, yet easy to use.\n\n'
|
|
'Getting Started\n\n'
|
|
'The first thing you must do is to create a new Family Tree. To '
|
|
'create a new Family Tree (sometimes called a database) select '
|
|
'"Family Trees" from the menu, pick "Manage Family Trees", press '
|
|
'"New" and name your database. For more details, please read the '
|
|
'User Manual, or the on-line manual at http://gramps-project.org.\n\n'
|
|
'You are currently reading from the "Gramplets" page, where you can'
|
|
' add your own gramplets.\n\n'
|
|
'You can right-click on the background of this page to add additional'
|
|
' gramplets and change the number of columns. You can also drag the '
|
|
'Properties button to reposition the gramplet on this page, and detach'
|
|
' the gramplet to float above GRAMPS. If you close GRAMPS with a gramplet'
|
|
' detached, it will re-opened detached the next time you start '
|
|
'GRAMPS.'
|
|
)
|
|
gui.set_text(text)
|
|
|
|
|
|
class NewsGramplet(Gramplet):
|
|
URL = "http://www.gramps-project.org/wiki/index.php?title=%s&action=raw"
|
|
|
|
def init(self):
|
|
self.tooltip = _("Read news from the GRAMPS wiki")
|
|
|
|
def main(self):
|
|
continuation = self.process('News')
|
|
retval = True
|
|
while retval:
|
|
retval, text = continuation.next()
|
|
self.cleanup(text)
|
|
yield True
|
|
self.cleanup(text)
|
|
yield False
|
|
|
|
def cleanup(self, text):
|
|
# final text
|
|
text = text.replace("<BR>", "\n")
|
|
while "\n\n\n" in text:
|
|
text = text.replace("\n\n\n", "\n\n")
|
|
text = text.strip()
|
|
self.set_text(text)
|
|
|
|
def process(self, title):
|
|
#print "processing '%s'..." % title
|
|
title = title.replace(" ", "_")
|
|
yield True, (_("Reading") + " '%s'..." % title)
|
|
fp = urllib.urlopen(self.URL % title)
|
|
text = fp.read()
|
|
#text = text.replace("\n", " ")
|
|
html = re.findall('<.*?>', text)
|
|
for exp in html:
|
|
text = text.replace(exp, "")
|
|
text = text.replace("\n", "<BR>")
|
|
fp.close()
|
|
pattern = '{{.*?}}'
|
|
matches = re.findall(pattern, text)
|
|
#print " before:", text
|
|
for match in matches:
|
|
page = match[2:-2]
|
|
oldtext = match
|
|
if "|" in page:
|
|
template, heading, body = page.split("|", 2)
|
|
if template.lower() == "release":
|
|
newtext = "GRAMPS " + heading + " released.<BR><BR>"
|
|
else:
|
|
newtext = heading + "<BR><BR>"
|
|
newtext += body + "<BR>"
|
|
text = text.replace(oldtext, newtext)
|
|
else: # a macro/redirect
|
|
continuation = self.process("Template:" + page)
|
|
retval = True
|
|
while retval:
|
|
retval, newtext = continuation.next()
|
|
yield True, newtext
|
|
text = text.replace(oldtext, newtext)
|
|
#print " after:", text
|
|
pattern = '#REDIRECT \[\[.*?\]\]'
|
|
matches = re.findall(pattern, text)
|
|
#print " before:", text
|
|
for match in matches:
|
|
page = match[12:-2]
|
|
oldtext = match
|
|
continuation = self.process(page)
|
|
retval = True
|
|
while retval:
|
|
retval, newtext = continuation.next()
|
|
yield True, newtext
|
|
text = text.replace(oldtext, newtext)
|
|
#print " after:", text
|
|
yield False, text
|
|
|
|
class AgeOnDateGramplet(Gramplet):
|
|
def init(self):
|
|
import gtk
|
|
# GUI setup:
|
|
self.tooltip = _("Enter a date, click Run")
|
|
vbox = gtk.VBox()
|
|
hbox = gtk.HBox()
|
|
# label, entry
|
|
description = gtk.TextView()
|
|
description.set_wrap_mode(gtk.WRAP_WORD)
|
|
buffer = description.get_buffer()
|
|
buffer.set_text(_("Enter a date in the entry below and click Run."
|
|
" This will compute the ages for everyone in your"
|
|
" Family Tree on that date. You can then sort by"
|
|
" the age column, and double-click the row to view"
|
|
" or edit."))
|
|
label = gtk.Label()
|
|
label.set_text(_("Date") + ":")
|
|
self.entry = gtk.Entry()
|
|
button = gtk.Button(_("Run"))
|
|
button.connect("clicked", self.run)
|
|
hbox.pack_start(label, False)
|
|
hbox.pack_start(self.entry, True)
|
|
vbox.pack_start(description, True)
|
|
vbox.pack_start(hbox, False)
|
|
vbox.pack_start(button, False)
|
|
self.gui.scrolledwindow.remove(self.gui.textview)
|
|
self.gui.scrolledwindow.add_with_viewport(vbox)
|
|
vbox.show_all()
|
|
|
|
def run(self, obj):
|
|
text = self.entry.get_text()
|
|
date = DateHandler.parser.parse(text)
|
|
run_quick_report_by_name(self.gui.dbstate,
|
|
self.gui.uistate,
|
|
'ageondate',
|
|
date)
|
|
|
|
|
|
register(type="gramplet",
|
|
name= "Top Surnames Gramplet",
|
|
tname=_("Top Surnames Gramplet"),
|
|
height=230,
|
|
content = TopSurnamesGramplet,
|
|
title=_("Top Surnames"),
|
|
)
|
|
|
|
register(type="gramplet",
|
|
name="Statistics Gramplet",
|
|
tname=_("Statistics Gramplet"),
|
|
height=230,
|
|
expand=True,
|
|
content = StatsGramplet,
|
|
title=_("Statistics"),
|
|
)
|
|
|
|
register(type="gramplet",
|
|
name="Session Log Gramplet",
|
|
tname=_("Session Log Gramplet"),
|
|
height=230,
|
|
data=['no'],
|
|
content = LogGramplet,
|
|
title=_("Session Log"),
|
|
)
|
|
|
|
register(type="gramplet",
|
|
name="Python Gramplet",
|
|
tname=_("Python Gramplet"),
|
|
height=250,
|
|
content = PythonGramplet,
|
|
title=_("Python Shell"),
|
|
)
|
|
|
|
register(type="gramplet",
|
|
name="TODO Gramplet",
|
|
tname=_("TODO Gramplet"),
|
|
height=300,
|
|
expand=True,
|
|
content = TODOGramplet,
|
|
title=_("TODO List"),
|
|
)
|
|
|
|
register(type="gramplet",
|
|
name="Welcome Gramplet",
|
|
tname=_("Welcome Gramplet"),
|
|
height=300,
|
|
expand=True,
|
|
content = make_welcome_content,
|
|
title=_("Welcome to GRAMPS!"),
|
|
)
|
|
|
|
register(type="gramplet",
|
|
name="Calendar Gramplet",
|
|
tname=_("Calendar Gramplet"),
|
|
height=200,
|
|
content = CalendarGramplet,
|
|
title=_("Calendar"),
|
|
)
|
|
|
|
register(type="gramplet",
|
|
name="News Gramplet",
|
|
tname=_("News Gramplet"),
|
|
height=300,
|
|
expand=True,
|
|
content = NewsGramplet,
|
|
title=_("News"),
|
|
)
|
|
|
|
register(type="gramplet",
|
|
name="Age on Date Gramplet",
|
|
tname=_("Age on Date Gramplet"),
|
|
height=200,
|
|
content = AgeOnDateGramplet,
|
|
title=_("Age on Date"),
|
|
)
|
|
|