Implement Gramps bottombar

svn: r16360
This commit is contained in:
Nick Hall 2011-01-11 01:21:38 +00:00
parent 78cc4c3000
commit e44dfa291e
13 changed files with 1100 additions and 4 deletions

View File

@ -80,6 +80,7 @@ src/gen/plug/report/utils.py
src/gen/proxy/private.py
# gui - GUI code
src/gui/bottombar.py
src/gui/columnorder.py
src/gui/configure.py
src/gui/dbloader.py
@ -255,12 +256,16 @@ src/plugins/export/ExportXml.py
src/plugins/gramplet/AgeOnDateGramplet.py
src/plugins/gramplet/AgeStats.py
src/plugins/gramplet/AttributesGramplet.py
src/plugins/gramplet/bottombar.gpr.py
src/plugins/gramplet/CalendarGramplet.py
src/plugins/gramplet/DescendGramplet.py
src/plugins/gramplet/FanChartGramplet.py
src/plugins/gramplet/GivenNameGramplet.py
src/plugins/gramplet/gramplet.gpr.py
src/plugins/gramplet/PedigreeGramplet.py
src/plugins/gramplet/PersonAttributes.py
src/plugins/gramplet/PersonDetails.py
src/plugins/gramplet/PersonResidence.py
src/plugins/gramplet/QuickViewGramplet.py
src/plugins/gramplet/RelativeGramplet.py
src/plugins/gramplet/SessionLogGramplet.py

View File

@ -296,6 +296,7 @@ src/plugins/gramplet/DeepConnections.py
src/plugins/gramplet/FaqGramplet.py
src/plugins/gramplet/HeadlineNewsGramplet.py
src/plugins/gramplet/NoteGramplet.py
src/plugins/gramplet/PersonGallery.py
src/plugins/gramplet/PluginManagerGramplet.py
src/plugins/gramplet/PythonGramplet.py

View File

@ -15,6 +15,7 @@ pkgdatadir = $(datadir)/@PACKAGE@/gui
pkgdata_PYTHON = \
__init__.py \
basesidebar.py \
bottombar.py \
columnorder.py \
configure.py \
dbguielement.py \

421
src/gui/bottombar.py Normal file
View File

