8377: Slow scrolling in Gramps 4.X, on all platforms.
Additions: 1. cache database access for column values 2. cache do_get_path lookups 3. sped up load access on treeviews with no filters 4. use cache in do_get_path from siblings 5. new LRU size of 1k (was 250)
This commit is contained in:
parent
3ed5e74657
commit
2dae5c3a08
@ -269,6 +269,7 @@ register('interface.url-width', 600)
|
|||||||
register('interface.view', True)
|
register('interface.view', True)
|
||||||
register('interface.width', 775)
|
register('interface.width', 775)
|
||||||
register('interface.surname-box-height', 150)
|
register('interface.surname-box-height', 150)
|
||||||
|
register('interface.treemodel-cache-size', 1000)
|
||||||
|
|
||||||
register('paths.recent-export-dir', '')
|
register('paths.recent-export-dir', '')
|
||||||
register('paths.recent-file', '')
|
register('paths.recent-file', '')
|
||||||
|
101
gramps/gui/views/treemodels/basemodel.py
Normal file
101
gramps/gui/views/treemodels/basemodel.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#
|
||||||
|
# Gramps - a GTK+/GNOME based genealogy program
|
||||||
|
#
|
||||||
|
# Copyright (C) 2000-2006 Donald N. Allingham
|
||||||
|
# Copyright (C) 2009 Benny Malengier
|
||||||
|
# Copyright (C) 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
#
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# GRAMPS modules
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
from .lru import LRU
|
||||||
|
from gramps.gen.config import config
|
||||||
|
|
||||||
|
class BaseModel(object):
|
||||||
|
|
||||||
|
# LRU cache size
|
||||||
|
_CACHE_SIZE = config.get('interface.treemodel-cache-size')
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.lru_data = LRU(BaseModel._CACHE_SIZE)
|
||||||
|
self.lru_path = LRU(BaseModel._CACHE_SIZE)
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
"""
|
||||||
|
Destroy the items in memory.
|
||||||
|
"""
|
||||||
|
self.lru_data = None
|
||||||
|
self.lru_path = None
|
||||||
|
|
||||||
|
def clear_cache(self, handle=None):
|
||||||
|
"""
|
||||||
|
Clear the LRU cache. Always clear lru_path, because paths may have
|
||||||
|
changed.
|
||||||
|
"""
|
||||||
|
if handle:
|
||||||
|
if handle in self.lru_data:
|
||||||
|
del self.lru_data[handle]
|
||||||
|
else:
|
||||||
|
self.lru_data.clear()
|
||||||
|
# Invalidates all paths
|
||||||
|
self.lru_path.clear()
|
||||||
|
|
||||||
|
def get_cached_value(self, handle, col):
|
||||||
|
"""
|
||||||
|
Get the value of a "col". col may be a number (position in a model)
|
||||||
|
or a name (special value used by view).
|
||||||
|
"""
|
||||||
|
if handle in self.lru_data and col in self.lru_data[handle]:
|
||||||
|
#print("hit", handle, col)
|
||||||
|
return (True, self.lru_data[handle][col])
|
||||||
|
#print("MISS", handle, col)
|
||||||
|
return (False, None)
|
||||||
|
|
||||||
|
def set_cached_value(self, handle, col, data):
|
||||||
|
"""
|
||||||
|
Set the data associated with handle + col.
|
||||||
|
"""
|
||||||
|
if not self._in_build:
|
||||||
|
if self.lru_data.count > 0:
|
||||||
|
if handle not in self.lru_data:
|
||||||
|
self.lru_data[handle] = {}
|
||||||
|
self.lru_data[handle][col] = data
|
||||||
|
|
||||||
|
## Cached Path's for TreeView:
|
||||||
|
def get_cached_path(self, handle):
|
||||||
|
"""
|
||||||
|
Saves the Gtk iter path.
|
||||||
|
"""
|
||||||
|
if handle in self.lru_path:
|
||||||
|
return (True, self.lru_path[handle])
|
||||||
|
return (False, None)
|
||||||
|
|
||||||
|
def set_cached_path(self, handle, path):
|
||||||
|
"""
|
||||||
|
Set the Gtk iter path value.
|
||||||
|
"""
|
||||||
|
if not self._in_build:
|
||||||
|
self.lru_path[handle] = path
|
||||||
|
|
||||||
|
def clear_path_cache(self):
|
||||||
|
"""
|
||||||
|
Clear path cache for all.
|
||||||
|
"""
|
||||||
|
self.lru_path.clear()
|
@ -135,15 +135,18 @@ class CitationBaseModel(object):
|
|||||||
"""
|
"""
|
||||||
Return the tag color.
|
Return the tag color.
|
||||||
"""
|
"""
|
||||||
|
tag_handle = data[0]
|
||||||
|
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||||
|
if not cached:
|
||||||
tag_color = "#000000000000"
|
tag_color = "#000000000000"
|
||||||
tag_priority = None
|
tag_priority = None
|
||||||
for handle in data[COLUMN_TAGS]:
|
for handle in data[COLUMN_TAGS]:
|
||||||
tag = self.db.get_tag_from_handle(handle)
|
tag = self.db.get_tag_from_handle(handle)
|
||||||
if tag:
|
|
||||||
this_priority = tag.get_priority()
|
this_priority = tag.get_priority()
|
||||||
if tag_priority is None or this_priority < tag_priority:
|
if tag_priority is None or this_priority < tag_priority:
|
||||||
tag_color = tag.get_color()
|
tag_color = tag.get_color()
|
||||||
tag_priority = this_priority
|
tag_priority = this_priority
|
||||||
|
self.set_cached_value(tag_handle, "TAG_COLOR", tag_color)
|
||||||
return tag_color
|
return tag_color
|
||||||
|
|
||||||
def citation_change(self, data):
|
def citation_change(self, data):
|
||||||
@ -157,72 +160,104 @@ class CitationBaseModel(object):
|
|||||||
|
|
||||||
def citation_src_title(self, data):
|
def citation_src_title(self, data):
|
||||||
source_handle = data[COLUMN_SOURCE]
|
source_handle = data[COLUMN_SOURCE]
|
||||||
|
cached, value = self.get_cached_value(source_handle, "SRC_TITLE")
|
||||||
|
if not cached:
|
||||||
try:
|
try:
|
||||||
source = self.db.get_source_from_handle(source_handle)
|
source = self.db.get_source_from_handle(source_handle)
|
||||||
return str(source.get_title())
|
value = str(source.get_title())
|
||||||
except:
|
except:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(source_handle, "SRC_TITLE", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def citation_src_id(self, data):
|
def citation_src_id(self, data):
|
||||||
source_handle = data[COLUMN_SOURCE]
|
source_handle = data[COLUMN_SOURCE]
|
||||||
|
cached, value = self.get_cached_value(source_handle, "SRC_ID")
|
||||||
|
if not cached:
|
||||||
try:
|
try:
|
||||||
source = self.db.get_source_from_handle(source_handle)
|
source = self.db.get_source_from_handle(source_handle)
|
||||||
return str(source.gramps_id)
|
value = str(source.gramps_id)
|
||||||
except:
|
except:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(source_handle, "SRC_ID", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def citation_src_auth(self, data):
|
def citation_src_auth(self, data):
|
||||||
source_handle = data[COLUMN_SOURCE]
|
source_handle = data[COLUMN_SOURCE]
|
||||||
|
cached, value = self.get_cached_value(source_handle, "SRC_AUTH")
|
||||||
|
if not cached:
|
||||||
try:
|
try:
|
||||||
source = self.db.get_source_from_handle(source_handle)
|
source = self.db.get_source_from_handle(source_handle)
|
||||||
return str(source.get_author())
|
value = str(source.get_author())
|
||||||
except:
|
except:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(source_handle, "SRC_AUTH", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def citation_src_abbr(self, data):
|
def citation_src_abbr(self, data):
|
||||||
source_handle = data[COLUMN_SOURCE]
|
source_handle = data[COLUMN_SOURCE]
|
||||||
|
cached, value = self.get_cached_value(source_handle, "SRC_ABBR")
|
||||||
|
if not cached:
|
||||||
try:
|
try:
|
||||||
source = self.db.get_source_from_handle(source_handle)
|
source = self.db.get_source_from_handle(source_handle)
|
||||||
return str(source.get_abbreviation())
|
value = str(source.get_abbreviation())
|
||||||
except:
|
except:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(source_handle, "SRC_ABBR", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def citation_src_pinfo(self, data):
|
def citation_src_pinfo(self, data):
|
||||||
source_handle = data[COLUMN_SOURCE]
|
source_handle = data[COLUMN_SOURCE]
|
||||||
|
cached, value = self.get_cached_value(source_handle, "SRC_PINFO")
|
||||||
|
if not cached:
|
||||||
try:
|
try:
|
||||||
source = self.db.get_source_from_handle(source_handle)
|
source = self.db.get_source_from_handle(source_handle)
|
||||||
return str(source.get_publication_info())
|
value = str(source.get_publication_info())
|
||||||
except:
|
except:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(source_handle, "SRC_PINFO", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def citation_src_private(self, data):
|
def citation_src_private(self, data):
|
||||||
source_handle = data[COLUMN_SOURCE]
|
source_handle = data[COLUMN_SOURCE]
|
||||||
|
cached, value = self.get_cached_value(source_handle, "SRC_PRIVATE")
|
||||||
|
if not cached:
|
||||||
try:
|
try:
|
||||||
source = self.db.get_source_from_handle(source_handle)
|
source = self.db.get_source_from_handle(source_handle)
|
||||||
if source.get_privacy():
|
if source.get_privacy():
|
||||||
return 'gramps-lock'
|
value = 'gramps-lock'
|
||||||
else:
|
else:
|
||||||
# There is a problem returning None here.
|
# There is a problem returning None here.
|
||||||
return ''
|
value = ''
|
||||||
except:
|
except:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(source_handle, "SRC_PRIVATE", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def citation_src_tags(self, data):
|
def citation_src_tags(self, data):
|
||||||
source_handle = data[COLUMN_SOURCE]
|
source_handle = data[COLUMN_SOURCE]
|
||||||
|
cached, value = self.get_cached_value(source_handle, "SRC_TAGS")
|
||||||
|
if not cached:
|
||||||
try:
|
try:
|
||||||
source = self.db.get_source_from_handle(source_handle)
|
source = self.db.get_source_from_handle(source_handle)
|
||||||
tag_list = list(map(self.get_tag_name, source.get_tag_list()))
|
tag_list = list(map(self.get_tag_name, source.get_tag_list()))
|
||||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
value = ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||||
except:
|
except:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(source_handle, "SRC_TAGS", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def citation_src_chan(self, data):
|
def citation_src_chan(self, data):
|
||||||
source_handle = data[COLUMN_SOURCE]
|
source_handle = data[COLUMN_SOURCE]
|
||||||
|
cached, value = self.get_cached_value(source_handle, "SRC_CHAN")
|
||||||
|
if not cached:
|
||||||
try:
|
try:
|
||||||
source = self.db.get_source_from_handle(source_handle)
|
source = self.db.get_source_from_handle(source_handle)
|
||||||
return format_time(source.change)
|
value = format_time(source.change)
|
||||||
except:
|
except:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(source_handle, "SRC_CHAN", value)
|
||||||
|
return value
|
||||||
|
|
||||||
# Fields access when 'data' is a Source
|
# Fields access when 'data' is a Source
|
||||||
|
|
||||||
@ -259,15 +294,18 @@ class CitationBaseModel(object):
|
|||||||
"""
|
"""
|
||||||
Return the tag color.
|
Return the tag color.
|
||||||
"""
|
"""
|
||||||
|
tag_handle = data[0]
|
||||||
|
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||||
|
if not cached:
|
||||||
tag_color = "#000000000000"
|
tag_color = "#000000000000"
|
||||||
tag_priority = None
|
tag_priority = None
|
||||||
for handle in data[COLUMN2_TAGS]:
|
for handle in data[COLUMN2_TAGS]:
|
||||||
tag = self.db.get_tag_from_handle(handle)
|
tag = self.db.get_tag_from_handle(handle)
|
||||||
if tag:
|
|
||||||
this_priority = tag.get_priority()
|
this_priority = tag.get_priority()
|
||||||
if tag_priority is None or this_priority < tag_priority:
|
if tag_priority is None or this_priority < tag_priority:
|
||||||
tag_color = tag.get_color()
|
tag_color = tag.get_color()
|
||||||
tag_priority = this_priority
|
tag_priority = this_priority
|
||||||
|
self.set_cached_value(tag_handle, "TAG_COLOR", tag_color)
|
||||||
return tag_color
|
return tag_color
|
||||||
|
|
||||||
def source_src_chan(self, data):
|
def source_src_chan(self, data):
|
||||||
@ -284,4 +322,8 @@ class CitationBaseModel(object):
|
|||||||
"""
|
"""
|
||||||
Return the tag name from the given tag handle.
|
Return the tag name from the given tag handle.
|
||||||
"""
|
"""
|
||||||
return self.db.get_tag_from_handle(tag_handle).get_name()
|
cached, value = self.get_cached_value(tag_handle, "TAG_NAME")
|
||||||
|
if not cached:
|
||||||
|
value = self.db.get_tag_from_handle(tag_handle).get_name()
|
||||||
|
self.set_cached_value(tag_handle, "TAG_NAME", value)
|
||||||
|
return value
|
||||||
|
@ -49,7 +49,7 @@ from gramps.gen.const import GRAMPS_LOCALE as glocale
|
|||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# COLUMN constants
|
# Positions in raw data structure
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
COLUMN_HANDLE = 0
|
COLUMN_HANDLE = 0
|
||||||
@ -127,13 +127,22 @@ class EventModel(FlatBaseModel):
|
|||||||
return data[COLUMN_DESCRIPTION]
|
return data[COLUMN_DESCRIPTION]
|
||||||
|
|
||||||
def column_participant(self,data):
|
def column_participant(self,data):
|
||||||
return get_participant_from_event(self.db, data[COLUMN_HANDLE])
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "PARTICIPANT")
|
||||||
|
if not cached:
|
||||||
|
value = get_participant_from_event(self.db, data[COLUMN_HANDLE])
|
||||||
|
self.set_cached_value(handle, "PARTICIPANT", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_place(self,data):
|
def column_place(self,data):
|
||||||
if data[COLUMN_PLACE]:
|
if data[COLUMN_PLACE]:
|
||||||
|
cached, value = self.get_cached_value(data[0], "PLACE")
|
||||||
|
if not cached:
|
||||||
event = Event()
|
event = Event()
|
||||||
event.unserialize(data)
|
event.unserialize(data)
|
||||||
return place_displayer.display_event(self.db, event)
|
value = place_displayer.display_event(self.db, event)
|
||||||
|
self.set_cached_value(data[0], "PLACE", value)
|
||||||
|
return value
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@ -185,21 +194,29 @@ class EventModel(FlatBaseModel):
|
|||||||
"""
|
"""
|
||||||
Return the tag name from the given tag handle.
|
Return the tag name from the given tag handle.
|
||||||
"""
|
"""
|
||||||
return self.db.get_tag_from_handle(tag_handle).get_name()
|
# TAG_NAME isn't a column, but we cache it
|
||||||
|
cached, value = self.get_cached_value(tag_handle, "TAG_NAME")
|
||||||
|
if not cached:
|
||||||
|
value = self.db.get_tag_from_handle(tag_handle).get_name()
|
||||||
|
self.set_cached_value(tag_handle, "TAG_NAME", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tag_color(self, data):
|
def column_tag_color(self, data):
|
||||||
"""
|
"""
|
||||||
Return the tag color.
|
Return the tag color.
|
||||||
"""
|
"""
|
||||||
|
tag_handle = data[0]
|
||||||
|
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||||
|
if not cached:
|
||||||
tag_color = "#000000000000"
|
tag_color = "#000000000000"
|
||||||
tag_priority = None
|
tag_priority = None
|
||||||
for handle in data[COLUMN_TAGS]:
|
for handle in data[COLUMN_TAGS]:
|
||||||
tag = self.db.get_tag_from_handle(handle)
|
tag = self.db.get_tag_from_handle(handle)
|
||||||
if tag:
|
|
||||||
this_priority = tag.get_priority()
|
this_priority = tag.get_priority()
|
||||||
if tag_priority is None or this_priority < tag_priority:
|
if tag_priority is None or this_priority < tag_priority:
|
||||||
tag_color = tag.get_color()
|
tag_color = tag.get_color()
|
||||||
tag_priority = this_priority
|
tag_priority = this_priority
|
||||||
|
self.set_cached_value(tag_handle, "TAG_COLOR", tag_color)
|
||||||
return tag_color
|
return tag_color
|
||||||
|
|
||||||
def column_tags(self, data):
|
def column_tags(self, data):
|
||||||
|
@ -106,56 +106,86 @@ class FamilyModel(FlatBaseModel):
|
|||||||
return len(self.fmap)+1
|
return len(self.fmap)+1
|
||||||
|
|
||||||
def column_father(self, data):
|
def column_father(self, data):
|
||||||
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "FATHER")
|
||||||
|
if not cached:
|
||||||
if data[2]:
|
if data[2]:
|
||||||
person = self.db.get_person_from_handle(data[2])
|
person = self.db.get_person_from_handle(data[2])
|
||||||
return name_displayer.display_name(person.primary_name)
|
value = name_displayer.display_name(person.primary_name)
|
||||||
else:
|
else:
|
||||||
return ""
|
value = ""
|
||||||
|
self.set_cached_value(handle, "FATHER", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def sort_father(self, data):
|
def sort_father(self, data):
|
||||||
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "SORT_FATHER")
|
||||||
|
if not cached:
|
||||||
if data[2]:
|
if data[2]:
|
||||||
person = self.db.get_person_from_handle(data[2])
|
person = self.db.get_person_from_handle(data[2])
|
||||||
return name_displayer.sorted_name(person.primary_name)
|
value = name_displayer.sorted_name(person.primary_name)
|
||||||
else:
|
else:
|
||||||
return ""
|
value = ""
|
||||||
|
self.set_cached_value(handle, "SORT_FATHER", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_mother(self, data):
|
def column_mother(self, data):
|
||||||
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "MOTHER")
|
||||||
|
if not cached:
|
||||||
if data[3]:
|
if data[3]:
|
||||||
person = self.db.get_person_from_handle(data[3])
|
person = self.db.get_person_from_handle(data[3])
|
||||||
return name_displayer.display_name(person.primary_name)
|
value = name_displayer.display_name(person.primary_name)
|
||||||
else:
|
else:
|
||||||
return ""
|
value = ""
|
||||||
|
self.set_cached_value(handle, "MOTHER", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def sort_mother(self, data):
|
def sort_mother(self, data):
|
||||||
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "SORT_MOTHER")
|
||||||
|
if not cached:
|
||||||
if data[3]:
|
if data[3]:
|
||||||
person = self.db.get_person_from_handle(data[3])
|
person = self.db.get_person_from_handle(data[3])
|
||||||
return name_displayer.sorted_name(person.primary_name)
|
value = name_displayer.sorted_name(person.primary_name)
|
||||||
else:
|
else:
|
||||||
return ""
|
value = ""
|
||||||
|
self.set_cached_value(handle, "SORT_MOTHER", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_type(self, data):
|
def column_type(self, data):
|
||||||
return str(FamilyRelType(data[5]))
|
return str(FamilyRelType(data[5]))
|
||||||
|
|
||||||
def column_marriage(self, data):
|
def column_marriage(self, data):
|
||||||
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "MARRIAGE")
|
||||||
|
if not cached:
|
||||||
family = self.db.get_family_from_handle(data[0])
|
family = self.db.get_family_from_handle(data[0])
|
||||||
event = get_marriage_or_fallback(self.db, family, "<i>%s</i>")
|
event = get_marriage_or_fallback(self.db, family, "<i>%s</i>")
|
||||||
if event:
|
if event:
|
||||||
if event.date.format:
|
if event.date.format:
|
||||||
return event.date.format % displayer.display(event.date)
|
value = event.date.format % displayer.display(event.date)
|
||||||
elif not get_date_valid(event):
|
elif not get_date_valid(event):
|
||||||
return invalid_date_format % displayer.display(event.date)
|
value = invalid_date_format % displayer.display(event.date)
|
||||||
else:
|
else:
|
||||||
return "%s" % displayer.display(event.date)
|
value = "%s" % displayer.display(event.date)
|
||||||
else:
|
else:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(handle, "MARRIAGE", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def sort_marriage(self, data):
|
def sort_marriage(self, data):
|
||||||
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "SORT_MARRIAGE")
|
||||||
|
if not cached:
|
||||||
family = self.db.get_family_from_handle(data[0])
|
family = self.db.get_family_from_handle(data[0])
|
||||||
event = get_marriage_or_fallback(self.db, family)
|
event = get_marriage_or_fallback(self.db, family)
|
||||||
if event:
|
if event:
|
||||||
return "%09d" % event.date.get_sort_value()
|
value = "%09d" % event.date.get_sort_value()
|
||||||
else:
|
else:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(handle, "SORT_MARRIAGE", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_id(self, data):
|
def column_id(self, data):
|
||||||
return str(data[1])
|
return str(data[1])
|
||||||
@ -177,12 +207,19 @@ class FamilyModel(FlatBaseModel):
|
|||||||
"""
|
"""
|
||||||
Return the tag name from the given tag handle.
|
Return the tag name from the given tag handle.
|
||||||
"""
|
"""
|
||||||
return self.db.get_tag_from_handle(tag_handle).get_name()
|
cached, value = self.get_cached_value(tag_handle, "TAG_NAME")
|
||||||
|
if not cached:
|
||||||
|
value = self.db.get_tag_from_handle(tag_handle).get_name()
|
||||||
|
self.set_cached_value(tag_handle, "TAG_NAME", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tag_color(self, data):
|
def column_tag_color(self, data):
|
||||||
"""
|
"""
|
||||||
Return the tag color.
|
Return the tag color.
|
||||||
"""
|
"""
|
||||||
|
tag_handle = data[0]
|
||||||
|
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||||
|
if not cached:
|
||||||
tag_color = "#000000000000"
|
tag_color = "#000000000000"
|
||||||
tag_priority = None
|
tag_priority = None
|
||||||
for handle in data[13]:
|
for handle in data[13]:
|
||||||
@ -191,6 +228,7 @@ class FamilyModel(FlatBaseModel):
|
|||||||
if tag_priority is None or this_priority < tag_priority:
|
if tag_priority is None or this_priority < tag_priority:
|
||||||
tag_color = tag.get_color()
|
tag_color = tag.get_color()
|
||||||
tag_priority = this_priority
|
tag_priority = this_priority
|
||||||
|
self.set_cached_value(tag_handle, "TAG_COLOR", tag_color)
|
||||||
return tag_color
|
return tag_color
|
||||||
|
|
||||||
def column_tags(self, data):
|
def column_tags(self, data):
|
||||||
|
@ -73,6 +73,7 @@ from gi.repository import Gtk
|
|||||||
from gramps.gen.filters import SearchFilter, ExactSearchFilter
|
from gramps.gen.filters import SearchFilter, ExactSearchFilter
|
||||||
from gramps.gen.constfunc import conv_to_unicode, handle2internal
|
from gramps.gen.constfunc import conv_to_unicode, handle2internal
|
||||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||||
|
from .basemodel import BaseModel
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
@ -442,7 +443,7 @@ class FlatNodeMap(object):
|
|||||||
# FlatBaseModel
|
# FlatBaseModel
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
class FlatBaseModel(GObject.GObject, Gtk.TreeModel):
|
class FlatBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel):
|
||||||
"""
|
"""
|
||||||
The base class for all flat treeview models.
|
The base class for all flat treeview models.
|
||||||
It keeps a FlatNodeMap, and obtains data from database as needed
|
It keeps a FlatNodeMap, and obtains data from database as needed
|
||||||
@ -454,7 +455,8 @@ class FlatBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
search=None, skip=set(),
|
search=None, skip=set(),
|
||||||
sort_map=None):
|
sort_map=None):
|
||||||
cput = time.clock()
|
cput = time.clock()
|
||||||
super(FlatBaseModel, self).__init__()
|
GObject.GObject.__init__(self)
|
||||||
|
BaseModel.__init__(self)
|
||||||
#inheriting classes must set self.map to obtain the data
|
#inheriting classes must set self.map to obtain the data
|
||||||
self.prev_handle = None
|
self.prev_handle = None
|
||||||
self.prev_data = None
|
self.prev_data = None
|
||||||
@ -491,6 +493,7 @@ class FlatBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
"""
|
"""
|
||||||
Unset all elements that prevent garbage collection
|
Unset all elements that prevent garbage collection
|
||||||
"""
|
"""
|
||||||
|
BaseModel.destroy(self)
|
||||||
self.db = None
|
self.db = None
|
||||||
self.sort_func = None
|
self.sort_func = None
|
||||||
if self.node_map:
|
if self.node_map:
|
||||||
@ -556,15 +559,6 @@ class FlatBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
"""
|
"""
|
||||||
return None
|
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):
|
def sort_keys(self):
|
||||||
"""
|
"""
|
||||||
Return the (sort_key, handle) list of all data that can maximally
|
Return the (sort_key, handle) list of all data that can maximally
|
||||||
@ -775,7 +769,10 @@ class FlatBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
We need this to search in the column in the GUI
|
We need this to search in the column in the GUI
|
||||||
"""
|
"""
|
||||||
if handle != self.prev_handle:
|
if handle != self.prev_handle:
|
||||||
|
cached, data = self.get_cached_value(handle, col)
|
||||||
|
if not cached:
|
||||||
data = self.map(handle)
|
data = self.map(handle)
|
||||||
|
self.set_cached_value(handle, col, data)
|
||||||
if data is None:
|
if data is None:
|
||||||
#object is no longer present
|
#object is no longer present
|
||||||
return ''
|
return ''
|
||||||
|
@ -39,7 +39,10 @@ class LRU(object):
|
|||||||
Implementation of a length-limited O(1) LRU cache
|
Implementation of a length-limited O(1) LRU cache
|
||||||
"""
|
"""
|
||||||
def __init__(self, count):
|
def __init__(self, count):
|
||||||
self.count = max(count, 2)
|
"""
|
||||||
|
Set count to 0 or 1 to disable.
|
||||||
|
"""
|
||||||
|
self.count = count
|
||||||
self.data = {}
|
self.data = {}
|
||||||
self.first = None
|
self.first = None
|
||||||
self.last = None
|
self.last = None
|
||||||
@ -60,6 +63,8 @@ class LRU(object):
|
|||||||
"""
|
"""
|
||||||
Set the item in the LRU, removing an old entry if needed
|
Set the item in the LRU, removing an old entry if needed
|
||||||
"""
|
"""
|
||||||
|
if self.count <= 1: # Disabled
|
||||||
|
return
|
||||||
if obj in self.data:
|
if obj in self.data:
|
||||||
del self[obj]
|
del self[obj]
|
||||||
nobj = Node(self.last, (obj, val))
|
nobj = Node(self.last, (obj, val))
|
||||||
|
@ -174,12 +174,19 @@ class MediaModel(FlatBaseModel):
|
|||||||
"""
|
"""
|
||||||
Return the tag name from the given tag handle.
|
Return the tag name from the given tag handle.
|
||||||
"""
|
"""
|
||||||
return self.db.get_tag_from_handle(tag_handle).get_name()
|
cached, value = self.get_cached_value(tag_handle, "TAG_NAME")
|
||||||
|
if not cached:
|
||||||
|
value = self.db.get_tag_from_handle(tag_handle).get_name()
|
||||||
|
self.set_cached_value(tag_handle, "TAG_NAME", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tag_color(self, data):
|
def column_tag_color(self, data):
|
||||||
"""
|
"""
|
||||||
Return the tag color.
|
Return the tag color.
|
||||||
"""
|
"""
|
||||||
|
tag_handle = data[0]
|
||||||
|
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||||
|
if not cached:
|
||||||
tag_color = "#000000000000"
|
tag_color = "#000000000000"
|
||||||
tag_priority = None
|
tag_priority = None
|
||||||
for handle in data[11]:
|
for handle in data[11]:
|
||||||
@ -188,6 +195,7 @@ class MediaModel(FlatBaseModel):
|
|||||||
if tag_priority is None or this_priority < tag_priority:
|
if tag_priority is None or this_priority < tag_priority:
|
||||||
tag_color = tag.get_color()
|
tag_color = tag.get_color()
|
||||||
tag_priority = this_priority
|
tag_priority = this_priority
|
||||||
|
self.set_cached_value(tag_handle, "TAG_COLOR", tag_color)
|
||||||
return tag_color
|
return tag_color
|
||||||
|
|
||||||
def column_tags(self, data):
|
def column_tags(self, data):
|
||||||
|
@ -137,12 +137,19 @@ class NoteModel(FlatBaseModel):
|
|||||||
"""
|
"""
|
||||||
Return the tag name from the given tag handle.
|
Return the tag name from the given tag handle.
|
||||||
"""
|
"""
|
||||||
return self.db.get_tag_from_handle(tag_handle).get_name()
|
cached, value = self.get_cached_value(tag_handle, "TAG_NAME")
|
||||||
|
if not cached:
|
||||||
|
value = self.db.get_tag_from_handle(tag_handle).get_name()
|
||||||
|
self.set_cached_value(tag_handle, "TAG_NAME", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tag_color(self, data):
|
def column_tag_color(self, data):
|
||||||
"""
|
"""
|
||||||
Return the tag color.
|
Return the tag color.
|
||||||
"""
|
"""
|
||||||
|
tag_handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||||
|
if not cached:
|
||||||
tag_color = "#000000000000"
|
tag_color = "#000000000000"
|
||||||
tag_priority = None
|
tag_priority = None
|
||||||
for handle in data[Note.POS_TAGS]:
|
for handle in data[Note.POS_TAGS]:
|
||||||
@ -152,7 +159,9 @@ class NoteModel(FlatBaseModel):
|
|||||||
if tag_priority is None or this_priority < tag_priority:
|
if tag_priority is None or this_priority < tag_priority:
|
||||||
tag_color = tag.get_color()
|
tag_color = tag.get_color()
|
||||||
tag_priority = this_priority
|
tag_priority = this_priority
|
||||||
return tag_color
|
value = tag_color
|
||||||
|
self.set_cached_value(tag_handle, "TAG_COLOR", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tags(self, data):
|
def column_tags(self, data):
|
||||||
"""
|
"""
|
||||||
|
@ -38,6 +38,7 @@ from html import escape
|
|||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
|
from gi.repository import GObject
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
@ -59,15 +60,15 @@ from gramps.gen.lib import (Name, EventRef, EventType, EventRoleType,
|
|||||||
from gramps.gen.display.name import displayer as name_displayer
|
from gramps.gen.display.name import displayer as name_displayer
|
||||||
from gramps.gen.display.place import displayer as place_displayer
|
from gramps.gen.display.place import displayer as place_displayer
|
||||||
from gramps.gen.datehandler import format_time, get_date, get_date_valid
|
from gramps.gen.datehandler import format_time, get_date, get_date_valid
|
||||||
from .lru import LRU
|
|
||||||
from .flatbasemodel import FlatBaseModel
|
from .flatbasemodel import FlatBaseModel
|
||||||
from .treebasemodel import TreeBaseModel
|
from .treebasemodel import TreeBaseModel
|
||||||
|
from .basemodel import BaseModel
|
||||||
from gramps.gen.config import config
|
from gramps.gen.config import config
|
||||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# COLUMN constants
|
# COLUMN constants; positions in raw data structure
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
COLUMN_ID = 1
|
COLUMN_ID = 1
|
||||||
@ -90,19 +91,17 @@ invalid_date_format = config.get('preferences.invalid-date-format')
|
|||||||
# PeopleBaseModel
|
# PeopleBaseModel
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
class PeopleBaseModel(object):
|
class PeopleBaseModel(BaseModel):
|
||||||
"""
|
"""
|
||||||
Basic Model interface to handle the PersonViews
|
Basic Model interface to handle the PersonViews
|
||||||
"""
|
"""
|
||||||
_GENDER = [ _('female'), _('male'), _('unknown') ]
|
_GENDER = [ _('female'), _('male'), _('unknown') ]
|
||||||
|
|
||||||
# LRU cache size
|
|
||||||
_CACHE_SIZE = 250
|
|
||||||
|
|
||||||
def __init__(self, db):
|
def __init__(self, db):
|
||||||
"""
|
"""
|
||||||
Initialize the model building the initial data
|
Initialize the model building the initial data
|
||||||
"""
|
"""
|
||||||
|
BaseModel.__init__(self)
|
||||||
self.db = db
|
self.db = db
|
||||||
self.gen_cursor = db.get_person_cursor
|
self.gen_cursor = db.get_person_cursor
|
||||||
self.map = db.get_raw_person_data
|
self.map = db.get_raw_person_data
|
||||||
@ -144,24 +143,16 @@ class PeopleBaseModel(object):
|
|||||||
self.column_tag_color,
|
self.column_tag_color,
|
||||||
]
|
]
|
||||||
|
|
||||||
#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 destroy(self):
|
def destroy(self):
|
||||||
"""
|
"""
|
||||||
Unset all elements that can prevent garbage collection
|
Unset all elements that can prevent garbage collection
|
||||||
"""
|
"""
|
||||||
|
BaseModel.destroy(self)
|
||||||
self.db = None
|
self.db = None
|
||||||
self.gen_cursor = None
|
self.gen_cursor = None
|
||||||
self.map = None
|
self.map = None
|
||||||
self.fmap = None
|
self.fmap = None
|
||||||
self.smap = None
|
self.smap = None
|
||||||
self.clear_local_cache()
|
|
||||||
|
|
||||||
def color_column(self):
|
def color_column(self):
|
||||||
"""
|
"""
|
||||||
@ -169,63 +160,38 @@ class PeopleBaseModel(object):
|
|||||||
"""
|
"""
|
||||||
return 15
|
return 15
|
||||||
|
|
||||||
def clear_local_cache(self, handle=None):
|
|
||||||
""" Clear the LRU cache """
|
|
||||||
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):
|
def on_get_n_columns(self):
|
||||||
""" Return the number of columns in the model """
|
""" Return the number of columns in the model """
|
||||||
return len(self.fmap)+1
|
return len(self.fmap)+1
|
||||||
|
|
||||||
def sort_name(self, data):
|
def sort_name(self, data):
|
||||||
|
handle = data[0]
|
||||||
|
cached, name = self.get_cached_value(handle, "SORT_NAME")
|
||||||
|
if not cached:
|
||||||
name = name_displayer.raw_sorted_name(data[COLUMN_NAME])
|
name = name_displayer.raw_sorted_name(data[COLUMN_NAME])
|
||||||
# internally we work with utf-8
|
# internally we work with utf-8
|
||||||
if not isinstance(name, str):
|
if not isinstance(name, str):
|
||||||
name = name.decode('utf-8')
|
name = name.decode('utf-8')
|
||||||
|
self.set_cached_value(handle, "SORT_NAME", name)
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def column_name(self, data):
|
def column_name(self, data):
|
||||||
handle = data[0]
|
handle = data[0]
|
||||||
if handle in self.lru_name:
|
cached, name = self.get_cached_value(handle, "NAME")
|
||||||
name = self.lru_name[handle]
|
if not cached:
|
||||||
else:
|
|
||||||
name = name_displayer.raw_display_name(data[COLUMN_NAME])
|
name = name_displayer.raw_display_name(data[COLUMN_NAME])
|
||||||
# internally we work with utf-8 for python 2.7
|
# internally we work with utf-8 for python 2.7
|
||||||
if not isinstance(name, str):
|
if not isinstance(name, str):
|
||||||
name = name.encode('utf-8')
|
name = name.encode('utf-8')
|
||||||
if not self._in_build:
|
self.set_cached_value(handle, "NAME", name)
|
||||||
self.lru_name[handle] = name
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def column_spouse(self, data):
|
def column_spouse(self, data):
|
||||||
handle = data[0]
|
handle = data[0]
|
||||||
if handle in self.lru_spouse:
|
cached, value = self.get_cached_value(handle, "SPOUSE")
|
||||||
value = self.lru_spouse[handle]
|
if not cached:
|
||||||
else:
|
|
||||||
value = self._get_spouse_data(data)
|
value = self._get_spouse_data(data)
|
||||||
if not self._in_build:
|
self.set_cached_value(handle, "SPOUSE", value)
|
||||||
self.lru_spouse[handle] = value
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def column_private(self, data):
|
def column_private(self, data):
|
||||||
@ -265,17 +231,19 @@ class PeopleBaseModel(object):
|
|||||||
|
|
||||||
def column_birth_day(self, data):
|
def column_birth_day(self, data):
|
||||||
handle = data[0]
|
handle = data[0]
|
||||||
if handle in self.lru_bdate:
|
cached, value = self.get_cached_value(handle, "BIRTH_DAY")
|
||||||
value = self.lru_bdate[handle]
|
if not cached:
|
||||||
else:
|
|
||||||
value = self._get_birth_data(data, False)
|
value = self._get_birth_data(data, False)
|
||||||
if not self._in_build:
|
self.set_cached_value(handle, "BIRTH_DAY", value)
|
||||||
self.lru_bdate[handle] = value
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def sort_birth_day(self, data):
|
def sort_birth_day(self, data):
|
||||||
handle = data[0]
|
handle = data[0]
|
||||||
return self._get_birth_data(data, True)
|
cached, value = self.get_cached_value(handle, "SORT_BIRTH_DAY")
|
||||||
|
if not cached:
|
||||||
|
value = self._get_birth_data(data, True)
|
||||||
|
self.set_cached_value(handle, "SORT_BIRTH_DAY", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def _get_birth_data(self, data, sort_mode):
|
def _get_birth_data(self, data, sort_mode):
|
||||||
index = data[COLUMN_BIRTH]
|
index = data[COLUMN_BIRTH]
|
||||||
@ -320,17 +288,19 @@ class PeopleBaseModel(object):
|
|||||||
|
|
||||||
def column_death_day(self, data):
|
def column_death_day(self, data):
|
||||||
handle = data[0]
|
handle = data[0]
|
||||||
if handle in self.lru_ddate:
|
cached, value = self.get_cached_value(handle, "DEATH_DAY")
|
||||||
value = self.lru_ddate[handle]
|
if not cached:
|
||||||
else:
|
|
||||||
value = self._get_death_data(data, False)
|
value = self._get_death_data(data, False)
|
||||||
if not self._in_build:
|
self.set_cached_value(handle, "DEATH_DAY", value)
|
||||||
self.lru_ddate[handle] = value
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def sort_death_day(self, data):
|
def sort_death_day(self, data):
|
||||||
handle = data[0]
|
handle = data[0]
|
||||||
return self._get_death_data(data, True)
|
cached, value = self.get_cached_value(handle, "SORT_DEATH_DAY")
|
||||||
|
if not cached:
|
||||||
|
value = self._get_death_data(data, True)
|
||||||
|
self.set_cached_value(handle, "SORT_DEATH_DAY", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def _get_death_data(self, data, sort_mode):
|
def _get_death_data(self, data, sort_mode):
|
||||||
index = data[COLUMN_DEATH]
|
index = data[COLUMN_DEATH]
|
||||||
@ -375,6 +345,11 @@ class PeopleBaseModel(object):
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
def column_birth_place(self, data):
|
def column_birth_place(self, data):
|
||||||
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "BIRTH_PLACE")
|
||||||
|
if cached:
|
||||||
|
return value
|
||||||
|
else:
|
||||||
index = data[COLUMN_BIRTH]
|
index = data[COLUMN_BIRTH]
|
||||||
if index != -1:
|
if index != -1:
|
||||||
try:
|
try:
|
||||||
@ -385,9 +360,13 @@ class PeopleBaseModel(object):
|
|||||||
if event:
|
if event:
|
||||||
place_title = place_displayer.display_event(self.db, event)
|
place_title = place_displayer.display_event(self.db, event)
|
||||||
if place_title:
|
if place_title:
|
||||||
return escape(place_title)
|
value = escape(place_title)
|
||||||
|
self.set_cached_value(handle, "BIRTH_PLACE", value)
|
||||||
|
return value
|
||||||
except:
|
except:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(handle, "BIRTH_PLACE", value)
|
||||||
|
return value
|
||||||
|
|
||||||
for event_ref in data[COLUMN_EVENT]:
|
for event_ref in data[COLUMN_EVENT]:
|
||||||
er = EventRef()
|
er = EventRef()
|
||||||
@ -396,13 +375,21 @@ class PeopleBaseModel(object):
|
|||||||
etype = event.get_type()
|
etype = event.get_type()
|
||||||
if (etype in [EventType.BAPTISM, EventType.CHRISTEN] and
|
if (etype in [EventType.BAPTISM, EventType.CHRISTEN] and
|
||||||
er.get_role() == EventRoleType.PRIMARY):
|
er.get_role() == EventRoleType.PRIMARY):
|
||||||
|
|
||||||
place_title = place_displayer.display_event(self.db, event)
|
place_title = place_displayer.display_event(self.db, event)
|
||||||
if place_title:
|
if place_title:
|
||||||
return "<i>%s</i>" % escape(place_title)
|
value = "<i>%s</i>" % escape(place_title)
|
||||||
return ""
|
self.set_cached_value(handle, "BIRTH_PLACE", value)
|
||||||
|
return value
|
||||||
|
value = ""
|
||||||
|
self.set_cached_value(handle, "BIRTH_PLACE", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_death_place(self, data):
|
def column_death_place(self, data):
|
||||||
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "DEATH_PLACE")
|
||||||
|
if cached:
|
||||||
|
return value
|
||||||
|
else:
|
||||||
index = data[COLUMN_DEATH]
|
index = data[COLUMN_DEATH]
|
||||||
if index != -1:
|
if index != -1:
|
||||||
try:
|
try:
|
||||||
@ -413,9 +400,13 @@ class PeopleBaseModel(object):
|
|||||||
if event:
|
if event:
|
||||||
place_title = place_displayer.display_event(self.db, event)
|
place_title = place_displayer.display_event(self.db, event)
|
||||||
if place_title:
|
if place_title:
|
||||||
return escape(place_title)
|
value = escape(place_title)
|
||||||
|
self.set_cached_value(handle, "DEATH_PLACE", value)
|
||||||
|
return value
|
||||||
except:
|
except:
|
||||||
return ''
|
value = ''
|
||||||
|
self.set_cached_value(handle, "DEATH_PLACE", value)
|
||||||
|
return value
|
||||||
|
|
||||||
for event_ref in data[COLUMN_EVENT]:
|
for event_ref in data[COLUMN_EVENT]:
|
||||||
er = EventRef()
|
er = EventRef()
|
||||||
@ -428,8 +419,12 @@ class PeopleBaseModel(object):
|
|||||||
|
|
||||||
place_title = place_displayer.display_event(self.db, event)
|
place_title = place_displayer.display_event(self.db, event)
|
||||||
if place_title:
|
if place_title:
|
||||||
return "<i>%s</i>" % escape(place_title)
|
value = "<i>%s</i>" % escape(place_title)
|
||||||
return ""
|
self.set_cached_value(handle, "DEATH_PLACE", value)
|
||||||
|
return value
|
||||||
|
value = ""
|
||||||
|
self.set_cached_value(handle, "DEATH_PLACE", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def _get_parents_data(self, data):
|
def _get_parents_data(self, data):
|
||||||
parents = 0
|
parents = 0
|
||||||
@ -468,39 +463,86 @@ class PeopleBaseModel(object):
|
|||||||
return todo
|
return todo
|
||||||
|
|
||||||
def column_parents(self, data):
|
def column_parents(self, data):
|
||||||
return str(self._get_parents_data(data))
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "PARENTS")
|
||||||
|
if not cached:
|
||||||
|
value = self._get_parents_data(data)
|
||||||
|
self.set_cached_value(handle, "PARENTS", value)
|
||||||
|
return str(value)
|
||||||
|
|
||||||
def sort_parents(self, data):
|
def sort_parents(self, data):
|
||||||
return '%06d' % self._get_parents_data(data)
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "SORT_PARENTS")
|
||||||
|
if not cached:
|
||||||
|
value = self._get_parents_data(data)
|
||||||
|
self.set_cached_value(handle, "SORT_PARENTS", value)
|
||||||
|
return '%06d' % value
|
||||||
|
|
||||||
def column_marriages(self, data):
|
def column_marriages(self, data):
|
||||||
return str(self._get_marriages_data(data))
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "MARRIAGES")
|
||||||
|
if not cached:
|
||||||
|
value = self._get_marriages_data(data)
|
||||||
|
self.set_cached_value(handle, "MARRIAGES", value)
|
||||||
|
return str(value)
|
||||||
|
|
||||||
def sort_marriages(self, data):
|
def sort_marriages(self, data):
|
||||||
return '%06d' % self._get_marriages_data(data)
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "SORT_MARRIAGES")
|
||||||
|
if not cached:
|
||||||
|
value = self._get_marriages_data(data)
|
||||||
|
self.set_cached_value(handle, "SORT_MARRIAGES", value)
|
||||||
|
return '%06d' % value
|
||||||
|
|
||||||
def column_children(self, data):
|
def column_children(self, data):
|
||||||
return str(self._get_children_data(data))
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "CHILDREN")
|
||||||
|
if not cached:
|
||||||
|
value = self._get_children_data(data)
|
||||||
|
self.set_cached_value(handle, "CHILDREN", value)
|
||||||
|
return str(value)
|
||||||
|
|
||||||
def sort_children(self, data):
|
def sort_children(self, data):
|
||||||
return '%06d' % self._get_children_data(data)
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "SORT_CHILDREN")
|
||||||
|
if not cached:
|
||||||
|
value = self._get_children_data(data)
|
||||||
|
self.set_cached_value(handle, "SORT_CHILDREN", value)
|
||||||
|
return '%06d' % value
|
||||||
|
|
||||||
def column_todo(self, data):
|
def column_todo(self, data):
|
||||||
return str(self._get_todo_data(data))
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "TODO")
|
||||||
|
if not cached:
|
||||||
|
value = self._get_todo_data(data)
|
||||||
|
self.set_cached_value(handle, "TODO", value)
|
||||||
|
return str(value)
|
||||||
|
|
||||||
def sort_todo(self, data):
|
def sort_todo(self, data):
|
||||||
return '%06d' % self._get_todo_data(data)
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "SORT_TODO")
|
||||||
|
if not cached:
|
||||||
|
value = self._get_todo_data(data)
|
||||||
|
self.set_cached_value(handle, "SORT_TODO", value)
|
||||||
|
return '%06d' % value
|
||||||
|
|
||||||
def get_tag_name(self, tag_handle):
|
def get_tag_name(self, tag_handle):
|
||||||
"""
|
"""
|
||||||
Return the tag name from the given tag handle.
|
Return the tag name from the given tag handle.
|
||||||
"""
|
"""
|
||||||
return self.db.get_tag_from_handle(tag_handle).get_name()
|
cached, value = self.get_cached_value(tag_handle, "TAG_NAME")
|
||||||
|
if not cached:
|
||||||
|
value = self.db.get_tag_from_handle(tag_handle).get_name()
|
||||||
|
self.set_cached_value(tag_handle, "TAG_NAME", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tag_color(self, data):
|
def column_tag_color(self, data):
|
||||||
"""
|
"""
|
||||||
Return the tag color.
|
Return the tag color.
|
||||||
"""
|
"""
|
||||||
|
tag_handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||||
|
if not cached:
|
||||||
tag_color = "#000000000000"
|
tag_color = "#000000000000"
|
||||||
tag_priority = None
|
tag_priority = None
|
||||||
for handle in data[COLUMN_TAGS]:
|
for handle in data[COLUMN_TAGS]:
|
||||||
@ -510,14 +552,21 @@ class PeopleBaseModel(object):
|
|||||||
if tag_priority is None or this_priority < tag_priority:
|
if tag_priority is None or this_priority < tag_priority:
|
||||||
tag_color = tag.get_color()
|
tag_color = tag.get_color()
|
||||||
tag_priority = this_priority
|
tag_priority = this_priority
|
||||||
return tag_color
|
value = tag_color
|
||||||
|
self.set_cached_value(tag_handle, "TAG_COLOR", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tags(self, data):
|
def column_tags(self, data):
|
||||||
"""
|
"""
|
||||||
Return the sorted list of tags.
|
Return the sorted list of tags.
|
||||||
"""
|
"""
|
||||||
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "TAGS")
|
||||||
|
if not cached:
|
||||||
tag_list = list(map(self.get_tag_name, data[COLUMN_TAGS]))
|
tag_list = list(map(self.get_tag_name, data[COLUMN_TAGS]))
|
||||||
return ', '.join(sorted(tag_list, key=glocale.sort_key))
|
value = ', '.join(sorted(tag_list, key=glocale.sort_key))
|
||||||
|
self.set_cached_value(handle, "TAGS", value)
|
||||||
|
return value
|
||||||
|
|
||||||
class PersonListModel(PeopleBaseModel, FlatBaseModel):
|
class PersonListModel(PeopleBaseModel, FlatBaseModel):
|
||||||
"""
|
"""
|
||||||
@ -529,10 +578,6 @@ class PersonListModel(PeopleBaseModel, FlatBaseModel):
|
|||||||
FlatBaseModel.__init__(self, db, search=search, skip=skip, scol=scol,
|
FlatBaseModel.__init__(self, db, search=search, skip=skip, scol=scol,
|
||||||
order=order, sort_map=sort_map)
|
order=order, sort_map=sort_map)
|
||||||
|
|
||||||
def clear_cache(self, handle=None):
|
|
||||||
""" Clear the LRU cache """
|
|
||||||
PeopleBaseModel.clear_local_cache(self, handle)
|
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
"""
|
"""
|
||||||
Unset all elements that can prevent garbage collection
|
Unset all elements that can prevent garbage collection
|
||||||
@ -546,7 +591,6 @@ class PersonTreeModel(PeopleBaseModel, TreeBaseModel):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, db, scol=0, order=Gtk.SortType.ASCENDING, search=None,
|
def __init__(self, db, scol=0, order=Gtk.SortType.ASCENDING, search=None,
|
||||||
skip=set(), sort_map=None):
|
skip=set(), sort_map=None):
|
||||||
|
|
||||||
PeopleBaseModel.__init__(self, db)
|
PeopleBaseModel.__init__(self, db)
|
||||||
TreeBaseModel.__init__(self, db, search=search, skip=skip, scol=scol,
|
TreeBaseModel.__init__(self, db, search=search, skip=skip, scol=scol,
|
||||||
order=order, sort_map=sort_map)
|
order=order, sort_map=sort_map)
|
||||||
@ -564,13 +608,6 @@ class PersonTreeModel(PeopleBaseModel, TreeBaseModel):
|
|||||||
"""
|
"""
|
||||||
self.number_items = self.db.get_number_of_people
|
self.number_items = self.db.get_number_of_people
|
||||||
|
|
||||||
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):
|
def get_tree_levels(self):
|
||||||
"""
|
"""
|
||||||
Return the headings of the levels in the hierarchy.
|
Return the headings of the levels in the hierarchy.
|
||||||
|
@ -116,9 +116,14 @@ class PlaceBaseModel(object):
|
|||||||
return len(self.fmap)+1
|
return len(self.fmap)+1
|
||||||
|
|
||||||
def column_title(self, data):
|
def column_title(self, data):
|
||||||
|
handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(handle, "PLACE")
|
||||||
|
if not cached:
|
||||||
place = Place()
|
place = Place()
|
||||||
place.unserialize(data)
|
place.unserialize(data)
|
||||||
return place_displayer.display(self.db, place)
|
value = place_displayer.display(self.db, place)
|
||||||
|
self.set_cached_value(handle, "PLACE", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_name(self, data):
|
def column_name(self, data):
|
||||||
return str(data[6][0])
|
return str(data[6][0])
|
||||||
@ -181,12 +186,19 @@ class PlaceBaseModel(object):
|
|||||||
"""
|
"""
|
||||||
Return the tag name from the given tag handle.
|
Return the tag name from the given tag handle.
|
||||||
"""
|
"""
|
||||||
return self.db.get_tag_from_handle(tag_handle).get_name()
|
cached, value = self.get_cached_value(tag_handle, "TAG_NAME")
|
||||||
|
if not cached:
|
||||||
|
value = self.db.get_tag_from_handle(tag_handle).get_name()
|
||||||
|
self.set_cached_value(tag_handle, "TAG_NAME", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tag_color(self, data):
|
def column_tag_color(self, data):
|
||||||
"""
|
"""
|
||||||
Return the tag color.
|
Return the tag color.
|
||||||
"""
|
"""
|
||||||
|
tag_handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||||
|
if not cached:
|
||||||
tag_color = "#000000000000"
|
tag_color = "#000000000000"
|
||||||
tag_priority = None
|
tag_priority = None
|
||||||
for handle in data[16]:
|
for handle in data[16]:
|
||||||
@ -196,7 +208,9 @@ class PlaceBaseModel(object):
|
|||||||
if tag_priority is None or this_priority < tag_priority:
|
if tag_priority is None or this_priority < tag_priority:
|
||||||
tag_color = tag.get_color()
|
tag_color = tag.get_color()
|
||||||
tag_priority = this_priority
|
tag_priority = this_priority
|
||||||
return tag_color
|
value = tag_color
|
||||||
|
self.set_cached_value(tag_handle, "TAG_COLOR", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tags(self, data):
|
def column_tags(self, data):
|
||||||
"""
|
"""
|
||||||
|
@ -239,21 +239,29 @@ class RepositoryModel(FlatBaseModel):
|
|||||||
"""
|
"""
|
||||||
Return the tag name from the given tag handle.
|
Return the tag name from the given tag handle.
|
||||||
"""
|
"""
|
||||||
return self.db.get_tag_from_handle(tag_handle).get_name()
|
# TAG_NAME isn't a column, but we cache it
|
||||||
|
cached, value = self.get_cached_value(tag_handle, "TAG_NAME")
|
||||||
|
if not cached:
|
||||||
|
value = self.db.get_tag_from_handle(tag_handle).get_name()
|
||||||
|
self.set_cached_value(tag_handle, "TAG_NAME", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tag_color(self, data):
|
def column_tag_color(self, data):
|
||||||
"""
|
"""
|
||||||
Return the tag color.
|
Return the tag color.
|
||||||
"""
|
"""
|
||||||
|
tag_handle = data[0]
|
||||||
|
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||||
|
if not cached:
|
||||||
tag_color = "#000000000000"
|
tag_color = "#000000000000"
|
||||||
tag_priority = None
|
tag_priority = None
|
||||||
for handle in data[8]:
|
for handle in data[8]:
|
||||||
tag = self.db.get_tag_from_handle(handle)
|
tag = self.db.get_tag_from_handle(handle)
|
||||||
if tag:
|
|
||||||
this_priority = tag.get_priority()
|
this_priority = tag.get_priority()
|
||||||
if tag_priority is None or this_priority < tag_priority:
|
if tag_priority is None or this_priority < tag_priority:
|
||||||
tag_color = tag.get_color()
|
tag_color = tag.get_color()
|
||||||
tag_priority = this_priority
|
tag_priority = this_priority
|
||||||
|
self.set_cached_value(tag_handle, "TAG_COLOR", tag_color)
|
||||||
return tag_color
|
return tag_color
|
||||||
|
|
||||||
def column_tags(self, data):
|
def column_tags(self, data):
|
||||||
|
@ -130,12 +130,19 @@ class SourceModel(FlatBaseModel):
|
|||||||
"""
|
"""
|
||||||
Return the tag name from the given tag handle.
|
Return the tag name from the given tag handle.
|
||||||
"""
|
"""
|
||||||
return self.db.get_tag_from_handle(tag_handle).get_name()
|
cached, value = self.get_cached_value(tag_handle, "TAG_NAME")
|
||||||
|
if not cached:
|
||||||
|
value = self.db.get_tag_from_handle(tag_handle).get_name()
|
||||||
|
self.set_cached_value(tag_handle, "TAG_NAME", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tag_color(self, data):
|
def column_tag_color(self, data):
|
||||||
"""
|
"""
|
||||||
Return the tag color.
|
Return the tag color.
|
||||||
"""
|
"""
|
||||||
|
tag_handle = data[0]
|
||||||
|
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||||
|
if not cached:
|
||||||
tag_color = "#000000000000"
|
tag_color = "#000000000000"
|
||||||
tag_priority = None
|
tag_priority = None
|
||||||
for handle in data[11]:
|
for handle in data[11]:
|
||||||
@ -145,7 +152,9 @@ class SourceModel(FlatBaseModel):
|
|||||||
if tag_priority is None or this_priority < tag_priority:
|
if tag_priority is None or this_priority < tag_priority:
|
||||||
tag_color = tag.get_color()
|
tag_color = tag.get_color()
|
||||||
tag_priority = this_priority
|
tag_priority = this_priority
|
||||||
return tag_color
|
value = tag_color
|
||||||
|
self.set_cached_value(tag_handle, "TAG_COLOR", value)
|
||||||
|
return value
|
||||||
|
|
||||||
def column_tags(self, data):
|
def column_tags(self, data):
|
||||||
"""
|
"""
|
||||||
|
@ -53,9 +53,9 @@ from gi.repository import Gtk
|
|||||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||||
_ = glocale.translation.gettext
|
_ = glocale.translation.gettext
|
||||||
import gramps.gui.widgets.progressdialog as progressdlg
|
import gramps.gui.widgets.progressdialog as progressdlg
|
||||||
from .lru import LRU
|
|
||||||
from bisect import bisect_right
|
from bisect import bisect_right
|
||||||
from gramps.gen.filters import SearchFilter, ExactSearchFilter
|
from gramps.gen.filters import SearchFilter, ExactSearchFilter
|
||||||
|
from .basemodel import BaseModel
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
@ -231,7 +231,7 @@ class NodeMap(object):
|
|||||||
# TreeBaseModel
|
# TreeBaseModel
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel):
|
||||||
"""
|
"""
|
||||||
The base class for all hierarchical treeview models. The model defines the
|
The base class for all hierarchical treeview models. The model defines the
|
||||||
mapping between a unique node and a path. Paths are defined by a tuple.
|
mapping between a unique node and a path. Paths are defined by a tuple.
|
||||||
@ -274,9 +274,6 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
secondary object type.
|
secondary object type.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# LRU cache size
|
|
||||||
_CACHE_SIZE = 250
|
|
||||||
|
|
||||||
def __init__(self, db,
|
def __init__(self, db,
|
||||||
search=None, skip=set(),
|
search=None, skip=set(),
|
||||||
scol=0, order=Gtk.SortType.ASCENDING, sort_map=None,
|
scol=0, order=Gtk.SortType.ASCENDING, sort_map=None,
|
||||||
@ -284,7 +281,8 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
group_can_have_handle = False,
|
group_can_have_handle = False,
|
||||||
has_secondary=False):
|
has_secondary=False):
|
||||||
cput = time.clock()
|
cput = time.clock()
|
||||||
super(TreeBaseModel, self).__init__()
|
GObject.GObject.__init__(self)
|
||||||
|
BaseModel.__init__(self)
|
||||||
#We create a stamp to recognize invalid iterators. From the docs:
|
#We create a stamp to recognize invalid iterators. From the docs:
|
||||||
#Set the stamp to be equal to your model's stamp, to mark the
|
#Set the stamp to be equal to your model's stamp, to mark the
|
||||||
#iterator as valid. When your model's structure changes, you should
|
#iterator as valid. When your model's structure changes, you should
|
||||||
@ -332,8 +330,6 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
|
|
||||||
self._in_build = False
|
self._in_build = False
|
||||||
|
|
||||||
self.lru_data = LRU(TreeBaseModel._CACHE_SIZE)
|
|
||||||
|
|
||||||
self.__total = 0
|
self.__total = 0
|
||||||
self.__displayed = 0
|
self.__displayed = 0
|
||||||
|
|
||||||
@ -350,6 +346,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
"""
|
"""
|
||||||
Unset all elements that prevent garbage collection
|
Unset all elements that prevent garbage collection
|
||||||
"""
|
"""
|
||||||
|
BaseModel.destroy(self)
|
||||||
self.db = None
|
self.db = None
|
||||||
self.sort_func = None
|
self.sort_func = None
|
||||||
if self.has_secondary:
|
if self.has_secondary:
|
||||||
@ -364,8 +361,6 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
self.search2 = None
|
self.search2 = None
|
||||||
self.current_filter = None
|
self.current_filter = None
|
||||||
self.current_filter2 = None
|
self.current_filter2 = None
|
||||||
self.clear_cache()
|
|
||||||
self.lru_data = None
|
|
||||||
|
|
||||||
def _set_base_data(self):
|
def _set_base_data(self):
|
||||||
"""
|
"""
|
||||||
@ -410,22 +405,11 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def clear_cache(self, handle=None):
|
|
||||||
"""
|
|
||||||
Clear the LRU cache.
|
|
||||||
"""
|
|
||||||
if handle:
|
|
||||||
try:
|
|
||||||
del self.lru_data[handle]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.lru_data.clear()
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""
|
"""
|
||||||
Clear the data map.
|
Clear the data map.
|
||||||
"""
|
"""
|
||||||
|
self.clear_cache()
|
||||||
self.tree.clear()
|
self.tree.clear()
|
||||||
self.handle2node.clear()
|
self.handle2node.clear()
|
||||||
self.stamp += 1
|
self.stamp += 1
|
||||||
@ -600,47 +584,22 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
status = progressdlg.LongOpStatus(msg=_("Building View"),
|
status = progressdlg.LongOpStatus(msg=_("Building View"),
|
||||||
total_steps=3, interval=1)
|
total_steps=3, interval=1)
|
||||||
pmon.add_op(status)
|
pmon.add_op(status)
|
||||||
status_ppl = progressdlg.LongOpStatus(msg=_("Obtaining all rows"),
|
status_ppl = progressdlg.LongOpStatus(msg=_("Loading items..."),
|
||||||
total_steps=items, interval=items//10)
|
total_steps=items, interval=items//10)
|
||||||
pmon.add_op(status_ppl)
|
pmon.add_op(status_ppl)
|
||||||
|
|
||||||
self.__total += items
|
self.__total += items
|
||||||
|
|
||||||
def beat(key):
|
|
||||||
status_ppl.heartbeat()
|
|
||||||
# for python3 this returns a byte object, so conversion needed
|
|
||||||
if not isinstance(key, str):
|
|
||||||
key = key.decode('utf-8')
|
|
||||||
return key
|
|
||||||
|
|
||||||
with gen_cursor() as cursor:
|
with gen_cursor() as cursor:
|
||||||
handle_list = [beat(key) for key, data in cursor]
|
for handle, data in cursor:
|
||||||
status_ppl.end()
|
if not isinstance(handle, str):
|
||||||
status.heartbeat()
|
handle = handle.decode('utf-8')
|
||||||
|
status_ppl.heartbeat()
|
||||||
if dfilter:
|
|
||||||
_LOG.debug("rebuild filter %s" % dfilter)
|
|
||||||
_LOG.debug(" list before filter %s" % handle_list)
|
|
||||||
status_filter = progressdlg.LongOpStatus(msg=_("Applying filter"),
|
|
||||||
total_steps=items, interval=items//10)
|
|
||||||
pmon.add_op(status_filter)
|
|
||||||
handle_list = dfilter.apply(self.db, handle_list,
|
|
||||||
cb_progress=status_filter.heartbeat)
|
|
||||||
_LOG.debug(" list after filter %s" % handle_list)
|
|
||||||
status_filter.end()
|
|
||||||
status.heartbeat()
|
|
||||||
|
|
||||||
todisplay = len(handle_list)
|
|
||||||
status_col = progressdlg.LongOpStatus(msg=_("Constructing column data"),
|
|
||||||
total_steps=todisplay, interval=todisplay//10)
|
|
||||||
pmon.add_op(status_col)
|
|
||||||
for handle in handle_list:
|
|
||||||
status_col.heartbeat()
|
|
||||||
data = data_map(handle)
|
|
||||||
if not handle in skip:
|
if not handle in skip:
|
||||||
|
if not dfilter or dfilter.apply(self.db, [handle]):
|
||||||
add_func(handle, data)
|
add_func(handle, data)
|
||||||
self.__displayed += 1
|
self.__displayed += 1
|
||||||
status_col.end()
|
status_ppl.end()
|
||||||
status.end()
|
status.end()
|
||||||
|
|
||||||
def add_node(self, parent, child, sortkey, handle, add_parent=True,
|
def add_node(self, parent, child, sortkey, handle, add_parent=True,
|
||||||
@ -660,6 +619,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
add_parent Bool, if True, check if parent is present, if not add the
|
add_parent Bool, if True, check if parent is present, if not add the
|
||||||
parent as a top group with no handle
|
parent as a top group with no handle
|
||||||
"""
|
"""
|
||||||
|
self.clear_path_cache()
|
||||||
if add_parent and not (parent in self.tree):
|
if add_parent and not (parent in self.tree):
|
||||||
#add parent to self.tree as a node with no handle, as the first
|
#add parent to self.tree as a node with no handle, as the first
|
||||||
#group level
|
#group level
|
||||||
@ -710,6 +670,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
"""
|
"""
|
||||||
Remove a node from the map.
|
Remove a node from the map.
|
||||||
"""
|
"""
|
||||||
|
self.clear_path_cache()
|
||||||
if node.children:
|
if node.children:
|
||||||
del self.handle2node[node.handle]
|
del self.handle2node[node.handle]
|
||||||
node.set_handle(None)
|
node.set_handle(None)
|
||||||
@ -736,6 +697,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
rows_reordered, so to propagate the change to the view, you need to
|
rows_reordered, so to propagate the change to the view, you need to
|
||||||
reattach the model to the view.
|
reattach the model to the view.
|
||||||
"""
|
"""
|
||||||
|
self.clear_path_cache()
|
||||||
self.__reverse = not self.__reverse
|
self.__reverse = not self.__reverse
|
||||||
top_node = self.tree[None]
|
top_node = self.tree[None]
|
||||||
self._reverse_level(top_node)
|
self._reverse_level(top_node)
|
||||||
@ -771,16 +733,17 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
|
|
||||||
def add_row(self, handle, data):
|
def add_row(self, handle, data):
|
||||||
"""
|
"""
|
||||||
Add a row to the model. In general this will add more then one node by
|
Add a row to the model. In general this will add more than one node by
|
||||||
using the add_node method.
|
using the add_node method.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
self.clear_path_cache()
|
||||||
|
|
||||||
def add_row_by_handle(self, handle):
|
def add_row_by_handle(self, handle):
|
||||||
"""
|
"""
|
||||||
Add a row to the model.
|
Add a row to the model.
|
||||||
"""
|
"""
|
||||||
assert isinstance(handle, str)
|
assert isinstance(handle, str)
|
||||||
|
self.clear_path_cache()
|
||||||
if self._get_node(handle) is not None:
|
if self._get_node(handle) is not None:
|
||||||
return # row already exists
|
return # row already exists
|
||||||
cput = time.clock()
|
cput = time.clock()
|
||||||
@ -805,11 +768,11 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
"""
|
"""
|
||||||
assert isinstance(handle, str)
|
assert isinstance(handle, str)
|
||||||
cput = time.clock()
|
cput = time.clock()
|
||||||
|
self.clear_cache(handle)
|
||||||
node = self._get_node(handle)
|
node = self._get_node(handle)
|
||||||
if node is None:
|
if node is None:
|
||||||
return # row not currently displayed
|
return # row not currently displayed
|
||||||
|
|
||||||
self.clear_cache(handle)
|
|
||||||
parent = self.nodemap.node(node.parent)
|
parent = self.nodemap.node(node.parent)
|
||||||
self.remove_node(node)
|
self.remove_node(node)
|
||||||
|
|
||||||
@ -835,6 +798,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
Update a row in the model.
|
Update a row in the model.
|
||||||
"""
|
"""
|
||||||
assert isinstance(handle, str)
|
assert isinstance(handle, str)
|
||||||
|
self.clear_cache(handle)
|
||||||
if self._get_node(handle) is None:
|
if self._get_node(handle) is None:
|
||||||
return # row not currently displayed
|
return # row not currently displayed
|
||||||
|
|
||||||
@ -947,7 +911,9 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
val = self._get_value(node.handle, col, node.secondary)
|
val = self._get_value(node.handle, col, node.secondary)
|
||||||
#GTK 3 should convert unicode objects automatically, but this
|
#GTK 3 should convert unicode objects automatically, but this
|
||||||
# gives wrong column values, so convert for python 2.7
|
# gives wrong column values, so convert for python 2.7
|
||||||
if not isinstance(val, str):
|
if val is None:
|
||||||
|
return ''
|
||||||
|
elif not isinstance(val, str):
|
||||||
return val.encode('utf-8')
|
return val.encode('utf-8')
|
||||||
else:
|
else:
|
||||||
return val
|
return val
|
||||||
@ -959,16 +925,18 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
if secondary is None:
|
if secondary is None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
if handle in self.lru_data:
|
cached, data = self.get_cached_value(handle, col)
|
||||||
data = self.lru_data[handle]
|
|
||||||
else:
|
if not cached:
|
||||||
if not secondary:
|
if not secondary:
|
||||||
data = self.map(handle)
|
data = self.map(handle)
|
||||||
else:
|
else:
|
||||||
data = self.map2(handle)
|
data = self.map2(handle)
|
||||||
if not self._in_build:
|
if store_cache:
|
||||||
self.lru_data[handle] = data
|
self.set_cached_value(handle, col, data)
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
return ''
|
||||||
if not secondary:
|
if not secondary:
|
||||||
# None is used to indicate this column has no data
|
# None is used to indicate this column has no data
|
||||||
if self.fmap[col] is None:
|
if self.fmap[col] is None:
|
||||||
@ -994,7 +962,13 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
pathlist = path.get_indices()
|
pathlist = path.get_indices()
|
||||||
for index in pathlist:
|
for index in pathlist:
|
||||||
_index = (-index - 1) if self.__reverse else index
|
_index = (-index - 1) if self.__reverse else index
|
||||||
|
try:
|
||||||
|
if len(node.children[_index]) > 0:
|
||||||
node = self.nodemap.node(node.children[_index][1])
|
node = self.nodemap.node(node.children[_index][1])
|
||||||
|
else:
|
||||||
|
return False, Gtk.TreeIter()
|
||||||
|
except IndexError:
|
||||||
|
return False, Gtk.TreeIter()
|
||||||
return True, self._get_iter(node)
|
return True, self._get_iter(node)
|
||||||
|
|
||||||
def get_node_from_iter(self, iter):
|
def get_node_from_iter(self, iter):
|
||||||
@ -1002,13 +976,16 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
return self.nodemap.node(iter.user_data)
|
return self.nodemap.node(iter.user_data)
|
||||||
else:
|
else:
|
||||||
print ('Problem', iter, iter.user_data)
|
print ('Problem', iter, iter.user_data)
|
||||||
raise NotImplementedError
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def do_get_path(self, iter):
|
def do_get_path(self, iter):
|
||||||
"""
|
"""
|
||||||
Returns a path from a given node.
|
Returns a path from a given node.
|
||||||
"""
|
"""
|
||||||
|
cached, path = self.get_cached_path(iter.user_data)
|
||||||
|
if cached:
|
||||||
|
(treepath, pathtup) = path
|
||||||
|
return treepath
|
||||||
node = self.get_node_from_iter(iter)
|
node = self.get_node_from_iter(iter)
|
||||||
pathlist = []
|
pathlist = []
|
||||||
while node.parent is not None:
|
while node.parent is not None:
|
||||||
@ -1017,16 +994,33 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
|
|||||||
while node is not None:
|
while node is not None:
|
||||||
# Step backwards
|
# Step backwards
|
||||||
nodeid = node.next if self.__reverse else node.prev
|
nodeid = node.next if self.__reverse else node.prev
|
||||||
|
# Let's see if sibling is cached:
|
||||||
|
cached, sib_path = self.get_cached_path(nodeid)
|
||||||
|
if cached:
|
||||||
|
(sib_treepath, sib_pathtup) = sib_path
|
||||||
|
# Does it have an actual path?
|
||||||
|
if sib_pathtup:
|
||||||
|
# Compute path to here from sibling:
|
||||||
|
# parent_path + sib_path + offset
|
||||||
|
newtup = (sib_pathtup[:-1] +
|
||||||
|
(sib_pathtup[-1] + index + 2, ) +
|
||||||
|
tuple(reversed(pathlist)))
|
||||||
|
#print("computed path:", iter.user_data, newtup)
|
||||||
|
retval = Gtk.TreePath(newtup)
|
||||||
|
self.set_cached_path(iter.user_data, (retval, newtup))
|
||||||
|
return retval
|
||||||
node = nodeid and self.nodemap.node(nodeid)
|
node = nodeid and self.nodemap.node(nodeid)
|
||||||
index += 1
|
index += 1
|
||||||
pathlist.append(index)
|
pathlist.append(index)
|
||||||
node = parent
|
node = parent
|
||||||
|
|
||||||
if pathlist:
|
if pathlist:
|
||||||
pathlist.reverse()
|
pathlist.reverse()
|
||||||
return Gtk.TreePath(tuple(pathlist))
|
#print("actual path :", iter.user_data, tuple(pathlist))
|
||||||
|
retval = Gtk.TreePath(tuple(pathlist))
|
||||||
else:
|
else:
|
||||||
return None
|
retval = None
|
||||||
|
self.set_cached_path(iter.user_data, (retval, tuple(pathlist) if pathlist else None))
|
||||||
|
return retval
|
||||||
|
|
||||||
def do_iter_next(self, iter):
|
def do_iter_next(self, iter):
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user