Use the new view plugin structure to offer a flat list person view

svn: r14195
This commit is contained in:
Benny Malengier 2010-02-03 13:01:04 +00:00
parent d97775d07c
commit 967f99bf7b
12 changed files with 244 additions and 154 deletions

View File

@ -446,6 +446,7 @@ src/plugins/lib/libhtmlconst.py
src/plugins/lib/libmapservice.py
src/plugins/lib/libnarrate.py
src/plugins/lib/libodfbackend.py
src/plugins/lib/libpersonview.py
src/plugins/lib/libplugins.gpr.py
src/plugins/lib/libtranslate.py
@ -546,7 +547,8 @@ src/plugins/view/noteview.py
src/plugins/view/pedigreeview.py
src/plugins/view/pedigreeviewext.py
src/plugins/view/pedigreeviewext.gpr.py
src/plugins/view/personview.py
src/plugins/view/personlistview.py
src/plugins/view/persontreeview.py
src/plugins/view/placeview.py
src/plugins/view/placetreeview.gpr.py
src/plugins/view/placetreeview.py

View File

@ -34,7 +34,7 @@ import gtk
# gramps modules
#
#-------------------------------------------------------------------------
from gui.views.treemodels import PeopleModel
from gui.views.treemodels import PeopleBaseModel, PersonTreeModel
from baseselector import BaseSelector
#-------------------------------------------------------------------------
@ -67,7 +67,7 @@ class SelectPerson(BaseSelector):
return _("Select Person")
def get_model_class(self):
return PeopleModel
return PersonTreeModel
def get_column_titles(self):
return [
@ -86,7 +86,7 @@ class SelectPerson(BaseSelector):
return self.db.get_person_from_handle
def get_handle_column(self):
return PeopleModel.COLUMN_INT_ID
return PeopleBaseModel.COLUMN_INT_ID
def exact_search(self):
"""

View File

@ -23,7 +23,7 @@
Package init for the treemodels package.
"""
from peoplemodel import PeopleModel
from peoplemodel import PeopleBaseModel, PersonListModel, PersonTreeModel
from familymodel import FamilyModel
from eventmodel import EventModel
from sourcemodel import SourceModel

View File

@ -392,6 +392,7 @@ class FlatBaseModel(gtk.GenericTreeModel):
self.sort_func = self.smap[scol]
self.sort_col = scol
self.skip = skip
self._in_build = False
self.node_map = FlatNodeMap()
self.set_search(search)
@ -494,6 +495,15 @@ class FlatBaseModel(gtk.GenericTreeModel):
"""
return None
def clear_cache(self, handle=None):
"""
If you use a cache, overwrite here so it is cleared when this
method is called (on rebuild)
:param handle: if None, clear entire cache, otherwise clear the handle
entry if present
"""
pass
def sort_keys(self):
"""
Return the (sort_key, handle) list of all data that can maximally
@ -513,6 +523,8 @@ class FlatBaseModel(gtk.GenericTreeModel):
""" function called when view must be build, given a search text
in the top search bar
"""
self.clear_cache()
self._in_build = True
if self.db.is_open():
allkeys = self.node_map.full_srtkey_hndl_map()
if not allkeys:
@ -534,11 +546,14 @@ class FlatBaseModel(gtk.GenericTreeModel):
reverse=self._reverse)
else:
self.node_map.clear_map()
self._in_build = False
def _rebuild_filter(self, ignore=None):
""" function called when view must be build, given filter options
in the filter sidebar
"""
self.clear_cache()
self._in_build = True
if self.db.is_open():
allkeys = self.node_map.full_srtkey_hndl_map()
if not allkeys:
@ -561,6 +576,7 @@ class FlatBaseModel(gtk.GenericTreeModel):
reverse=self._reverse)
else:
self.node_map.clear_map()
self._in_build = False
def add_row_by_handle(self, handle):
"""
@ -584,6 +600,7 @@ class FlatBaseModel(gtk.GenericTreeModel):
"""
Delete a row, called after the object with handle is deleted
"""
self.clear_cache(handle)
data = self.map(handle)
delete_val = (conv_unicode_tosrtkey_ongtk(self.sort_func(data)), handle)
delete_path = self.node_map.delete(delete_val)
@ -595,6 +612,7 @@ class FlatBaseModel(gtk.GenericTreeModel):
"""
Update a row, called after the object with handle is changed
"""
self.clear_cache(handle)
oldsortkey = self.node_map.get_sortkey(handle)
newsortkey = conv_unicode_tosrtkey_ongtk(self.sort_func(self.map(
handle)))

View File

@ -32,7 +32,6 @@ TreeModel for the GRAMPS Person tree.
# Standard python modules
#
#-------------------------------------------------------------------------
from __future__ import with_statement
from gen.ggettext import gettext as _
import time
import cgi
@ -64,6 +63,7 @@ import DateHandler
import ToolTips
import GrampsLocale
from Lru import LRU
from gui.views.treemodels.flatbasemodel import FlatBaseModel
from gui.views.treemodels.treebasemodel import TreeBaseModel
import config
@ -86,34 +86,26 @@ invalid_date_format = config.get('preferences.invalid-date-format')
#-------------------------------------------------------------------------
#
# PeopleModel
# PeopleBaseModel
#
#-------------------------------------------------------------------------
class PeopleModel(TreeBaseModel):
class PeopleBaseModel(object):
"""
Basic GenericTreeModel interface to handle the Tree interface for
the PersonView
Basic Model interface to handle the PersonViews
"""
_GENDER = [ _(u'female'), _(u'male'), _(u'unknown') ]
# The following is accessed from PersonView - CHECK
COLUMN_INT_ID = 12 # dynamic calculation of column indices
# LRU cache size
_CACHE_SIZE = 250
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
skip=set(), sort_map=None):
def __init__(self, db):
"""
Initialize the model building the initial data
"""
TreeBaseModel.__init__(self, db, search=search, skip=skip,
tooltip_column=11, marker_column=10,
scol=scol, order=order, sort_map=sort_map)
def _set_base_data(self):
"""See TreeBaseModel, we also set some extra lru caches
"""
self.gen_cursor = self.db.get_person_cursor
self.number_items = self.db.get_number_of_people
self.map = self.db.get_raw_person_data
self.gen_cursor = db.get_person_cursor
self.map = db.get_raw_person_data
self.fmap = [
self.column_name,
@ -145,59 +137,71 @@ class PeopleModel(TreeBaseModel):
self.column_tooltip,
self.column_int_id,
]
self.hmap = [self.column_header] + [None]*len(self.smap)
self.lru_name = LRU(TreeBaseModel._CACHE_SIZE)
self.lru_bdate = LRU(TreeBaseModel._CACHE_SIZE)
self.lru_ddate = LRU(TreeBaseModel._CACHE_SIZE)
#columns are accessed on every mouse over, so it is worthwhile to
#cache columns visible in one screen to avoid expensive database
#lookup of derived values
self.lru_name = LRU(PeopleBaseModel._CACHE_SIZE)
self.lru_spouse = LRU(PeopleBaseModel._CACHE_SIZE)
self.lru_bdate = LRU(PeopleBaseModel._CACHE_SIZE)
self.lru_ddate = LRU(PeopleBaseModel._CACHE_SIZE)
def clear_cache(self):
def clear_local_cache(self, handle=None):
""" Clear the LRU cache """
TreeBaseModel.clear_cache(self)
self.lru_name.clear()
self.lru_bdate.clear()
self.lru_ddate.clear()
if handle:
try:
del self.lru_name[handle]
except KeyError:
pass
try:
del self.lru_spouse[handle]
except KeyError:
pass
try:
del self.lru_bdate[handle]
except KeyError:
pass
try:
del self.lru_ddate[handle]
except KeyError:
pass
else:
self.lru_name.clear()
self.lru_spouse.clear()
self.lru_bdate.clear()
self.lru_ddate.clear()
def on_get_n_columns(self):
""" Return the number of columns in the model """
return len(self.fmap)+1
def get_tree_levels(self):
"""
Return the headings of the levels in the hierarchy.
"""
return ['Group As', 'Name']
def add_row(self, handle, data):
"""
Add nodes to the node map for a single person.
handle The handle of the gramps object.
data The object data.
"""
ngn = name_displayer.name_grouping_data
nsn = name_displayer.raw_sorted_name
name_data = data[COLUMN_NAME]
group_name = ngn(self.db, name_data)
sort_key = self.sort_func(data)
#if group_name not in self.group_list:
#self.group_list.append(group_name)
#self.add_node(None, group_name, group_name, None)
# add as node: parent, child, sortkey, handle; parent and child are
# nodes in the treebasemodel, and will be used as iters
self.add_node(group_name, handle, sort_key, handle)
def sort_name(self, data):
n = Name()
n.unserialize(data[COLUMN_NAME])
return name_displayer.sort_string(n)
def column_spouse(self, data):
spouses_names = u""
def column_name(self, data):
handle = data[0]
if handle in self.lru_name:
name = self.lru_name[handle]
else:
name = name_displayer.raw_sorted_name(data[COLUMN_NAME])
if not self._in_build:
self.lru_name[handle] = name
return name
def column_spouse(self, data):
handle = data[0]
if handle in self.lru_spouse:
value = self.lru_spouse[handle]
else:
value = self._get_spouse_data(data)
if not self._in_build:
self.lru_spouse[handle] = value
return value
def _get_spouse_data(self, data):
spouses_names = u""
for family_handle in data[COLUMN_FAMILY]:
family = self.db.get_family_from_handle(family_handle)
for spouse_id in [family.get_father_handle(),
@ -212,16 +216,6 @@ class PeopleModel(TreeBaseModel):
spouses_names += name_displayer.display(spouse)
return spouses_names
def column_name(self, data):
handle = data[0]
if handle in self.lru_name:
name = self.lru_name[handle]
else:
name = name_displayer.raw_sorted_name(data[COLUMN_NAME])
if not self._in_build:
self.lru_name[handle] = name
return name
def column_id(self, data):
return data[COLUMN_ID]
@ -232,7 +226,7 @@ class PeopleModel(TreeBaseModel):
GrampsLocale.codeset)
def column_gender(self, data):
return PeopleModel._GENDER[data[COLUMN_GENDER]]
return PeopleBaseModel._GENDER[data[COLUMN_GENDER]]
def column_birth_day(self, data):
handle = data[0]
@ -445,9 +439,80 @@ class PeopleModel(TreeBaseModel):
def column_int_id(self, data):
return data[0]
class PersonListModel(PeopleBaseModel, FlatBaseModel):
"""
Listed people model.
"""
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
skip=set(), sort_map=None):
PeopleBaseModel.__init__(self, db)
FlatBaseModel.__init__(self, db, search=search, skip=skip,
tooltip_column=11,
scol=scol, order=order, sort_map=sort_map)
def clear_cache(self, handle=None):
""" Clear the LRU cache """
PeopleBaseModel.clear_local_cache(self, handle)
def marker_column(self):
"""
Return the column for marker colour.
"""
return 10
class PersonTreeModel(PeopleBaseModel, TreeBaseModel):
"""
Hierarchical people model.
"""
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
skip=set(), sort_map=None):
PeopleBaseModel.__init__(self, db)
TreeBaseModel.__init__(self, db, 11, search=search, skip=skip,
marker_column=10,
scol=scol, order=order, sort_map=sort_map)
def _set_base_data(self):
"""See TreeBaseModel, we also set some extra lru caches
"""
self.number_items = self.db.get_number_of_people
self.hmap = [self.column_header] + [None]*len(self.smap)
def clear_cache(self, handle=None):
""" Clear the LRU cache
overwrite of base methods
"""
TreeBaseModel.clear_cache(self, handle)
PeopleBaseModel.clear_local_cache(self, handle)
def get_tree_levels(self):
"""
Return the headings of the levels in the hierarchy.
"""
return ['Group As', 'Name']
def column_header(self, node):
return node.name
def column_header_view(self, node):
return True
def add_row(self, handle, data):
"""
Add nodes to the node map for a single person.
handle The handle of the gramps object.
data The object data.
"""
ngn = name_displayer.name_grouping_data
nsn = name_displayer.raw_sorted_name
name_data = data[COLUMN_NAME]
group_name = ngn(self.db, name_data)
sort_key = self.sort_func(data)
#if group_name not in self.group_list:
#self.group_list.append(group_name)
#self.add_node(None, group_name, group_name, None)
# add as node: parent, child, sortkey, handle; parent and child are
# nodes in the treebasemodel, and will be used as iters
self.add_node(group_name, handle, sort_key, handle)

View File

@ -386,11 +386,17 @@ class TreeBaseModel(gtk.GenericTreeModel):
"""
return self._marker_column
def clear_cache(self):
def clear_cache(self, handle=None):
"""
Clear the LRU cache.
"""
self.lru_data.clear()
if handle:
try:
del self.lru_data[handle]
except KeyError:
pass
else:
self.lru_data.clear()
def clear(self):
"""
@ -671,7 +677,7 @@ class TreeBaseModel(gtk.GenericTreeModel):
Delete a row from the model.
"""
cput = time.clock()
self.clear_cache()
self.clear_cache(handle)
node = self.get_node(handle)
parent = self.nodemap.node(node.parent)

