3275: PageView reworking, changes by B. Malengier and N.Hall
Specifically: improve new treeview by using a linked list implementation so iters can be quickly iterated over Also: progressdialog on long personview loads. svn: r14002
This commit is contained in:
@@ -111,6 +111,13 @@ class CLIDbLoader(object):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _end_progress(self):
|
||||||
|
"""
|
||||||
|
Convenience method to allow to hide the progress bar if wanted at
|
||||||
|
end of load actions. Inherit if needed
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def read_file(self, filename):
|
def read_file(self, filename):
|
||||||
"""
|
"""
|
||||||
This method takes care of changing database, and loading the data.
|
This method takes care of changing database, and loading the data.
|
||||||
|
@@ -89,12 +89,17 @@ class DbLoader(CLIDbLoader):
|
|||||||
DBErrorDialog(str(msg.value))
|
DBErrorDialog(str(msg.value))
|
||||||
|
|
||||||
def _begin_progress(self):
|
def _begin_progress(self):
|
||||||
self.uistate.window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
self.uistate.set_busy_cursor(1)
|
||||||
self.uistate.progress.show()
|
self.uistate.progress.show()
|
||||||
|
self.uistate.pulse_progressbar(0)
|
||||||
|
|
||||||
def _pulse_progress(self, value):
|
def _pulse_progress(self, value):
|
||||||
self.uistate.pulse_progressbar(value)
|
self.uistate.pulse_progressbar(value)
|
||||||
|
|
||||||
|
def _end_progress(self):
|
||||||
|
self.uistate.set_busy_cursor(0)
|
||||||
|
self.uistate.progress.hide()
|
||||||
|
|
||||||
def import_file(self):
|
def import_file(self):
|
||||||
# First thing first: import is a batch transaction
|
# First thing first: import is a batch transaction
|
||||||
# so we will lose the undo history. Warn the user.
|
# so we will lose the undo history. Warn the user.
|
||||||
@@ -222,14 +227,13 @@ class DbLoader(CLIDbLoader):
|
|||||||
def do_import(self, dialog, importer, filename):
|
def do_import(self, dialog, importer, filename):
|
||||||
self.import_info = None
|
self.import_info = None
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
self.uistate.window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
self._begin_progress()
|
||||||
self.uistate.progress.show()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
#an importer can return an object with info, object.info_text()
|
#an importer can return an object with info, object.info_text()
|
||||||
#returns that info. Otherwise None is set to import_info
|
#returns that info. Otherwise None is set to import_info
|
||||||
self.import_info = importer(self.dbstate.db, filename,
|
self.import_info = importer(self.dbstate.db, filename,
|
||||||
self.uistate.pulse_progressbar)
|
self._pulse_progress)
|
||||||
dirname = os.path.dirname(filename) + os.path.sep
|
dirname = os.path.dirname(filename) + os.path.sep
|
||||||
config.set('paths.recent-import-dir', dirname)
|
config.set('paths.recent-import-dir', dirname)
|
||||||
except UnicodeError, msg:
|
except UnicodeError, msg:
|
||||||
@@ -240,6 +244,7 @@ class DbLoader(CLIDbLoader):
|
|||||||
"encoding, and import again") + "\n\n %s" % msg)
|
"encoding, and import again") + "\n\n %s" % msg)
|
||||||
except Exception:
|
except Exception:
|
||||||
_LOG.error("Failed to import database.", exc_info=True)
|
_LOG.error("Failed to import database.", exc_info=True)
|
||||||
|
self._end_progress()
|
||||||
|
|
||||||
def import_info_text(self):
|
def import_info_text(self):
|
||||||
"""
|
"""
|
||||||
@@ -308,6 +313,7 @@ class DbLoader(CLIDbLoader):
|
|||||||
self._dberrordialog(msg)
|
self._dberrordialog(msg)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.dbstate.no_database()
|
self.dbstate.no_database()
|
||||||
|
self._end_progress()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
@@ -1230,7 +1230,6 @@ class ViewManager(CLIManager):
|
|||||||
self.uistate.clear_history(self.dbstate.active.handle)
|
self.uistate.clear_history(self.dbstate.active.handle)
|
||||||
else :
|
else :
|
||||||
self.uistate.clear_history(None)
|
self.uistate.clear_history(None)
|
||||||
self.uistate.progress.hide()
|
|
||||||
|
|
||||||
self.dbstate.db.undo_callback = self.__change_undo_label
|
self.dbstate.db.undo_callback = self.__change_undo_label
|
||||||
self.dbstate.db.redo_callback = self.__change_redo_label
|
self.dbstate.db.redo_callback = self.__change_redo_label
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
# Copyright (C) 2001-2007 Donald N. Allingham
|
# Copyright (C) 2001-2007 Donald N. Allingham
|
||||||
# Copyright (C) 2009 Nick Hall
|
# Copyright (C) 2009 Nick Hall
|
||||||
|
# Copyright (C) 2009 Benny Malengier
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -532,6 +533,8 @@ class ListView(NavigationView):
|
|||||||
obj A TreeViewColumn object of the column clicked
|
obj A TreeViewColumn object of the column clicked
|
||||||
data The column index
|
data The column index
|
||||||
"""
|
"""
|
||||||
|
self.uistate.set_busy_cursor(1)
|
||||||
|
self.uistate.push_message(self.dbstate, _("Column clicked, sorting..."))
|
||||||
cput = time.clock()
|
cput = time.clock()
|
||||||
same_col = False
|
same_col = False
|
||||||
if self.sort_col != data:
|
if self.sort_col != data:
|
||||||
@@ -558,6 +561,7 @@ class ListView(NavigationView):
|
|||||||
filter_info = (False, value, False)
|
filter_info = (False, value, False)
|
||||||
|
|
||||||
if same_col:
|
if same_col:
|
||||||
|
self.list.set_model(None)
|
||||||
self.model.reverse_order()
|
self.model.reverse_order()
|
||||||
else:
|
else:
|
||||||
self.model = self.make_model(self.dbstate.db, self.sort_col,
|
self.model = self.make_model(self.dbstate.db, self.sort_col,
|
||||||
@@ -575,6 +579,8 @@ class ListView(NavigationView):
|
|||||||
search_col = self.column_order()[data][1]
|
search_col = self.column_order()[data][1]
|
||||||
self.list.set_search_column(search_col)
|
self.list.set_search_column(search_col)
|
||||||
|
|
||||||
|
self.uistate.set_busy_cursor(0)
|
||||||
|
|
||||||
_LOG.debug(' ' + self.__class__.__name__ + ' column_clicked ' +
|
_LOG.debug(' ' + self.__class__.__name__ + ' column_clicked ' +
|
||||||
str(time.clock() - cput) + ' sec')
|
str(time.clock() - cput) + ' sec')
|
||||||
|
|
||||||
|
@@ -105,15 +105,16 @@ class PeopleModel(TreeBaseModel):
|
|||||||
"""
|
"""
|
||||||
Initialize the model building the initial data
|
Initialize the model building the initial data
|
||||||
"""
|
"""
|
||||||
self.lru_name = LRU(TreeBaseModel._CACHE_SIZE)
|
TreeBaseModel.__init__(self, db, search=search, skip=skip,
|
||||||
self.lru_bdate = LRU(TreeBaseModel._CACHE_SIZE)
|
tooltip_column=11, marker_column=10,
|
||||||
self.lru_ddate = LRU(TreeBaseModel._CACHE_SIZE)
|
scol=scol, order=order, sort_map=sort_map)
|
||||||
|
|
||||||
self.gen_cursor = db.get_person_cursor
|
def _set_base_data(self):
|
||||||
self.map = db.get_raw_person_data
|
"""See TreeBaseModel, we also set some extra lru caches
|
||||||
self.scol = scol
|
"""
|
||||||
|
self.gen_cursor = self.db.get_person_cursor
|
||||||
#self.group_list = []
|
self.number_items = self.db.get_number_of_people
|
||||||
|
self.map = self.db.get_raw_person_data
|
||||||
|
|
||||||
self.fmap = [
|
self.fmap = [
|
||||||
self.column_name,
|
self.column_name,
|
||||||
@@ -146,9 +147,11 @@ class PeopleModel(TreeBaseModel):
|
|||||||
self.column_int_id,
|
self.column_int_id,
|
||||||
]
|
]
|
||||||
self.hmap = [self.column_header] + [None]*len(self.smap)
|
self.hmap = [self.column_header] + [None]*len(self.smap)
|
||||||
TreeBaseModel.__init__(self, db, search=search, skip=skip,
|
|
||||||
tooltip_column=11, marker_column=10,
|
self.lru_name = LRU(TreeBaseModel._CACHE_SIZE)
|
||||||
scol=scol, order=order, sort_map=sort_map)
|
self.lru_bdate = LRU(TreeBaseModel._CACHE_SIZE)
|
||||||
|
self.lru_ddate = LRU(TreeBaseModel._CACHE_SIZE)
|
||||||
|
|
||||||
def clear_cache(self):
|
def clear_cache(self):
|
||||||
""" Clear the LRU cache """
|
""" Clear the LRU cache """
|
||||||
TreeBaseModel.clear_cache(self)
|
TreeBaseModel.clear_cache(self)
|
||||||
@@ -444,7 +447,7 @@ class PeopleModel(TreeBaseModel):
|
|||||||
return data[0]
|
return data[0]
|
||||||
|
|
||||||
def column_header(self, node):
|
def column_header(self, node):
|
||||||
return node
|
return node.name
|
||||||
|
|
||||||
def column_header_view(self, node):
|
def column_header_view(self, node):
|
||||||
return True
|
return True
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
# Gramps - a GTK+/GNOME based genealogy program
|
# Gramps - a GTK+/GNOME based genealogy program
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 Nick Hall
|
# Copyright (C) 2009 Nick Hall
|
||||||
|
# Copyright (C) 2009 Benny Malengier
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -64,8 +65,6 @@ class PlaceTreeModel(PlaceBaseModel, TreeBaseModel):
|
|||||||
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
|
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
|
||||||
skip=set(), sort_map=None):
|
skip=set(), sort_map=None):
|
||||||
|
|
||||||
self.hmap = [self.column_header] + [None]*12
|
|
||||||
|
|
||||||
PlaceBaseModel.__init__(self, db)
|
PlaceBaseModel.__init__(self, db)
|
||||||
TreeBaseModel.__init__(self, db, scol=scol, order=order,
|
TreeBaseModel.__init__(self, db, scol=scol, order=order,
|
||||||
tooltip_column=13,
|
tooltip_column=13,
|
||||||
@@ -73,6 +72,13 @@ class PlaceTreeModel(PlaceBaseModel, TreeBaseModel):
|
|||||||
nrgroups = 3,
|
nrgroups = 3,
|
||||||
group_can_have_handle = True)
|
group_can_have_handle = True)
|
||||||
|
|
||||||
|
def _set_base_data(self):
|
||||||
|
"""See TreeBaseModel, for place, most have been set in init of
|
||||||
|
PlaceBaseModel
|
||||||
|
"""
|
||||||
|
self.number_items = self.db.get_number_of_places
|
||||||
|
self.hmap = [self.column_header] + [None]*12
|
||||||
|
|
||||||
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.
|
||||||
@@ -134,4 +140,4 @@ class PlaceTreeModel(PlaceBaseModel, TreeBaseModel):
|
|||||||
Return a column heading. This is called for nodes with no associated
|
Return a column heading. This is called for nodes with no associated
|
||||||
Gramps handle.
|
Gramps handle.
|
||||||
"""
|
"""
|
||||||
return node[0]
|
return node.name
|
||||||
|
@@ -33,6 +33,7 @@ This module provides the model that is used for all hierarchical treeviews.
|
|||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
import time
|
import time
|
||||||
import locale
|
import locale
|
||||||
|
from gettext import gettext as _
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
_LOG = logging.getLogger(".gui.treebasemodel")
|
_LOG = logging.getLogger(".gui.treebasemodel")
|
||||||
@@ -51,11 +52,162 @@ import gtk
|
|||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
import config
|
import config
|
||||||
from Utils import conv_unicode_tosrtkey_ongtk
|
from Utils import conv_unicode_tosrtkey_ongtk
|
||||||
from gui.widgets.progressdialog import LongOpStatus
|
import gui.widgets.progressdialog as progressdlg
|
||||||
from Lru import LRU
|
from Lru import LRU
|
||||||
from bisect import bisect_right
|
from bisect import bisect_right
|
||||||
from Filters import SearchFilter, ExactSearchFilter
|
from Filters import SearchFilter, ExactSearchFilter
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Node
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
class Node(object):
|
||||||
|
"""
|
||||||
|
This class defines an individual node of a tree in the model. The node
|
||||||
|
stores the following data:
|
||||||
|
|
||||||
|
name Textual description of the node.
|
||||||
|
sortkey A key which defines the sort order of the node.
|
||||||
|
ref Reference to this node in the tree dictionary.
|
||||||
|
handle A Gramps handle. Can be None if no Gramps object is
|
||||||
|
associated with the node.
|
||||||
|
parent id of the parent node.
|
||||||
|
prev Link to the previous sibling via id.
|
||||||
|
next Link to the next sibling via id.
|
||||||
|
|
||||||
|
children A list of (sortkey, nodeid) tuples for the children of the node.
|
||||||
|
This list is always kept sorted.
|
||||||
|
"""
|
||||||
|
__slots__ = ('name', 'sortkey', 'ref', 'handle', 'parent', 'prev',
|
||||||
|
'next', 'children')#, '__weakref__')
|
||||||
|
|
||||||
|
def __init__(self, ref, parent, sortkey, handle):
|
||||||
|
self.name = sortkey
|
||||||
|
if sortkey:
|
||||||
|
self.sortkey = conv_unicode_tosrtkey_ongtk(sortkey)
|
||||||
|
else:
|
||||||
|
self.sortkey = None
|
||||||
|
self.ref = ref
|
||||||
|
self.handle = handle
|
||||||
|
self.parent = parent
|
||||||
|
self.prev = None
|
||||||
|
self.next = None
|
||||||
|
self.children = []
|
||||||
|
|
||||||
|
def set_handle(self, handle):
|
||||||
|
"""
|
||||||
|
Assign the handle of a Gramps object to this node.
|
||||||
|
"""
|
||||||
|
if not self.handle:
|
||||||
|
self.handle = handle
|
||||||
|
else:
|
||||||
|
raise ValueError, 'attempt to add twice a node to the model'
|
||||||
|
|
||||||
|
def add_child(self, node, nodemap):
|
||||||
|
"""
|
||||||
|
Add a node to the list of children for this node using the id's in
|
||||||
|
nodemap.
|
||||||
|
"""
|
||||||
|
nodeid = id(node)
|
||||||
|
if len(self.children):
|
||||||
|
index = bisect_right(self.children, (node.sortkey, nodeid))
|
||||||
|
if index == 0:
|
||||||
|
node.prev = None
|
||||||
|
next_nodeid = self.children[0][1]
|
||||||
|
next_node = nodemap.node(next_nodeid)
|
||||||
|
next_node.prev = nodeid
|
||||||
|
node.next = next_nodeid
|
||||||
|
elif index == len(self.children):
|
||||||
|
prev_nodeid = self.children[-1][1]
|
||||||
|
prev_node = nodemap.node(prev_nodeid)
|
||||||
|
prev_node.next = nodeid
|
||||||
|
node.prev = prev_nodeid
|
||||||
|
node.next = None
|
||||||
|
else:
|
||||||
|
prev_nodeid = self.children[index - 1][1]
|
||||||
|
next_nodeid = self.children[index][1]
|
||||||
|
prev_node = nodemap.node(prev_nodeid)
|
||||||
|
next_node = nodemap.node(next_nodeid)
|
||||||
|
prev_node.next = nodeid
|
||||||
|
next_node.prev = nodeid
|
||||||
|
node.prev = prev_nodeid
|
||||||
|
node.next = next_nodeid
|
||||||
|
|
||||||
|
self.children.insert(index, (node.sortkey, nodeid))
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.children.append((node.sortkey, nodeid))
|
||||||
|
|
||||||
|
def remove_child(self, node, nodemap):
|
||||||
|
"""
|
||||||
|
Remove a node from the list of children for this node, using nodemap.
|
||||||
|
"""
|
||||||
|
nodeid = id(node)
|
||||||
|
index = bisect_right(self.children, (node.sortkey, nodeid)) - 1
|
||||||
|
if not (self.children[index] == (node.sortkey, nodeid)):
|
||||||
|
raise ValueError, str(node.name) + \
|
||||||
|
' not present in self.children: ' + str(self.children)\
|
||||||
|
+ ' at index ' + str(index)
|
||||||
|
if index == 0:
|
||||||
|
nodemap.node(self.children[index][1]).prev = None
|
||||||
|
elif index == len(self.children)-1:
|
||||||
|
nodemap.node(self.children[index - 1][1]).next = None
|
||||||
|
else:
|
||||||
|
nodemap.node(self.children[index - 1][1]).next = \
|
||||||
|
self.children[index + 1][1]
|
||||||
|
nodemap.node(self.children[index + 1][1]).prev = \
|
||||||
|
self.children[index - 1][1]
|
||||||
|
|
||||||
|
self.children.pop(index)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# NodeMap
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
class NodeMap(object):
|
||||||
|
"""
|
||||||
|
Map of id of Node classes to real object
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.id2node = {}
|
||||||
|
|
||||||
|
def add_node(self, node):
|
||||||
|
"""
|
||||||
|
Add a Node object to the map and return id of this node
|
||||||
|
"""
|
||||||
|
nodeid = id(node)
|
||||||
|
self.id2node[nodeid] = node
|
||||||
|
return nodeid
|
||||||
|
|
||||||
|
def del_node(self, node):
|
||||||
|
"""
|
||||||
|
Remove a Node object from the map and return nodeid
|
||||||
|
"""
|
||||||
|
nodeid = id(node)
|
||||||
|
del self.id2node[nodeid]
|
||||||
|
return nodeid
|
||||||
|
|
||||||
|
def del_nodeid(self, nodeid):
|
||||||
|
"""
|
||||||
|
Remove Node with id nodeid from the map
|
||||||
|
"""
|
||||||
|
del self.id2node[nodeid]
|
||||||
|
|
||||||
|
def node(self, nodeid):
|
||||||
|
"""
|
||||||
|
Obtain the node object from it's id
|
||||||
|
"""
|
||||||
|
return self.id2node[nodeid]
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
"""
|
||||||
|
clear the map
|
||||||
|
"""
|
||||||
|
self.id2node.clear()
|
||||||
|
self.id2node = {}
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# TreeBaseModel
|
# TreeBaseModel
|
||||||
@@ -72,14 +224,11 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
|
|
||||||
The following data is stored:
|
The following data is stored:
|
||||||
|
|
||||||
tree A dictionary of unique nodes. Each entry is a list
|
tree A dictionary of unique identifiers which correspond to nodes in
|
||||||
containing the parent node and gramps handle. The handle
|
the hierarchy. Each entry is a node object.
|
||||||
is set to None if no gramps object is associated with the
|
handle2node A dictionary of gramps handles. Each entry is a node object.
|
||||||
node.
|
nodemap A NodeMap, mapping id's of the nodes to the node objects. Node
|
||||||
children A dictionary of parent nodes. Each entry is a list of
|
refer to other notes via id's in a linked list form.
|
||||||
(sortkey, child) tuples. The list is sorted during the
|
|
||||||
build. The top node of the hierarchy is None.
|
|
||||||
handle2node A dictionary of gramps handles. Each entry is a node.
|
|
||||||
|
|
||||||
The model obtains data from database as needed and holds a cache of most
|
The model obtains data from database as needed and holds a cache of most
|
||||||
recently used data.
|
recently used data.
|
||||||
@@ -117,17 +266,20 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
cput = time.clock()
|
cput = time.clock()
|
||||||
gtk.GenericTreeModel.__init__(self)
|
gtk.GenericTreeModel.__init__(self)
|
||||||
|
|
||||||
# Initialise data structures
|
|
||||||
self.tree = {}
|
|
||||||
self.children = {}
|
|
||||||
self.children[None] = []
|
|
||||||
self.handle2node = {}
|
|
||||||
self.__reverse = (order == gtk.SORT_DESCENDING)
|
self.__reverse = (order == gtk.SORT_DESCENDING)
|
||||||
|
self.scol = scol
|
||||||
self.nrgroups = nrgroups
|
self.nrgroups = nrgroups
|
||||||
self.group_can_have_handle = group_can_have_handle
|
self.group_can_have_handle = group_can_have_handle
|
||||||
|
self.db = db
|
||||||
|
|
||||||
|
self._set_base_data()
|
||||||
|
|
||||||
|
# Initialise data structures
|
||||||
|
self.tree = {}
|
||||||
|
self.nodemap = NodeMap()
|
||||||
|
self.handle2node = {}
|
||||||
|
|
||||||
self.set_property("leak_references", False)
|
self.set_property("leak_references", False)
|
||||||
self.db = db
|
|
||||||
#normally sort on first column, so scol=0
|
#normally sort on first column, so scol=0
|
||||||
if sort_map:
|
if sort_map:
|
||||||
#sort_map is the stored order of the columns and if they are
|
#sort_map is the stored order of the columns and if they are
|
||||||
@@ -168,6 +320,26 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
_LOG.debug(self.__class__.__name__ + ' __init__ ' +
|
_LOG.debug(self.__class__.__name__ + ' __init__ ' +
|
||||||
str(time.clock() - cput) + ' sec')
|
str(time.clock() - cput) + ' sec')
|
||||||
|
|
||||||
|
def _set_base_data(self):
|
||||||
|
"""
|
||||||
|
This method must be overwritten in the inheriting class, setting
|
||||||
|
all needed information
|
||||||
|
|
||||||
|
gen_cursor : func to create cursor to loop over objects in model
|
||||||
|
number_items : func to obtain number of items that are shown if all
|
||||||
|
shown
|
||||||
|
map : function to obtain the raw bsddb object datamap
|
||||||
|
smap : the map with functions to obtain sort value based on sort col
|
||||||
|
fmap : the map with functions to obtain value of a row with handle
|
||||||
|
hmap : the map with functions to obtain value of a row without handle
|
||||||
|
"""
|
||||||
|
self.gen_cursor = None
|
||||||
|
self.number_items = None # function
|
||||||
|
self.map = None
|
||||||
|
|
||||||
|
self.smap = None
|
||||||
|
self.fmap = None
|
||||||
|
self.hmap = None
|
||||||
|
|
||||||
def __update_todo(self, *args):
|
def __update_todo(self, *args):
|
||||||
"""
|
"""
|
||||||
@@ -221,11 +393,18 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
"""
|
"""
|
||||||
Clear the data map.
|
Clear the data map.
|
||||||
"""
|
"""
|
||||||
|
#invalidate the iters within gtk
|
||||||
|
self.invalidate_iters()
|
||||||
|
self.tree.clear()
|
||||||
self.tree = {}
|
self.tree = {}
|
||||||
self.children = {}
|
self.handle2node.clear()
|
||||||
self.children[None] = []
|
|
||||||
self.handle2node = {}
|
self.handle2node = {}
|
||||||
self.__reverse = False
|
self.nodemap.clear()
|
||||||
|
self.nodemap = NodeMap()
|
||||||
|
#start with creating the new iters
|
||||||
|
topnode = Node(None, None, None, None)
|
||||||
|
self.nodemap.add_node(topnode)
|
||||||
|
self.tree[None] = topnode
|
||||||
|
|
||||||
def set_search(self, search):
|
def set_search(self, search):
|
||||||
"""
|
"""
|
||||||
@@ -275,7 +454,6 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
|
|
||||||
self.clear()
|
self.clear()
|
||||||
self._build_data(self.current_filter, skip)
|
self._build_data(self.current_filter, skip)
|
||||||
self.sort_data()
|
|
||||||
|
|
||||||
self._in_build = False
|
self._in_build = False
|
||||||
|
|
||||||
@@ -290,37 +468,71 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
"""
|
"""
|
||||||
self.__total = 0
|
self.__total = 0
|
||||||
self.__displayed = 0
|
self.__displayed = 0
|
||||||
|
|
||||||
|
items = self.number_items()
|
||||||
|
pmon = progressdlg.ProgressMonitor(progressdlg.GtkProgressDialog,
|
||||||
|
popup_time=2)
|
||||||
|
status = progressdlg.LongOpStatus(msg=_("Building People View"),
|
||||||
|
total_steps=items, interval=items//20,
|
||||||
|
can_cancel=True)
|
||||||
|
pmon.add_op(status)
|
||||||
with self.gen_cursor() as cursor:
|
with self.gen_cursor() as cursor:
|
||||||
for handle, data in cursor:
|
for handle, data in cursor:
|
||||||
|
status.heartbeat()
|
||||||
|
if status.should_cancel():
|
||||||
|
break
|
||||||
self.__total += 1
|
self.__total += 1
|
||||||
if not (handle in skip or (dfilter and not
|
if not (handle in skip or (dfilter and not
|
||||||
dfilter.match(handle, self.db))):
|
dfilter.match(handle, self.db))):
|
||||||
self.__displayed += 1
|
self.__displayed += 1
|
||||||
self.add_row(handle, data)
|
self.add_row(handle, data)
|
||||||
|
if not status.was_cancelled():
|
||||||
|
status.end()
|
||||||
|
|
||||||
def _rebuild_filter(self, dfilter, skip):
|
def _rebuild_filter(self, dfilter, skip):
|
||||||
"""
|
"""
|
||||||
Rebuild the data map where a filter is applied.
|
Rebuild the data map where a filter is applied.
|
||||||
"""
|
"""
|
||||||
|
pmon = progressdlg.ProgressMonitor(progressdlg.GtkProgressDialog,
|
||||||
|
popup_time=2)
|
||||||
|
status = progressdlg.LongOpStatus(msg=_("Building People View"),
|
||||||
|
total_steps=3, interval=1)
|
||||||
|
pmon.add_op(status)
|
||||||
|
self.__total = self.number_items()
|
||||||
|
status_ppl = progressdlg.LongOpStatus(msg=_("Obtaining all people"),
|
||||||
|
total_steps=self.__total, interval=self.__total//10)
|
||||||
|
pmon.add_op(status_ppl)
|
||||||
|
|
||||||
|
def beat(key):
|
||||||
|
status_ppl.heartbeat()
|
||||||
|
return key
|
||||||
|
|
||||||
with self.gen_cursor() as cursor:
|
with self.gen_cursor() as cursor:
|
||||||
handle_list = [key for key, data in cursor]
|
handle_list = [beat(key) for key, data in cursor]
|
||||||
self.__total = len(handle_list)
|
status_ppl.end()
|
||||||
|
self.__displayed = 0
|
||||||
|
status.heartbeat()
|
||||||
|
|
||||||
if dfilter:
|
if dfilter:
|
||||||
handle_list = dfilter.apply(self.db, handle_list)
|
status_filter = progressdlg.LongOpStatus(msg=_("Applying filter"),
|
||||||
self.__displayed = len(handle_list)
|
total_steps=self.__total, interval=self.__total//10)
|
||||||
else:
|
pmon.add_op(status_filter)
|
||||||
self.__displayed = self.db.get_number_of_people()
|
handle_list = dfilter.apply(self.db, handle_list,
|
||||||
|
progress=status_filter)
|
||||||
status = LongOpStatus(msg="Loading People",
|
status_filter.end()
|
||||||
total_steps=self.__displayed,
|
|
||||||
interval=self.__displayed//10)
|
|
||||||
self.db.emit('long-op-start', (status,))
|
|
||||||
for handle in handle_list:
|
|
||||||
status.heartbeat()
|
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 = self.map(handle)
|
data = self.map(handle)
|
||||||
if not handle in skip:
|
if not handle in skip:
|
||||||
self.add_row(handle, data)
|
self.add_row(handle, data)
|
||||||
|
self.__displayed += 1
|
||||||
|
status_col.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):
|
||||||
@@ -339,35 +551,31 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
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
|
||||||
"""
|
"""
|
||||||
sortkey = conv_unicode_tosrtkey_ongtk(sortkey)
|
|
||||||
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
|
||||||
self.add_node(None, parent, parent, None, add_parent=False)
|
self.add_node(None, parent, parent, None, add_parent=False)
|
||||||
if child in self.tree:
|
if child in self.tree:
|
||||||
#a node is added that is already present,
|
#a node is added that is already present,
|
||||||
self._add_dup_node(parent, child, sortkey, handle)
|
child_node = self.tree[child]
|
||||||
|
self._add_dup_node(child_node, parent, child, sortkey, handle)
|
||||||
else:
|
else:
|
||||||
self.tree[child] = [parent, handle]
|
parent_node = self.tree[parent]
|
||||||
if parent in self.children:
|
child_node = Node(child, id(parent_node), sortkey, handle)
|
||||||
if self._in_build:
|
parent_node.add_child(child_node, self.nodemap)
|
||||||
self.children[parent].append((sortkey, child))
|
self.tree[child] = child_node
|
||||||
else:
|
self.nodemap.add_node(child_node)
|
||||||
index = bisect_right(self.children[parent], (sortkey, child))
|
|
||||||
self.children[parent].insert(index, (sortkey, child))
|
|
||||||
else:
|
|
||||||
self.children[parent] = [(sortkey, child)]
|
|
||||||
|
|
||||||
if not self._in_build:
|
if not self._in_build:
|
||||||
# emit row_inserted signal
|
# emit row_inserted signal
|
||||||
path = self.on_get_path(child)
|
path = self.on_get_path(child_node)
|
||||||
node = self.get_iter(path)
|
node = self.get_iter(path)
|
||||||
self.row_inserted(path, node)
|
self.row_inserted(path, node)
|
||||||
|
|
||||||
if handle:
|
if handle:
|
||||||
self.handle2node[handle] = child
|
self.handle2node[handle] = child_node
|
||||||
|
|
||||||
def _add_dup_node(self, parent, child, sortkey, handle):
|
def _add_dup_node(self, node, parent, child, sortkey, handle):
|
||||||
"""
|
"""
|
||||||
How to handle adding a node a second time
|
How to handle adding a node a second time
|
||||||
Default: if group nodes can have handles, it is allowed to add it
|
Default: if group nodes can have handles, it is allowed to add it
|
||||||
@@ -377,67 +585,51 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
if not self.group_can_have_handle:
|
if not self.group_can_have_handle:
|
||||||
raise ValueError, 'attempt to add twice a node to the model %s' % \
|
raise ValueError, 'attempt to add twice a node to the model %s' % \
|
||||||
str(parent) + ' ' + str(child) + ' ' + sortkey
|
str(parent) + ' ' + str(child) + ' ' + sortkey
|
||||||
present_val = self.tree[child]
|
if handle:
|
||||||
if handle and present_val[1] is None:
|
node.set_handle(handle)
|
||||||
self.tree[child][1] = handle
|
|
||||||
elif handle is None:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
#handle given, and present handle is not None
|
|
||||||
raise ValueError, 'attempt to add twice a node to the model'
|
|
||||||
|
|
||||||
def sort_data(self):
|
|
||||||
"""
|
|
||||||
Sort the data in the map according to the value of the sort key.
|
|
||||||
"""
|
|
||||||
for node in self.children:
|
|
||||||
self.children[node].sort()
|
|
||||||
|
|
||||||
def remove_node(self, node):
|
def remove_node(self, node):
|
||||||
"""
|
"""
|
||||||
Remove a node from the map.
|
Remove a node from the map.
|
||||||
"""
|
"""
|
||||||
if node in self.children:
|
if node.children:
|
||||||
self.tree[node][1] = None
|
node.set_handle(None)
|
||||||
else:
|
else:
|
||||||
path = self.on_get_path(node)
|
path = self.on_get_path(node)
|
||||||
parent = self.tree[node][0]
|
self.nodemap.node(node.parent).remove_child(node, self.nodemap)
|
||||||
del self.tree[node]
|
del self.tree[node.ref]
|
||||||
new_list = [child for child in self.children[parent]
|
self.nodemap.del_node(node)
|
||||||
if child[1] != node]
|
del node
|
||||||
if not new_list:
|
|
||||||
del self.children[parent]
|
|
||||||
else:
|
|
||||||
self.children[parent] = new_list
|
|
||||||
|
|
||||||
# emit row_deleted signal
|
# emit row_deleted signal
|
||||||
self.row_deleted(path)
|
self.row_deleted(path)
|
||||||
|
|
||||||
def reverse_order(self):
|
def reverse_order(self):
|
||||||
"""
|
"""
|
||||||
Reverse the order of the map.
|
Reverse the order of the map. This does not signal rows_reordered,
|
||||||
|
so to propagate the change to the view, you need to reattach the
|
||||||
|
model to the view.
|
||||||
"""
|
"""
|
||||||
cput = time.clock()
|
|
||||||
self.__reverse = not self.__reverse
|
self.__reverse = not self.__reverse
|
||||||
self._reverse_level(None)
|
|
||||||
_LOG.debug(self.__class__.__name__ + ' reverse_order ' +
|
|
||||||
str(time.clock() - cput) + ' sec')
|
|
||||||
|
|
||||||
def _reverse_level(self, node):
|
def _reverse_level(self, node):
|
||||||
"""
|
"""
|
||||||
Reverse the order of a single level in the map.
|
Reverse the order of a single level in the map and signal
|
||||||
|
rows_reordered so the view is updated.
|
||||||
|
If many changes are done, it is better to detach the model, do the
|
||||||
|
changes to reverse the level, and reattach the model, so the view
|
||||||
|
does not update for every change signal.
|
||||||
"""
|
"""
|
||||||
if node in self.children:
|
if node.children:
|
||||||
rows = range(len(self.children[node]))
|
rows = range(len(node.children)-1,-1,-1)
|
||||||
rows.reverse()
|
if node.parent is None:
|
||||||
if node is None:
|
|
||||||
path = iter = None
|
path = iter = None
|
||||||
else:
|
else:
|
||||||
path = self.on_get_path(node)
|
path = self.on_get_path(node)
|
||||||
iter = self.get_iter(path)
|
iter = self.get_iter(path)
|
||||||
self.rows_reordered(path, iter, rows)
|
self.rows_reordered(path, iter, rows)
|
||||||
for child in self.children[node]:
|
for child in node.children:
|
||||||
self._reverse_level(child[1])
|
self._reverse_level(self.nodemap.node(child[1]))
|
||||||
|
|
||||||
def get_tree_levels(self):
|
def get_tree_levels(self):
|
||||||
"""
|
"""
|
||||||
@@ -447,7 +639,8 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
|
|
||||||
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.
|
Add a row to the model. In general this will add more then one node by
|
||||||
|
using the add_node method.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@@ -470,13 +663,14 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
self.clear_cache()
|
self.clear_cache()
|
||||||
|
|
||||||
node = self.get_node(handle)
|
node = self.get_node(handle)
|
||||||
parent = self.on_iter_parent(node)
|
parent = self.nodemap.node(node.parent)
|
||||||
self.remove_node(node)
|
self.remove_node(node)
|
||||||
|
|
||||||
while parent is not None:
|
while parent is not None:
|
||||||
next_parent = self.on_iter_parent(parent)
|
next_parent = self.nodemap.node(parent.parent) \
|
||||||
if parent not in self.children:
|
if parent.parent is not None else None
|
||||||
if self.tree[parent][1]:
|
if not parent.children:
|
||||||
|
if parent.handle:
|
||||||
# emit row_has_child_toggled signal
|
# emit row_has_child_toggled signal
|
||||||
path = self.on_get_path(parent)
|
path = self.on_get_path(parent)
|
||||||
node = self.get_iter(path)
|
node = self.get_iter(path)
|
||||||
@@ -503,10 +697,7 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
Get the gramps handle for a node. Return None if the node does
|
Get the gramps handle for a node. Return None if the node does
|
||||||
not correspond to a gramps object.
|
not correspond to a gramps object.
|
||||||
"""
|
"""
|
||||||
ret = self.tree.get(node)
|
return node.handle
|
||||||
if ret:
|
|
||||||
return ret[1]
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def get_node(self, handle):
|
def get_node(self, handle):
|
||||||
"""
|
"""
|
||||||
@@ -537,12 +728,14 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
return object
|
return object
|
||||||
return str
|
return str
|
||||||
|
|
||||||
def on_get_value(self, node, col):
|
def on_get_value(self, nodeid, col):
|
||||||
"""
|
"""
|
||||||
See gtk.GenericTreeModel
|
See gtk.GenericTreeModel
|
||||||
"""
|
"""
|
||||||
handle = self.get_handle(node)
|
#print 'get_value', nodeid, col
|
||||||
if handle is None:
|
nodeid = id(nodeid)
|
||||||
|
node = self.nodemap.node(nodeid)
|
||||||
|
if node.handle is None:
|
||||||
# Header rows dont get the foreground color set
|
# Header rows dont get the foreground color set
|
||||||
if col == self._marker_column:
|
if col == self._marker_column:
|
||||||
return None
|
return None
|
||||||
@@ -557,7 +750,7 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
else:
|
else:
|
||||||
# return values for 'data' row, calling a function
|
# return values for 'data' row, calling a function
|
||||||
# according to column_defs table
|
# according to column_defs table
|
||||||
return self._get_value(handle, col)
|
return self._get_value(node.handle, col)
|
||||||
|
|
||||||
def _get_value(self, handle, col):
|
def _get_value(self, handle, col):
|
||||||
"""
|
"""
|
||||||
@@ -578,111 +771,118 @@ class TreeBaseModel(gtk.GenericTreeModel):
|
|||||||
"""
|
"""
|
||||||
Returns a node from a given path.
|
Returns a node from a given path.
|
||||||
"""
|
"""
|
||||||
if not self.tree:
|
if not self.tree or not self.tree[None].children:
|
||||||
return None
|
return None
|
||||||
node = None
|
node = self.tree[None]
|
||||||
pathlist = list(path)
|
pathlist = list(path)
|
||||||
for index in pathlist:
|
for index in pathlist:
|
||||||
if self.__reverse:
|
if self.__reverse:
|
||||||
size = len(self.children[node])
|
size = len(node.children)
|
||||||
node = self.children[node][size - index - 1][1]
|
node = self.nodemap.node(node.children[size - index - 1][1])
|
||||||
else:
|
else:
|
||||||
node = self.children[node][index][1]
|
node = self.nodemap.node(node.children[index][1])
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def on_get_path(self, node):
|
def on_get_path(self, nodeid):
|
||||||
"""
|
"""
|
||||||
Returns a path from a given node.
|
Returns a path from a given node.
|
||||||
"""
|
"""
|
||||||
|
nodeid = id(nodeid)
|
||||||
|
node = self.nodemap.node(nodeid)
|
||||||
pathlist = []
|
pathlist = []
|
||||||
|
while node.parent is not None:
|
||||||
|
parent = self.nodemap.node(node.parent)
|
||||||
|
index = -1
|
||||||
while node is not None:
|
while node is not None:
|
||||||
parent = self.tree[node][0]
|
# Step backwards
|
||||||
for index, value in enumerate(self.children[parent]):
|
nodeid = node.next if self.__reverse else node.prev
|
||||||
if value[1] == node:
|
node = self.nodemap.node(nodeid) if nodeid is not None else \
|
||||||
break
|
None
|
||||||
if self.__reverse:
|
index += 1
|
||||||
size = len(self.children[parent])
|
|
||||||
pathlist.append(size - index - 1)
|
|
||||||
else:
|
|
||||||
pathlist.append(index)
|
pathlist.append(index)
|
||||||
node = parent
|
node = parent
|
||||||
|
|
||||||
if pathlist is not None:
|
if pathlist is not None:
|
||||||
pathlist.reverse()
|
pathlist.reverse()
|
||||||
return tuple(pathlist)
|
return tuple(pathlist)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def on_iter_next(self, node):
|
def on_iter_next(self, nodeid):
|
||||||
"""
|
"""
|
||||||
Get the next node with the same parent as the given node.
|
Get the next node with the same parent as the given node.
|
||||||
"""
|
"""
|
||||||
parent = self.tree[node][0]
|
nodeid = id(nodeid)
|
||||||
for index, child in enumerate(self.children[parent]):
|
node = self.nodemap.node(nodeid)
|
||||||
if child[1] == node:
|
val = node.prev if self.__reverse else node.next
|
||||||
break
|
return self.nodemap.node(val) if val is not None else val
|
||||||
|
|
||||||
if self.__reverse:
|
def on_iter_children(self, nodeid):
|
||||||
index -= 1
|
|
||||||
else:
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
if index >= 0 and index < len(self.children[parent]):
|
|
||||||
return self.children[parent][index][1]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def on_iter_children(self, node):
|
|
||||||
"""
|
"""
|
||||||
Get the first child of the given node.
|
Get the first child of the given node.
|
||||||
"""
|
"""
|
||||||
if node in self.children:
|
if nodeid is None:
|
||||||
if self.__reverse:
|
node = self.tree[None]
|
||||||
size = len(self.children[node])
|
|
||||||
return self.children[node][size - 1][1]
|
|
||||||
else:
|
else:
|
||||||
return self.children[node][0][1]
|
nodeid = id(nodeid)
|
||||||
|
node = self.nodemap.node(nodeid)
|
||||||
|
if node.children:
|
||||||
|
if self.__reverse:
|
||||||
|
size = len(node.children)
|
||||||
|
return self.nodemap.node(node.children[size - 1][1])
|
||||||
|
else:
|
||||||
|
return self.nodemap.node(node.children[0][1])
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def on_iter_has_child(self, node):
|
def on_iter_has_child(self, nodeid):
|
||||||
"""
|
"""
|
||||||
Find if the given node has any children.
|
Find if the given node has any children.
|
||||||
"""
|
"""
|
||||||
if node in self.children:
|
if nodeid is None:
|
||||||
return True
|
node = self.tree[None]
|
||||||
else:
|
else:
|
||||||
return False
|
nodeid = id(nodeid)
|
||||||
|
node = self.nodemap.node(nodeid)
|
||||||
|
return True if node.children else False
|
||||||
|
|
||||||
def on_iter_n_children(self, node):
|
def on_iter_n_children(self, nodeid):
|
||||||
"""
|
"""
|
||||||
Get the number of children of the given node.
|
Get the number of children of the given node.
|
||||||
"""
|
"""
|
||||||
if node in self.children:
|
if nodeid is None:
|
||||||
return len(self.children[node])
|
node = self.tree[None]
|
||||||
else:
|
else:
|
||||||
return 0
|
nodeid = id(nodeid)
|
||||||
|
node = self.nodemap.node(nodeid)
|
||||||
|
return len(node.children)
|
||||||
|
|
||||||
def on_iter_nth_child(self, node, index):
|
def on_iter_nth_child(self, nodeid, index):
|
||||||
"""
|
"""
|
||||||
Get the nth child of the given node.
|
Get the nth child of the given node.
|
||||||
"""
|
"""
|
||||||
if node in self.children:
|
if nodeid is None:
|
||||||
if len(self.children[node]) > index:
|
node = self.tree[None]
|
||||||
if self.__reverse:
|
|
||||||
size = len(self.children[node])
|
|
||||||
return self.children[node][size - index - 1][1]
|
|
||||||
else:
|
else:
|
||||||
return self.children[node][index][1]
|
nodeid = id(nodeid)
|
||||||
|
node = self.nodemap.node(nodeid)
|
||||||
|
if node.children:
|
||||||
|
if len(node.children) > index:
|
||||||
|
if self.__reverse:
|
||||||
|
size = len(node.children)
|
||||||
|
return self.nodemap.node(node.children[size - index - 1][1])
|
||||||
|
else:
|
||||||
|
return self.nodemap.node(node.children[index][1])
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def on_iter_parent(self, node):
|
def on_iter_parent(self, nodeid):
|
||||||
"""
|
"""
|
||||||
Get the parent of the given node.
|
Get the parent of the given node.
|
||||||
"""
|
"""
|
||||||
if node in self.tree:
|
nodeid = id(nodeid)
|
||||||
return self.tree[node][0]
|
node = self.nodemap.node(nodeid)
|
||||||
else:
|
return self.nodemap.node(node.parent) if node.parent is not None else \
|
||||||
return None
|
None
|
||||||
|
@@ -166,6 +166,11 @@ class LongOpStatus(Callback):
|
|||||||
self._start = time.time()
|
self._start = time.time()
|
||||||
self.emit('op-heartbeat')
|
self.emit('op-heartbeat')
|
||||||
|
|
||||||
|
def step(self):
|
||||||
|
"""Convenience function so LongOpStatus can be used as a ProgressBar
|
||||||
|
if set up correctly"""
|
||||||
|
self.heartbeat()
|
||||||
|
|
||||||
def estimated_secs_to_complete(self):
|
def estimated_secs_to_complete(self):
|
||||||
"""Return the number of seconds estimated left before operation
|
"""Return the number of seconds estimated left before operation
|
||||||
completes. This will change as 'hearbeat' is called.
|
completes. This will change as 'hearbeat' is called.
|
||||||
@@ -391,6 +396,8 @@ class ProgressMonitor(object):
|
|||||||
facade.status_obj.disconnect(facade.heartbeat_cb_id)
|
facade.status_obj.disconnect(facade.heartbeat_cb_id)
|
||||||
facade.status_obj.disconnect(facade.end_cb_id)
|
facade.status_obj.disconnect(facade.end_cb_id)
|
||||||
del self._status_stack[idx]
|
del self._status_stack[idx]
|
||||||
|
if len(self._status_stack) == 0 and self._dlg:
|
||||||
|
self._dlg.close()
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
@@ -600,4 +607,3 @@ if __name__ == '__main__':
|
|||||||
w.show()
|
w.show()
|
||||||
gtk.main()
|
gtk.main()
|
||||||
print 'done'
|
print 'done'
|
||||||
|
|
||||||
|
@@ -269,10 +269,10 @@ class PersonView(ListView):
|
|||||||
if len(pathlist) == 1:
|
if len(pathlist) == 1:
|
||||||
path = pathlist[0]
|
path = pathlist[0]
|
||||||
if len(path) == 1:
|
if len(path) == 1:
|
||||||
name = model.on_get_iter(path)
|
name = model.on_get_iter(path).name
|
||||||
else:
|
else:
|
||||||
node = model.on_get_iter(path)
|
node = model.on_get_iter(path)
|
||||||
name = model.on_iter_parent(node)
|
name = model.on_iter_parent(node).name
|
||||||
|
|
||||||
try:
|
try:
|
||||||
person.get_primary_name().set_surname(name)
|
person.get_primary_name().set_surname(name)
|
||||||
@@ -332,7 +332,8 @@ class PersonView(ListView):
|
|||||||
def remove_from_person_list(self, person):
|
def remove_from_person_list(self, person):
|
||||||
"""Remove the selected person from the list. A person object is
|
"""Remove the selected person from the list. A person object is
|
||||||
expected, not an ID"""
|
expected, not an ID"""
|
||||||
path = self.model.on_get_path(person.get_handle())
|
node = self.model.get_node(person.get_handle())
|
||||||
|
path = self.model.on_get_path(node)
|
||||||
(col, row) = path
|
(col, row) = path
|
||||||
if row > 0:
|
if row > 0:
|
||||||
self.selection.select_path((col, row-1))
|
self.selection.select_path((col, row-1))
|
||||||
|
Reference in New Issue
Block a user