@ -0,0 +1,421 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2011 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$
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
from gen.ggettext import gettext as _
#-------------------------------------------------------------------------
#
# GNOME modules
#
#-------------------------------------------------------------------------
import gtk
import time
import os
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
import ConfigParser
import const
from gui.widgets.grampletpane import (AVAILABLE_GRAMPLETS,
GET_AVAILABLE_GRAMPLETS,
get_gramplet_opts,
get_gramplet_options_by_tname,
get_gramplet_options_by_name,
make_requested_gramplet)
from ListModel import ListModel, NOSORT
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
NL = "\n"
#-------------------------------------------------------------------------
#
# Bottombar class
#
#-------------------------------------------------------------------------
class Bottombar(object):
"""
A class which defines the graphical representation of the Gramps bottom bar.
"""
def __init__(self, dbstate, uistate, configfile, close_callback, defaults):
self.dbstate = dbstate
self.uistate = uistate
self.configfile = os.path.join(const.VERSION_DIR, "%s.ini" % configfile)
self.close_callback = close_callback
self.gramplet_map = {} # title->gramplet
self.top = gtk.HBox()
self.notebook = gtk.Notebook()
self.notebook.set_show_border(False)
self.notebook.set_scrollable(True)
self.notebook.connect('switch_page', self.__switch_page)
vbox = gtk.VBox()
close_button = gtk.Button()
img = gtk.image_new_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
close_button.set_image(img)
close_button.set_relief(gtk.RELIEF_NONE)
close_button.connect('clicked', self.__close_clicked)
vbox.pack_start(close_button, False)
delete_button = gtk.Button()
img = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
delete_button.set_image(img)
delete_button.set_relief(gtk.RELIEF_NONE)
delete_button.connect('clicked', self.__delete_clicked)
vbox.pack_end(delete_button, False)
add_button = gtk.Button()
img = gtk.image_new_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
add_button.set_image(img)
add_button.set_relief(gtk.RELIEF_NONE)
add_button.connect('clicked', self.__add_clicked)
vbox.pack_end(add_button, False)
self.top.pack_start(self.notebook, True)
self.top.pack_start(vbox, False)
vbox.show_all()
self.notebook.show()
self.default_gramplets = defaults
config_settings = self.load_gramplets()
for (name, opts) in config_settings[1]:
all_opts = get_gramplet_opts(name, opts)
gramplet = make_requested_gramplet(self, name, all_opts,
self.dbstate, self.uistate)
self.gramplet_map[all_opts["title"]] = gramplet
gramplets = [g for g in self.gramplet_map.itervalues()
if g is not None]
gramplets.sort(key=lambda x: x.page)
for gramplet in gramplets:
gramplet.scrolledwindow.set_size_request(-1, 200)
self.notebook.append_page(gramplet.mainframe,
gtk.Label(gramplet.title))
self.notebook.set_tab_reorderable(gramplet.mainframe, True)
if config_settings[0][0]:
self.top.show()
self.notebook.set_current_page(config_settings[0][1])
def load_gramplets(self):
"""
Load the gramplets from the configuration file.
"""
retval = []
visible = False
default_page = 0
filename = self.configfile
if filename and os.path.exists(filename):
cp = ConfigParser.ConfigParser()
cp.read(filename)
for sec in cp.sections():
if sec == "Bar Options":
if "visible" in cp.options(sec):
visible = cp.get(sec, "visible") == "True"
if "page" in cp.options(sec):
default_page = int(cp.get(sec, "page"))
else:
data = {"title": sec}
for opt in cp.options(sec):
if opt.startswith("data["):
temp = data.get("data", {})
#temp.append(cp.get(sec, opt).strip())
pos = int(opt[5:-1])
temp[pos] = cp.get(sec, opt).strip()
data["data"] = temp
else:
data[opt] = cp.get(sec, opt).strip()
if "data" in data:
data["data"] = [data["data"][key]
for key in sorted(data["data"].keys())]
if "name" not in data:
data["name"] = "Unnamed Gramplet"
data["tname"]= _("Unnamed Gramplet")
retval.append((data["name"], data)) # name, opts
else:
# give defaults as currently known
for name in self.default_gramplets:
if name in AVAILABLE_GRAMPLETS():
retval.append((name, GET_AVAILABLE_GRAMPLETS(name)))
return ((visible, default_page), retval)
def save(self):
"""
Save the gramplet configuration.
"""
if len(self.gramplet_map) == 0:
return # something is the matter
filename = self.configfile
try:
fp = open(filename, "w")
except:
print "Failed writing '%s'; gramplets not saved" % filename
return
fp.write(";; Gramps bar configuration file" + NL)
fp.write((";; Automatically created at %s" %
time.strftime("%Y/%m/%d %H:%M:%S")) + NL + NL)
fp.write("[Bar Options]" + NL)
fp.write(("visible=%s" + NL) % self.top.get_property('visible'))
fp.write(("page=%d" + NL) % self.notebook.get_current_page())
fp.write(NL)
for page_num in range(self.notebook.get_n_pages()):
title = get_title(self.notebook, page_num)
gramplet = self.gramplet_map[title]
opts = get_gramplet_options_by_name(gramplet.name)
if opts is not None:
base_opts = opts.copy()
for key in base_opts:
if key in gramplet.__dict__:
base_opts[key] = gramplet.__dict__[key]
fp.write(("[%s]" + NL) % gramplet.title)
for key in base_opts:
if key == "content": continue
elif key == "title": continue
elif key == "row": continue
elif key == "column": continue
elif key == "page": continue
elif key == "version": continue # code, don't save
elif key == "gramps": continue # code, don't save
elif key == "data":
if not isinstance(base_opts["data"], (list, tuple)):
fp.write(("data[0]=%s" + NL) % base_opts["data"])
else:
cnt = 0
for item in base_opts["data"]:
fp.write(("data[%d]=%s" + NL) % (cnt, item))
cnt += 1
else:
fp.write(("%s=%s" + NL)% (key, base_opts[key]))
fp.write(("page=%d" + NL) % page_num)
fp.write(NL)
fp.close()
def set_active(self):
"""
Called with the view is set as active.
"""
page = self.notebook.get_current_page()
title = get_title(self.notebook, page)
if self.gramplet_map[title].pui:
self.gramplet_map[title].pui.active = True
if self.gramplet_map[title].pui.dirty:
self.gramplet_map[title].pui.update()
def set_inactive(self):
"""
Called with the view is set as inactive.
"""
page = self.notebook.get_current_page()
title = get_title(self.notebook, page)
if self.gramplet_map[title].pui:
if self.gramplet_map[title].state != "detached":
self.gramplet_map[title].pui.active = False
def on_delete(self):
"""
Called when the view is closed.
"""
gramplets = (g for g in self.gramplet_map.itervalues()
if g is not None)
for gramplet in gramplets:
# this is the only place where the gui runs user code directly
if gramplet.pui:
gramplet.pui.on_save()
self.save()
def get_display(self):
"""
Return the top container widget for the GUI.
"""
return self.top
def show(self):
"""
Show the bottom bar.
"""
return self.top.show()
def hide(self):
"""
Hide the bottom bar.
"""
return self.top.hide()
def __close_clicked(self, button):
"""
Called when the sidebar is closed.
"""
self.close_callback()
def __add_clicked(self, button):
"""
Called when the add button is clicked.
"""
names = [GET_AVAILABLE_GRAMPLETS(key)["tname"] for key
in AVAILABLE_GRAMPLETS()]
skip = [gramplet.tname for gramplet in self.gramplet_map.values()]
gramplet_list = [name for name in names if name not in skip]
gramplet_list.sort()
dialog = ChooseGrampletDialog(_("Select Gramplet"), gramplet_list)
tname = dialog.run()
if not tname:
return
all_opts = get_gramplet_options_by_tname(tname)
gramplet = make_requested_gramplet(self, all_opts["name"], all_opts,
self.dbstate, self.uistate)
if not gramplet:
print "Problem creating ", tname
return
title = all_opts["title"]
self.gramplet_map[title] = gramplet
gramplet.scrolledwindow.set_size_request(-1, gramplet.height)
page_num = self.notebook.append_page(gramplet.mainframe,
gtk.Label(title))
self.notebook.set_tab_reorderable(gramplet.mainframe, True)
self.notebook.set_current_page(page_num)
def __delete_clicked(self, button):
"""
Called when the delete button is clicked.
"""
page_num = self.notebook.get_current_page()
title = get_title(self.notebook, page_num)
del self.gramplet_map[title]
self.notebook.remove_page(page_num)
def __switch_page(self, notebook, unused, new_page):
"""
Called when the user has switched to a new bottombar page.
"""
old_page = notebook.get_current_page()
#print "switch from", old_page, "to", new_page
if old_page >= 0:
title = get_title(notebook, old_page)
if self.gramplet_map[title].pui:
if self.gramplet_map[title].state != "detached":
self.gramplet_map[title].pui.active = False
title = get_title(notebook, new_page)
if self.gramplet_map[title].pui:
self.gramplet_map[title].pui.active = True
if self.gramplet_map[title].pui.dirty:
self.gramplet_map[title].pui.update()
def get_title(notebook, page_num):
"""
Reurn the title of a given page in a notebook.
"""
return notebook.get_tab_label_text(notebook.get_nth_page(page_num))
#-------------------------------------------------------------------------
#
# Choose Gramplet Dialog
#
#-------------------------------------------------------------------------
class ChooseGrampletDialog(object):
"""
A dialog to choose a gramplet
"""
def __init__(self, title, names):
self.title = title
self.names = names
self.namelist = None
self.namemodel = None
self.top = self._create_dialog()
def run(self):
"""
Run the dialog and return the result.
"""
self._populate_model()
response = self.top.run()
result = None
if response == gtk.RESPONSE_OK:
store, iter_ = self.namemodel.get_selected()
if iter_:
result = store.get_value(iter_, 0)
self.top.destroy()
return result
def _populate_model(self):
"""
Populate the model.
"""
self.namemodel.clear()
for name in self.names:
self.namemodel.add([name])
def _create_dialog(self):
"""
Create a dialog box to organize tags.
"""
# pylint: disable-msg=E1101
title = _("%(title)s - Gramps") % {'title': self.title}
top = gtk.Dialog(title)
top.set_default_size(400, 350)
top.set_modal(True)
top.set_has_separator(False)
top.vbox.set_spacing(5)
label = gtk.Label('<span size="larger" weight="bold">%s</span>'
% self.title)
label.set_use_markup(True)
top.vbox.pack_start(label, 0, 0, 5)
box = gtk.HBox()
top.vbox.pack_start(box, 1, 1, 5)
name_titles = [(_('Name'), NOSORT, 200)]
self.namelist = gtk.TreeView()
self.namemodel = ListModel(self.namelist, name_titles)
slist = gtk.ScrolledWindow()
slist.add_with_viewport(self.namelist)
slist.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
box.pack_start(slist, 1, 1, 5)
bbox = gtk.VButtonBox()
bbox.set_layout(gtk.BUTTONBOX_START)
bbox.set_spacing(6)
top.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
top.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
box.pack_start(bbox, 0, 0, 5)
top.show_all()
return top