View File

@ -18,6 +18,7 @@ pkgdata_PYTHON = \
libmapservice.py\
libmixin.py\
libodfbackend.py\
libpersonview.py\
libplugins.gpr.py\
libtranslate.py

View File

@ -3,6 +3,7 @@
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2008 Gary Burton
# Copyright (C) 2009 Nick Hall
# Copyright (C) 2010 Benny Malengier
#
# 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
@ -16,13 +17,13 @@
#
# 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
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 021111307 USA
#
# $Id$
"""
Provide the person view.
Provide the base for a list person view.
"""
#-------------------------------------------------------------------------
@ -46,8 +47,7 @@ _LOG = logging.getLogger(".gui.personview")
#
#-------------------------------------------------------------------------
import gen.lib
from gui.views.listview import ListView, LISTTREE
from gui.views.treemodels import PeopleModel
from gui.views.listview import ListView
import Utils
from gen.display.name import displayer as name_displayer
from QuestionDialog import ErrorDialog, QuestionDialog
@ -71,9 +71,9 @@ from gen.ggettext import sgettext as _
# PersonView
#
#-------------------------------------------------------------------------
class PersonView(ListView):
class BasePersonView(ListView):
"""
PersonView class, derived from the ListView, a treeview
Base view for PersonView listviews ListView, a treeview
"""
COL_NAME = 0
COL_ID = 1
@ -109,7 +109,7 @@ class PersonView(ListView):
FILTER_TYPE = "Person"
QR_CATEGORY = CATEGORY_QR_PERSON
def __init__(self, dbstate, uistate, nav_group=0):
def __init__(self, dbstate, uistate, title, model, nav_group=0):
"""
Create the Person View
"""
@ -121,10 +121,9 @@ class PersonView(ListView):
}
ListView.__init__(
self, _('People'), dbstate, uistate,
PersonView.COLUMN_NAMES, len(PersonView.COLUMN_NAMES),
PeopleModel,
signal_map, dbstate.db.get_bookmarks(),
self, title, dbstate, uistate,
BasePersonView.COLUMN_NAMES, len(BasePersonView.COLUMN_NAMES),
model, signal_map, dbstate.db.get_bookmarks(),
Bookmarks.PersonBookmarks, nav_group,
multiple=True,
filter_class=PersonSidebarFilter,
@ -137,12 +136,6 @@ class PersonView(ListView):
config.connect("interface.filter", self.filter_toggle)
def type_list(self):
"""
set the listtype, this governs eg keybinding
"""
return LISTTREE
def navigation_type(self):
return 'Person'
@ -168,19 +161,14 @@ class PersonView(ListView):
"""
Returns a tuple indicating columns requiring an exact search
"""
return (PersonView.COL_GEN,) # Gender ('female' contains the string 'male')
return (BasePersonView.COL_GEN,) # Gender ('female' contains the string 'male')
def get_stock(self):
"""
Use the gramps-person stock icon
Use the grampsperson stock icon
"""
return 'gramps-person'
def get_viewtype_stock(self):
"""Type of view in category
"""
return 'gramps-tree-group'
def ui_definition(self):
"""
Defines the UI string for UIManager
@ -238,9 +226,6 @@ class PersonView(ListView):
<menuitem action="Forward"/>
<menuitem action="HomePerson"/>
<separator/>
<menuitem action="OpenAllNodes"/>
<menuitem action="CloseAllNodes"/>
<separator/>
<menuitem action="Add"/>
<menuitem action="Edit"/>
<menuitem action="Remove"/>
@ -261,20 +246,8 @@ class PersonView(ListView):
def add(self, obj):
person = gen.lib.Person()
# attempt to get the current surname
(model, pathlist) = self.selection.get_selected_rows()
name = u""
if len(pathlist) == 1:
path = pathlist[0]
if len(path) == 1:
name = model.on_get_iter(path).name
else:
node = model.on_get_iter(path)
name = model.on_iter_parent(node).name
try:
person.get_primary_name().set_surname(name)
EditPerson(self.dbstate, self.uistate, [], person)
EditPerson(self.dbstate, self.uistate, [], gen.lib.Person())
except Errors.WindowActiveError:
pass
@ -336,7 +309,7 @@ class PersonView(ListView):
path = self.model.on_get_path(node)
(col, row) = path
if row > 0:
self.selection.select_path((col, row-1))
self.selection.select_path((col, row1))
elif row == 0 and self.model.on_get_iter(path):
self.selection.select_path(path)
@ -369,16 +342,13 @@ class PersonView(ListView):
self.all_action.add_actions([
('FilterEdit', None, _('Person Filter Editor'), None, None,
self.filter_editor),
('OpenAllNodes', None, _("Expand all Nodes"), None, None,
self.open_all_nodes),
('Edit', gtk.STOCK_EDIT, _("action|_Edit..."), "<control>Return",
_("Edit the selected person"), self.edit),
('CloseAllNodes', None, _("Collapse all Nodes"), None, None,
self.close_all_nodes),
('QuickReport', None, _("Quick View"), None, None, None),
('Dummy', None, ' ', None, None, self.dummy_report),
])
self.edit_action.add_actions(
[
('Add', gtk.STOCK_ADD, _("_Add..."), "<control>Insert",

View File

@ -238,3 +238,19 @@ fname = 'libtranslate.py',
authors = ["Brian Matherly"],
authors_email = ["brian@gramps-project.org"],
)
#------------------------------------------------------------------------
#
# libpersonview
#
#------------------------------------------------------------------------
register(GENERAL,
id = 'libpersonview',
name = "lib for the person list views",
description = _("Provides the Base needed for the List People views.") ,
version = '1.0',
status = STABLE,
fname = 'libpersonview.py',
authors = ["The Gramps project"],
authors_email = ["http://gramps-project.org"],
)

View File

@ -45,7 +45,8 @@ import gtk
#-------------------------------------------------------------------------
from gen.display.name import displayer as name_displayer
import ManagedWindow
from gui.views.treemodels import PeopleModel
from gui.views.treemodels import PeopleBaseModel, PersonTreeModel
from libpersonview import BasePersonView
import Relationship
from QuestionDialog import ErrorDialog
@ -58,18 +59,7 @@ from glade import Glade
#
#-------------------------------------------------------------------------
column_names = [
_('Name'),
_('ID') ,
_('Gender'),
_('Birth Date'),
_('Birth Place'),
_('Death Date'),
_('Death Place'),
_('Spouse'),
_('Last Change'),
_('Cause of Death'),
]
column_names = BasePersonView.COLUMN_NAMES
#-------------------------------------------------------------------------
#
@ -86,6 +76,21 @@ class RelCalc(Tool.Tool, ManagedWindow.ManagedWindow):
Tool.Tool.__init__(self, dbstate, options_class, name)
ManagedWindow.ManagedWindow.__init__(self,uistate,[],self.__class__)
#set the columns to see
for data in BasePersonView.CONFIGSETTINGS:
if data[0] == 'columns.order':
colord = data[1]
elif data[0] == 'columns.visible':
colvis = data[1]
elif data[0] == 'columns.sizecol':
colsize = data[1]
self.colord = []
for col, size in zip(colord, colsize):
if col in colvis:
self.colord.append((1, col, size))
else:
self.colord.append((0, col, size))
self.dbstate = dbstate
self.relationship = Relationship.get_relationship_calculator()
self.relationship.connect_db_signals(dbstate)
@ -110,23 +115,16 @@ class RelCalc(Tool.Tool, ManagedWindow.ManagedWindow):
self.textbuffer = gtk.TextBuffer()
self.text.set_buffer(self.textbuffer)
self.model = PeopleModel(self.db)
self.model = PersonTreeModel(self.db)
self.tree.set_model(self.model)
self.tree.connect('key-press-event', self._key_press)
self.selection = self.tree.get_selection()
self.selection.set_mode(gtk.SELECTION_SINGLE)
column = gtk.TreeViewColumn(_('Name'), gtk.CellRendererText(),text=0)
column.set_resizable(True)
column.set_min_width(225)
column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
self.tree.append_column(column)
#keep reference of column so garbage collection works
self.columns = [column]
index = 1
for pair in self.db.get_person_column_order():
self.columns = []
for pair in self.colord:
if not pair[0]:
continue
name = column_names[pair[1]]
@ -138,7 +136,6 @@ class RelCalc(Tool.Tool, ManagedWindow.ManagedWindow):
self.tree.append_column(column)
#keep reference of column so garbage collection works
self.columns.append(column)
index += 1
self.sel = self.tree.get_selection()
self.changedkey = self.sel.connect('changed',self.on_apply_clicked)
@ -171,7 +168,7 @@ class RelCalc(Tool.Tool, ManagedWindow.ManagedWindow):
if not node:
return
handle = model.get_value(node, PeopleModel.COLUMN_INT_ID)
handle = model.get_value(node, PeopleBaseModel.COLUMN_INT_ID)
other_person = self.db.get_person_from_handle(handle)
if other_person is None :
self.textbuffer.set_text("")

View File

@ -17,7 +17,8 @@ pkgdata_PYTHON = \
pedigreeview.py \
pedigreeviewext.py \
pedigreeviewext.gpr.py \
personview.py \
personlistview.py \
persontreeview.py \
placetreeview.gpr.py \
placetreeview.py \
placeview.py \

View File

@ -131,15 +131,29 @@ name = _("Person View"),
description = _("The view showing all people in the family tree"),
version = '1.0',
status = STABLE,
fname = 'personview.py',
fname = 'persontreeview.py',
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("People", _("People")),
viewclass = 'PersonView',
viewclass = 'PersonTreeView',
order = START,
)
register(VIEW,
id = 'personlistview',
name = _("Person List View"),
description = _("The view showing all people in the family tree"
" in a flat list"),
version = '1.0',
status = STABLE,
fname = 'personlistview.py',
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("People", _("People")),
viewclass = 'PersonListView',
order = END,
)
register(VIEW,
id = 'placeview',
name = _("Place View"),
description = _("The view showing all the places of the family tree"),