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 src/gen/proxy/private.py
# gui - GUI code # gui - GUI code
src/gui/bottombar.py
src/gui/columnorder.py src/gui/columnorder.py
src/gui/configure.py src/gui/configure.py
src/gui/dbloader.py src/gui/dbloader.py
@ -255,12 +256,16 @@ src/plugins/export/ExportXml.py
src/plugins/gramplet/AgeOnDateGramplet.py src/plugins/gramplet/AgeOnDateGramplet.py
src/plugins/gramplet/AgeStats.py src/plugins/gramplet/AgeStats.py
src/plugins/gramplet/AttributesGramplet.py src/plugins/gramplet/AttributesGramplet.py
src/plugins/gramplet/bottombar.gpr.py
src/plugins/gramplet/CalendarGramplet.py src/plugins/gramplet/CalendarGramplet.py
src/plugins/gramplet/DescendGramplet.py src/plugins/gramplet/DescendGramplet.py
src/plugins/gramplet/FanChartGramplet.py src/plugins/gramplet/FanChartGramplet.py
src/plugins/gramplet/GivenNameGramplet.py src/plugins/gramplet/GivenNameGramplet.py
src/plugins/gramplet/gramplet.gpr.py src/plugins/gramplet/gramplet.gpr.py
src/plugins/gramplet/PedigreeGramplet.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/QuickViewGramplet.py
src/plugins/gramplet/RelativeGramplet.py src/plugins/gramplet/RelativeGramplet.py
src/plugins/gramplet/SessionLogGramplet.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/FaqGramplet.py
src/plugins/gramplet/HeadlineNewsGramplet.py src/plugins/gramplet/HeadlineNewsGramplet.py
src/plugins/gramplet/NoteGramplet.py src/plugins/gramplet/NoteGramplet.py
src/plugins/gramplet/PersonGallery.py
src/plugins/gramplet/PluginManagerGramplet.py src/plugins/gramplet/PluginManagerGramplet.py
src/plugins/gramplet/PythonGramplet.py src/plugins/gramplet/PythonGramplet.py

View File

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

View File

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

View File

@ -9,6 +9,7 @@ pkgdata_PYTHON = \
AgeOnDateGramplet.py \ AgeOnDateGramplet.py \
AgeStats.py \ AgeStats.py \
AttributesGramplet.py \ AttributesGramplet.py \
bottombar.gpr.py \
CalendarGramplet.py \ CalendarGramplet.py \
DescendGramplet.py \ DescendGramplet.py \
FanChartGramplet.py \ FanChartGramplet.py \
@ -16,6 +17,10 @@ pkgdata_PYTHON = \
GivenNameGramplet.py \ GivenNameGramplet.py \
gramplet.gpr.py \ gramplet.gpr.py \
PedigreeGramplet.py \ PedigreeGramplet.py \
PersonAttributes.py \
PersonDetails.py \
PersonGallery.py \
PersonResidence.py \
PluginManagerGramplet.py\ PluginManagerGramplet.py\
QuickViewGramplet.py \ QuickViewGramplet.py \
RelativeGramplet.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 = self.dbstate.db.get_person_from_handle(person_handle)
person.add_tag(tag_handle) person.add_tag(tag_handle)
self.dbstate.db.commit_person(person, transaction) 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"))