View File

@ -50,6 +50,7 @@ import Errors
from gui.dbguielement import DbGUIElement
from gui.widgets.menutoolbuttonaction import MenuToolButtonAction
from gui.sidebar import Sidebar
from gui.bottombar import Bottombar
from gui.widgets.grampletpane import GrampletPane
from gui.configure import ConfigureDialog
from config import config
@ -116,6 +117,7 @@ class PageView(DbGUIElement):
<menu action="ViewMenu">
<placeholder name="Bars">
<menuitem action="Sidebar"/>
<menuitem action="Bottombar"/>
</placeholder>
</menu>
</menubar>
@ -150,6 +152,7 @@ class PageView(DbGUIElement):
self.top = None
self.gramplet_pane = None
self.sidebar = None
self.bottombar = None
DbGUIElement.__init__(self, dbstate.db)
@ -159,16 +162,22 @@ class PageView(DbGUIElement):
Returns a gtk container widget.
"""
self.sidebar = Sidebar(self.sidebar_changed, self.sidebar_closed)
defaults = self.get_default_gramplets()[1]
self.bottombar = Bottombar(self.dbstate, self.uistate,
self.ident + "_bottombar",
self.bottombar_closed,
defaults)
hpane = gtk.HPaned()
vpane = gtk.VPaned()
hpane.pack1(vpane, resize=True, shrink=True)
hpane.pack1(vpane, resize=True, shrink=False)
hpane.pack2(self.sidebar.get_display(), resize=False, shrink=False)
hpane.show()
vpane.show()
widget = self.build_widget()
widget.show_all()
vpane.add1(widget)
vpane.pack1(widget, resize=True, shrink=False)
vpane.pack2(self.bottombar.get_display(), resize=False, shrink=False)
initial_page = self._config.get('sidebar.page')
self.gramplet_pane = self.__create_gramplet_pane()
@ -205,9 +214,11 @@ class PageView(DbGUIElement):
"""
Create a gramplet pane.
"""
defaults = self.get_default_gramplets()[0]
gramplet_pane = GrampletPane(self.ident + "_sidebar",
self, self.dbstate, self.uistate,
column_count=1)
column_count=1,
default_gramplets=defaults)
gramplet_pane.show_all()
self.sidebar.add(_('Gramplets'), gramplet_pane, GRAMPLET_PAGE)
return gramplet_pane
@ -232,6 +243,17 @@ class PageView(DbGUIElement):
self.sidebar_changed(None, False, None)
self._config.set('sidebar.visible', active)
def __bottombar_toggled(self, action):
"""
Called when the bottombar is toggled.
"""
active = action.get_active()
if active:
self.bottombar.show()
else:
self.bottombar.hide()
self._config.set('bottombar.visible', active)
def sidebar_changed(self, page_type, active, index):
"""
Called when the sidebar page is changed.
@ -246,6 +268,23 @@ class PageView(DbGUIElement):
uimanager = self.uistate.uimanager
uimanager.get_action('/MenuBar/ViewMenu/Bars/Sidebar').activate()
def bottombar_closed(self):
"""
Called when the bottombar close button is clicked.
"""
uimanager = self.uistate.uimanager
uimanager.get_action('/MenuBar/ViewMenu/Bars/Bottombar').activate()
def get_default_gramplets(self):
"""
Get the default gramplets for the Gramps sidebar and bottombar.
Returns a 2-tuple. The first element is a tuple of sidebar gramplets
and the second element is a tuple of bottombar gramplets.
Views should override this method to define default gramplets.
"""
return ((), ())
def key_press_handler(self, widget, event):
"""
A general keypress handler. Override if you want to handle
@ -337,6 +376,7 @@ class PageView(DbGUIElement):
then we rebuild the data.
"""
self.gramplet_pane.set_active()
self.bottombar.set_active()
self.active = True
if self.dirty:
self.uistate.set_busy_cursor(True)
@ -348,6 +388,7 @@ class PageView(DbGUIElement):
Marks page as being inactive (not currently displayed)
"""
self.gramplet_pane.set_inactive()
self.bottombar.set_inactive()
self.active = False
def build_tree(self):
@ -443,6 +484,9 @@ class PageView(DbGUIElement):
self._add_toggle_action('Sidebar', None, _('_Sidebar'),
None, None, self.__sidebar_toggled,
self._config.get('sidebar.visible'))
self._add_toggle_action('Bottombar', None, _('_Bottombar'),
None, None, self.__bottombar_toggled,
self._config.get('bottombar.visible'))
self._add_action("AddGramplet", gtk.STOCK_ADD, _("Add a gramplet"))
self._add_action("RestoreGramplet", None, _("Restore a gramplet"))
@ -562,6 +606,7 @@ class PageView(DbGUIElement):
that should be called when quiting the main application.
"""
self.gramplet_pane.on_delete()
self.bottombar.on_delete()
self._config.save()
def init_config(self):
@ -584,8 +629,10 @@ class PageView(DbGUIElement):
use_config_path=True)
for section, value in self.CONFIGSETTINGS:
self._config.register(section, value)
self._config.register('sidebar.visible', True)
self._config.register('sidebar.visible', False)
self._config.register('sidebar.page', 0)
self._config.register('bottombar.visible', False)
self._config.register('bottombar.page', 0)
self._config.init()
self.config_connect()

View File

@ -332,6 +332,7 @@ class GuiGramplet(object):
self.detached_height = int(kwargs.get("detached_height", 300))
self.detached_width = int(kwargs.get("detached_width", 400))
self.row = int(kwargs.get("row", -1))
self.page = int(kwargs.get("page", -1))
self.state = kwargs.get("state", "maximized")
self.data = kwargs.get("data", [])
self.help_url = kwargs.get("help_url", WIKI_HELP_PAGE)
@ -360,12 +361,16 @@ class GuiGramplet(object):
self.titlelabel.get_children()[0].set_use_markup(True)
self.titlelabel.connect("clicked", self.edit_title)
self.titlelabel_entry = None
self.titlelabel.hide()
self.gvclose = self.xml.get_object('gvclose')
self.gvclose.connect('clicked', self.close)
self.gvclose.hide()
self.gvstate = self.xml.get_object('gvstate')
self.gvstate.connect('clicked', self.change_state)
self.gvstate.hide()
self.gvproperties = self.xml.get_object('gvproperties')
self.gvproperties.connect('clicked', self.set_properties)
self.gvproperties.hide()
self.xml.get_object('gvcloseimage').set_from_stock(gtk.STOCK_CLOSE,
gtk.ICON_SIZE_MENU)
self.xml.get_object('gvstateimage').set_from_stock(gtk.STOCK_REMOVE,

View File

@ -9,6 +9,7 @@ pkgdata_PYTHON = \
AgeOnDateGramplet.py \
AgeStats.py \
AttributesGramplet.py \
bottombar.gpr.py \
CalendarGramplet.py \
DescendGramplet.py \
FanChartGramplet.py \
@ -16,6 +17,10 @@ pkgdata_PYTHON = \
GivenNameGramplet.py \
gramplet.gpr.py \
PedigreeGramplet.py \
PersonAttributes.py \
PersonDetails.py \
PersonGallery.py \
PersonResidence.py \
PluginManagerGramplet.py\
QuickViewGramplet.py \
RelativeGramplet.py \

View File

@ -0,0 +1,74 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2011 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$
#
from ListModel import ListModel, NOSORT
from QuickReports import run_quick_report_by_name
from gen.plug import Gramplet
import gtk
class PersonAttributes(Gramplet):
"""
Displays the attributes of a person.
"""
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.
"""
top = gtk.TreeView()
titles = [(_('Key'), 1, 100),
(_('Value'), 2, 100)]
self.model = ListModel(top, titles, event_func=self.display_report)
return top
def db_changed(self):
self.dbstate.db.connect('person-update', self.update)
self.update()
def active_changed(self, handle):
self.update()
def main(self): # return false finishes
active_handle = self.get_active('Person')
active_person = self.dbstate.db.get_person_from_handle(active_handle)
if not active_person:
return
self.model.clear()
for attr in active_person.get_attribute_list():
self.model.add((attr.get_type(), attr.get_value()))
def display_report(self, treeview):
"""
Display the quick report for matching attribute key.
"""
model, iter_ = treeview.get_selection().get_selected()
if iter_:
key = model.get_value(iter_, 0)
run_quick_report_by_name(self.dbstate,
self.uistate,
'attribute_match',
key)

View File

@ -0,0 +1,232 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2011 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$
#
from gen.lib import EventType, EventRoleType
from gen.plug import Gramplet
from gen.display.name import displayer as name_displayer
from gen.ggettext import gettext as _
import DateHandler
import Utils
import gtk
import pango
class PersonDetails(Gramplet):
"""
Displays details for a person.
"""
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.load_obj = None
self.load_rect = None
top = gtk.HBox()
vbox = gtk.VBox()
self.obj_photo = gtk.Image()
self.name = gtk.Label()
self.name.set_alignment(0, 0)
self.name.modify_font(pango.FontDescription('sans bold 12'))
vbox.pack_start(self.name, fill=True, expand=False, padding=10)
table = gtk.Table(2, 2)
self.father = self.make_row(table, 0, _('Father'))
self.mother = self.make_row(table, 1, _('Mother'))
vbox.pack_start(table, fill=True, expand=False, padding=5)
table = gtk.Table(4, 2)
self.birth = self.make_row(table, 0, _('Birth'))
self.baptism = self.make_row(table, 1, _('Baptism'))
self.death = self.make_row(table, 2, _('Death'))
self.burial = self.make_row(table, 3, _('Burial'))
vbox.pack_start(table, fill=True, expand=False, padding=5)
table = gtk.Table(1, 2)
self.occupation = self.make_row(table, 0, _('Occupation'))
vbox.pack_start(table, fill=True, expand=False, padding=5)
vbox.show_all()
top.pack_start(self.obj_photo, fill=True, expand=False, padding=5)
top.pack_start(vbox, fill=True, expand=True, padding=10)
return top
def make_row(self, table, row, title):
"""
Make a row in a table.
"""
label = gtk.Label(title + ':')
label.set_alignment(1, 0)
widget = gtk.Label()
widget.set_alignment(0, 0)
table.attach(label, 0, 1, row, row + 1, xoptions=gtk.FILL, xpadding=10)
table.attach(widget, 1, 2, row, row + 1)
return (label, widget)
def db_changed(self):
self.dbstate.db.connect('person-update', self.update)
self.update()
def active_changed(self, handle):
self.update()
def main(self): # return false finishes
active_handle = self.get_active('Person')
active_person = self.dbstate.db.get_person_from_handle(active_handle)
if not active_person:
return
self.load_person_image(active_person)
self.name.set_text(name_displayer.display(active_person))
self.display_parents(active_person)
self.display_type(active_person, self.birth, EventType.BIRTH)
self.display_type(active_person, self.baptism, EventType.BAPTISM)
self.display_type(active_person, self.death, EventType.DEATH)
self.display_type(active_person, self.burial, EventType.BURIAL)
occupation_text = self.get_attribute(active_person, 'Occupation')
self.occupation[1].set_text(occupation_text)
def display_parents(self, active_person):
"""
Display the parents of the active person.
"""
family_handle = active_person.get_main_parents_family_handle()
if family_handle:
family = self.dbstate.db.get_family_from_handle(family_handle)
handle = family.get_father_handle()
if handle:
father = self.dbstate.db.get_person_from_handle(handle)
self.father[1].set_text(name_displayer.display(father))
else:
self.father[1].set_text(_('Unknown'))
handle = family.get_mother_handle()
if handle:
mother = self.dbstate.db.get_person_from_handle(handle)
self.mother[1].set_text(name_displayer.display(mother))
else:
self.mother[1].set_text(_('Unknown'))
else:
self.father[1].set_text(_('Unknown'))
self.mother[1].set_text(_('Unknown'))
def get_attribute(self, person, attr_key):
"""
Return an attribute with the given key.
"""
for attr in person.get_attribute_list():
if attr.get_type() == attr_key:
return attr.get_value()
return _('Unknown')
def display_type(self, active_person, widget, event_type):
"""
Display an event type row.
"""
event = self.get_event(active_person, event_type)
if event:
widget[1].set_text(self.format_event(event))
widget[0].show()
widget[1].show()
else:
widget[1].set_text('')
widget[0].hide()
widget[1].hide()
def get_event(self, person, event_type):
"""
Return an event of the given type.
"""
for event_ref in person.get_event_ref_list():
if int(event_ref.get_role()) == EventRoleType.PRIMARY:
event = self.dbstate.db.get_event_from_handle(event_ref.ref)
if int(event.get_type()) == event_type:
return event
return None
def format_event(self, event):
"""
Format the event for display.
"""
date = DateHandler.get_date(event)
handle = event.get_place_handle()
if handle:
place = self.dbstate.db.get_place_from_handle(handle).get_title()
retval = _('%s - %s.') % (date, place)
else:
retval = _('%s.') % date
return retval
def load_person_image(self, person):
"""
Load the primary image if it exists.
"""
media_list = person.get_media_list()
if media_list:
photo = media_list[0]
object_handle = photo.get_reference_handle()
obj = self.dbstate.db.get_object_from_handle(object_handle)
full_path = Utils.media_path_full(self.dbstate.db, obj.get_path())
#reload if different media, or different rectangle
if self.load_obj != full_path or \
self.load_rect != photo.get_rectangle():
mime_type = obj.get_mime_type()
if mime_type and mime_type.startswith("image"):
self.load_photo(full_path, photo.get_rectangle())
else:
self.load_photo(None)
else:
self.load_photo(None)
def load_photo(self, path, rectangle=None):
"""
Load, scale and display the person's main photo from the path.
"""
self.load_obj = path
self.load_rect = rectangle
if path is None:
self.obj_photo.hide()
else:
try:
i = gtk.gdk.pixbuf_new_from_file(path)
width = i.get_width()
height = i.get_height()
if rectangle is not None:
upper_x = min(rectangle[0], rectangle[2])/100.
lower_x = max(rectangle[0], rectangle[2])/100.
upper_y = min(rectangle[1], rectangle[3])/100.
lower_y = max(rectangle[1], rectangle[3])/100.
sub_x = int(upper_x * width)
sub_y = int(upper_y * height)
sub_width = int((lower_x - upper_x) * width)
sub_height = int((lower_y - upper_y) * height)
if sub_width > 0 and sub_height > 0:
i = i.subpixbuf(sub_x, sub_y, sub_width, sub_height)
ratio = float(max(i.get_height(), i.get_width()))
scale = float(190.0)/ratio
x = int(scale*(i.get_width()))
y = int(scale*(i.get_height()))
i = i.scale_simple(x, y, gtk.gdk.INTERP_BILINEAR)
self.obj_photo.set_from_pixbuf(i)
self.obj_photo.show()
except:
self.obj_photo.hide()

View File

@ -0,0 +1,125 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2011 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$
#
from gui.utils import open_file_with_default_application
from gen.plug import Gramplet
import Utils
import gtk
class PersonGallery(Gramplet):
"""
Displays details for a person.
"""
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.image_list = []
self.top = gtk.HBox(False, 3)
return self.top
def db_changed(self):
self.dbstate.db.connect('person-update', self.update)
self.update()
def active_changed(self, handle):
self.update()
def main(self): # return false finishes
active_handle = self.get_active('Person')
active_person = self.dbstate.db.get_person_from_handle(active_handle)
if not active_person:
return
self.clear_images()
self.load_person_images(active_person)
def clear_images(self):
"""
Remove all images from the Gramplet.
"""
for image in self.image_list:
self.top.remove(image)
self.image_list = []
def load_person_images(self, person):
"""
Load the primary image into the main form if it exists.
"""
media_list = person.get_media_list()
for photo in media_list:
object_handle = photo.get_reference_handle()
obj = self.dbstate.db.get_object_from_handle(object_handle)
full_path = Utils.media_path_full(self.dbstate.db, obj.get_path())
mime_type = obj.get_mime_type()
if mime_type and mime_type.startswith("image"):
pb = self.get_pixbuf(full_path, photo.get_rectangle())
eb = gtk.EventBox()
eb.connect('button-press-event', self.display_image, full_path)
image = gtk.Image()
eb.add(image)
self.image_list.append(eb)
image.set_from_pixbuf(pb)
eb.show_all()
self.top.pack_start(eb, expand=False, fill=False)
def display_image(self, widget, event, path):
"""
Display the image with the default application.
"""
if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1:
open_file_with_default_application(path)
def get_pixbuf(self, path, rectangle=None):
"""
Load, scale and display the person's main photo from the path.
"""
try:
i = gtk.gdk.pixbuf_new_from_file(path)
width = i.get_width()
height = i.get_height()
if rectangle is not None:
upper_x = min(rectangle[0], rectangle[2])/100.
lower_x = max(rectangle[0], rectangle[2])/100.
upper_y = min(rectangle[1], rectangle[3])/100.
lower_y = max(rectangle[1], rectangle[3])/100.
sub_x = int(upper_x * width)
sub_y = int(upper_y * height)
sub_width = int((lower_x - upper_x) * width)
sub_height = int((lower_y - upper_y) * height)
if sub_width > 0 and sub_height > 0:
i = i.subpixbuf(sub_x, sub_y, sub_width, sub_height)
ratio = float(max(i.get_height(), i.get_width()))
scale = float(180.0)/ratio
x = int(scale*(i.get_width()))
y = int(scale*(i.get_height()))
i = i.scale_simple(x, y, gtk.gdk.INTERP_BILINEAR)
return i
except:
return None

View File

@ -0,0 +1,92 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2011 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$
#
from gen.lib import EventType, EventRoleType
from gui.editors import EditEvent
from ListModel import ListModel, NOSORT
from gen.plug import Gramplet
import DateHandler
import gtk
class PersonResidence(Gramplet):
"""
Displays residence events for a person.
"""
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.
"""
top = gtk.TreeView()
titles = [('', NOSORT, 50,),
(_('Date'), 1, 200),
(_('Place'), 2, 200)]
self.model = ListModel(top, titles, event_func=self.edit_event)
return top
def db_changed(self):
self.dbstate.db.connect('person-update', self.update)
self.update()
def active_changed(self, handle):
self.update()
def main(self): # return false finishes
active_handle = self.get_active('Person')
active_person = self.dbstate.db.get_person_from_handle(active_handle)
if not active_person:
return
self.model.clear()
for event_ref in active_person.get_event_ref_list():
if int(event_ref.get_role()) == EventRoleType.PRIMARY:
event = self.dbstate.db.get_event_from_handle(event_ref.ref)
if int(event.get_type()) == EventType.RESIDENCE:
self.add_residence(event)
def add_residence(self, event):
"""
Add a residence event to the model.
"""
date = DateHandler.get_date(event)
place = ''
handle = event.get_place_handle()
if handle:
place = self.dbstate.db.get_place_from_handle(handle).get_title()
self.model.add((event.get_handle(), date, place))
def edit_event(self, treeview):
"""
Edit the selected event.
"""
model, iter_ = treeview.get_selection().get_selected()
if iter_:
handle = model.get_value(iter_, 0)
try:
event = self.dbstate.db.get_event_from_handle(handle)
EditEvent(self.dbstate, self.uistate, [], event)
except Errors.WindowActiveError:
pass

View File

@ -0,0 +1,78 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2011 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$
#------------------------------------------------------------------------
#
# Register Gramplet
#
#------------------------------------------------------------------------
register(GRAMPLET,
id="Person Details Gramplet",
name=_("Person Details Gramplet"),
description = _("Gramplet showing details of a person"),
version="1.0.0",
gramps_target_version="3.3",
status = STABLE,
fname="PersonDetails.py",
height=200,
gramplet = 'PersonDetails',
gramplet_title=_("Details"),
)
register(GRAMPLET,
id="Person Gallery Gramplet",
name=_("Person Gallery Gramplet"),
description = _("Gramplet showing media objects for a person"),
version="1.0.0",
gramps_target_version="3.3",
status = STABLE,
fname="PersonGallery.py",
height=200,
gramplet = 'PersonGallery',
gramplet_title=_("Gallery"),
)
register(GRAMPLET,
id="Person Residence Gramplet",
name=_("Person Residence Gramplet"),
description = _("Gramplet showing residence events for a person"),
version="1.0.0",
gramps_target_version="3.3",
status = STABLE,
fname="PersonResidence.py",
height=200,
gramplet = 'PersonResidence',
gramplet_title=_("Residence"),
)
register(GRAMPLET,
id="Person Attributes Gramplet",
name=_("Person Attributes Gramplet"),
description = _("Gramplet showing the attributes of a person"),
version="1.0.0",
gramps_target_version="3.3",
status = STABLE,
fname="PersonAttributes.py",
height=200,
gramplet = 'PersonAttributes',
gramplet_title=_("Attributes"),
)

View File

@ -440,3 +440,13 @@ class BasePersonView(ListView):
person = self.dbstate.db.get_person_from_handle(person_handle)
person.add_tag(tag_handle)
self.dbstate.db.commit_person(person, transaction)
def get_default_gramplets(self):
"""
Define the default gramplets for the sidebar and bottombar.
"""
return (("Welcome Gramplet",),
("Person Details Gramplet",
"Person Gallery Gramplet",
"Person Residence Gramplet",
"Person Attributes Gramplet"))