Deleted the src/Models directory and removed the only file that I could find that was using it.
svn: r12497
This commit is contained in:
		| @@ -1,28 +0,0 @@ | ||||
| # This is the src/Mime level Makefile for Gramps | ||||
| # We could use GNU make's ':=' syntax for nice wildcard use, | ||||
| # but that is not necessarily portable. | ||||
| # If not using GNU make, then list all .py files individually | ||||
|  | ||||
| pkgdatadir = $(datadir)/@PACKAGE@/Models | ||||
|  | ||||
| pkgdata_PYTHON = \ | ||||
| 	__init__.py\ | ||||
| 	_FastFilterModel.py\ | ||||
| 	_FastModel.py\ | ||||
| 	_ListCursor.py\ | ||||
| 	_PathCursor.py\ | ||||
| 	_PersonFilterModel.py\ | ||||
| 	_PersonListModel.py\ | ||||
| 	_PersonTreeModel.py | ||||
|  | ||||
| pkgpyexecdir = @pkgpyexecdir@/Models | ||||
| pkgpythondir = @pkgpythondir@/Models | ||||
|  | ||||
| # Clean up all the byte-compiled files | ||||
| MOSTLYCLEANFILES = *pyc *pyo | ||||
|  | ||||
| GRAMPS_PY_MODPATH = "../" | ||||
|  | ||||
| pycheck:  | ||||
| 	(export PYTHONPATH=$(GRAMPS_PY_MODPATH); \ | ||||
| 	pychecker $(pkgdata_PYTHON)); | ||||
| @@ -1,177 +0,0 @@ | ||||
|  | ||||
| import gtk | ||||
| import logging | ||||
| log = logging.getLogger(".") | ||||
|  | ||||
|  | ||||
|  | ||||
| class FastFilterModel(gtk.GenericTreeModel): | ||||
|     """A I{gtk.GenericTreeModel} that links to a BSDB cursor to provide | ||||
|     fast access to large tables. This is a pure virtual class, it must be | ||||
|     subclassed and the subclass must implement L{_get_table}, L{_get_cursor} and | ||||
|     L{_get_object_class}. | ||||
|  | ||||
|     The primary trick is to use the path specification as the tree iter. | ||||
|     This means that when the TreeView asks for the iter for path=[1,2] | ||||
|     we just echo it straight back. The onlt hard part is making sure that | ||||
|     on_iter_next can do something sensible. It needs to know how many | ||||
|     non duplicate records are in the table and then it can just accept the | ||||
|     iter from the TreeView and increment it until it reaches the total | ||||
|     length. | ||||
|  | ||||
|     The record itself is only fetched when its value is requested from | ||||
|     on_get_value() and when the number of childen need to calculated. The | ||||
|     cursor looks after the number of children calculation but it does require | ||||
|     walking the list of duplicate keys, usually this is quite short. | ||||
|      | ||||
|     @ivar _db: handle of the Gramps DB | ||||
|     @ivar _table: main table to be displayed | ||||
|     @ivar _cursor: cursor for accessing the table. | ||||
|     @ivar _obj_class: the class of the object that is being pulled from | ||||
|     the database. This should probably be one of the primary gen.lib | ||||
|     classes. | ||||
|     @ivar _num_children_cache: dictionary to hold the number of | ||||
|     children for each primary record so that we don't have to look | ||||
|     it up every time. | ||||
|     @ivar _length: the number of primary (non duplicate) records. | ||||
|     """ | ||||
|      | ||||
|     column_types = (object,) | ||||
|      | ||||
|     def __init__(self,db,data_filter): | ||||
|         gtk.GenericTreeModel.__init__(self) | ||||
|  | ||||
|         self._db = db | ||||
|         self._data_filter = data_filter | ||||
|         self._fetch_func = self._get_fetch_func(db) | ||||
|  | ||||
|         self._keys = [] | ||||
|         self._length = 0 | ||||
|          | ||||
|         self._build_data() | ||||
|          | ||||
|     def _build_data(self): | ||||
|         if not self._data_filter.is_empty(): | ||||
|             self._keys = self._data_filter.apply(self._db) | ||||
|         else:             | ||||
|             return  | ||||
|          | ||||
|         self._length = len(self._keys) | ||||
|  | ||||
|     # Helper methods to enable treeviews to tell if the model is | ||||
|     # a tree or a list. | ||||
|  | ||||
|     def is_tree(self): | ||||
|         return not self.is_list() | ||||
|  | ||||
|     def is_list(self): | ||||
|         return self.on_get_flags()>k.TREE_MODEL_LIST_ONLY | ||||
|  | ||||
|  | ||||
|     # Methods that must be implemented by subclasses.     | ||||
|     def _get_fetch_func(self,db): | ||||
|         raise NotImplementedError("subclass of FastModel must implement _get_fetch_func") | ||||
|  | ||||
|  | ||||
|     # GenericTreeModel methods | ||||
|      | ||||
|     def on_get_flags(self): | ||||
|         return gtk.TREE_MODEL_LIST_ONLY|gtk.TREE_MODEL_ITERS_PERSIST | ||||
|      | ||||
|     def on_get_n_columns(self): | ||||
|         return len(self.__class__.column_types) | ||||
|  | ||||
|     def on_get_column_type(self, index): | ||||
|         return self.column_types[index] | ||||
|  | ||||
|     def on_get_iter(self, path): | ||||
|         return list(path) | ||||
|  | ||||
|     def on_get_path(self, rowref): | ||||
|         return list(rowref) | ||||
|  | ||||
|     def on_get_value(self, rowref, column): | ||||
|         """ | ||||
|         Fetch the real object from the database. | ||||
|         """ | ||||
|  | ||||
|         record = None | ||||
|          | ||||
|         # We only have one column | ||||
|         if column is 0 and self._length > 0: | ||||
|             log.debug("on_get_value: rowref = %s", (repr(rowref))) | ||||
|  | ||||
|             try: | ||||
|                 record = self._fetch_func(self._keys[rowref[0]]) | ||||
|  | ||||
|                 # This should never return none, but there is a subtle bug | ||||
|                 # somewhere that I can't find and sometimes it does. | ||||
|                 if record is None: | ||||
|                     log.warn("Failed to fetch a record from the "\ | ||||
|                              "cursor rowref = %s" % (str(rowref))) | ||||
|             except: | ||||
|                 log.warn("Failed to fetch record, rowref = %s"\ | ||||
|                          " len(self._keys) = %d "\ | ||||
|                          " self._length = %d " % (repr(rowref), | ||||
|                                                   len(self._keys), | ||||
|                                                   self._length),                          | ||||
|                          exc_info=True) | ||||
|                  | ||||
|         return (record,rowref) | ||||
|  | ||||
|     def on_iter_next(self, rowref): | ||||
|         """ | ||||
|         Calculate the next iter at the same level in the tree. | ||||
|         """ | ||||
|  | ||||
|         # The length of the rowref (i.e. the number of elements in the path) | ||||
|         # tells us the level in the tree. | ||||
|         if len(rowref) == 1: | ||||
|  | ||||
|             # If we are at the top of the tree we just increment the | ||||
|             # first element in the iter until we reach the total length. | ||||
|             if rowref[0]+1 >= self._length: | ||||
|                 ret = None | ||||
|             else: | ||||
|                 ret = [rowref[0]+1,] | ||||
|                  | ||||
|         else: | ||||
|             # We only support one level. | ||||
|             ret = None | ||||
|  | ||||
|         return ret | ||||
|  | ||||
|  | ||||
|     def on_iter_children(self, rowref): | ||||
|         """ | ||||
|         Return the first child of the given rowref. | ||||
|         """ | ||||
|         if rowref: | ||||
|             # If the rowref is not none then we must be | ||||
|             # asking for the second level so the first | ||||
|             # child is always 0. | ||||
|             ret = [rowref[0],0] | ||||
|         else: | ||||
|             # If rowref is None the we are asking for the | ||||
|             # top level and that is always [0] | ||||
|             ret = [0,] | ||||
|              | ||||
|         return ret | ||||
|  | ||||
|     def on_iter_has_child(self, rowref): | ||||
|         return False | ||||
|  | ||||
|     def on_iter_n_children(self, rowref):         | ||||
|         return self._length | ||||
|  | ||||
|     def on_iter_nth_child(self, parent, n): | ||||
|         if parent: | ||||
|             ret = [parent[0], n] | ||||
|         else: | ||||
|             ret = [n,] | ||||
|         return ret | ||||
|  | ||||
|     def on_iter_parent(self, child): | ||||
|         if len(child) > 1: | ||||
|             return [child[0]] | ||||
|         return None | ||||
| @@ -1,202 +0,0 @@ | ||||
|  | ||||
| import gtk | ||||
| import logging | ||||
| log = logging.getLogger(".") | ||||
|  | ||||
|  | ||||
|  | ||||
| class FastModel(gtk.GenericTreeModel): | ||||
|     """A I{gtk.GenericTreeModel} that links to a BSDB cursor to provide | ||||
|     fast access to large tables. This is a pure virtual class, it must be | ||||
|     subclassed and the subclass must implement L{_get_table}, L{_get_cursor} and | ||||
|     L{_get_object_class}. | ||||
|  | ||||
|     The primary trick is to use the path specification as the tree iter. | ||||
|     This means that when the TreeView asks for the iter for path=[1,2] | ||||
|     we just echo it straight back. The onlt hard part is making sure that | ||||
|     on_iter_next can do something sensible. It needs to know how many | ||||
|     non duplicate records are in the table and then it can just accept the | ||||
|     iter from the TreeView and increment it until it reaches the total | ||||
|     length. | ||||
|  | ||||
|     The record itself is only fetched when its value is requested from | ||||
|     on_get_value() and when the number of childen need to calculated. The | ||||
|     cursor looks after the number of children calculation but it does require | ||||
|     walking the list of duplicate keys, usually this is quite short. | ||||
|      | ||||
|     @ivar _db: handle of the Gramps DB | ||||
|     @ivar _table: main table to be displayed | ||||
|     @ivar _cursor: cursor for accessing the table. | ||||
|     @ivar _obj_class: the class of the object that is being pulled from | ||||
|     the database. This should probably be one of the primary gen.lib | ||||
|     classes. | ||||
|     @ivar _num_children_cache: dictionary to hold the number of | ||||
|     children for each primary record so that we don't have to look | ||||
|     it up every time. | ||||
|     @ivar _length: the number of primary (non duplicate) records. | ||||
|     """ | ||||
|      | ||||
|     column_types = (object,) | ||||
|      | ||||
|     def __init__(self,db): | ||||
|         gtk.GenericTreeModel.__init__(self) | ||||
|  | ||||
|         self._db = db | ||||
|         self._table = self._get_table(db) | ||||
|         self._cursor = self._get_cursor(db) | ||||
|         self._object_class = self._get_object_class(db) | ||||
|         self._length = self._get_length(db) | ||||
|          | ||||
|         self._num_children_cache = {} | ||||
|  | ||||
|     # Helper methods to enable treeviews to tell if the model is | ||||
|     # a tree or a list. | ||||
|  | ||||
|     def is_tree(self): | ||||
|         return not self.is_list() | ||||
|  | ||||
|     def is_list(self): | ||||
|         return self.on_get_flags()>k.TREE_MODEL_LIST_ONLY | ||||
|  | ||||
|     # Methods that must be implemented by subclasses. | ||||
|      | ||||
|     def _get_table(self,db): | ||||
|         raise NotImplementedError("subclass of FastModel must implement _get_table") | ||||
|  | ||||
|     def _get_cursor(self,db): | ||||
|         raise NotImplementedError("subclass of FastModel must implement _get_cursor") | ||||
|  | ||||
|     def _get_object_class(self,db): | ||||
|         raise NotImplementedError("subclass of FastModel must implement _get_cursor") | ||||
|  | ||||
|     def _get_length(self,db): | ||||
|         raise NotImplementedError("subclass of FastModel must implement _get_length") | ||||
|  | ||||
|     # GenericTreeModel methods | ||||
|      | ||||
|     def on_get_flags(self): | ||||
|         return gtk.TREE_MODEL_ITERS_PERSIST | ||||
|      | ||||
|     def on_get_n_columns(self): | ||||
|         return len(self.__class__.column_types) | ||||
|  | ||||
|     def on_get_column_type(self, index): | ||||
|         return self.column_types[index] | ||||
|  | ||||
|     def on_get_iter(self, path): | ||||
|         return list(path) | ||||
|  | ||||
|     def on_get_path(self, rowref): | ||||
|         return list(rowref) | ||||
|  | ||||
|     def on_get_value(self, rowref, column): | ||||
|         """ | ||||
|         Fetch the real object from the database. | ||||
|         """ | ||||
|  | ||||
|         # We only have one column | ||||
|         if column is 0: | ||||
|             obj = self._object_class() | ||||
|  | ||||
|             # Use the rowref as the path, because the iter methods | ||||
|             # simple return the path as the iter it is safe to use | ||||
|             # it here. | ||||
|             record = self._cursor.lookup_path(rowref) | ||||
|  | ||||
|             # This should never return none, but there is a subtle bug | ||||
|             # somewhere that I can't find and sometimes it does. | ||||
|             if record is not None: | ||||
|                 obj.unserialize(record[1]) | ||||
|             else: | ||||
|                 log.warn("Failed to fetch a record from the cursor rowref = %s" % (str(rowref))) | ||||
|                  | ||||
|             return (obj,rowref) | ||||
|  | ||||
|     def on_iter_next(self, rowref): | ||||
|         """ | ||||
|         Calculate the next iter at the same level in the tree. | ||||
|         """ | ||||
|  | ||||
|         # The length of the rowref (i.e. the number of elements in the path) | ||||
|         # tells us the level in the tree. | ||||
|         if len(rowref) == 1: | ||||
|  | ||||
|             # If we are at the top of the tree we just increment the | ||||
|             # first element in the iter until we reach the total length. | ||||
|             if rowref[0]+1 >= self._length: | ||||
|                 ret = None | ||||
|             else: | ||||
|                 ret = [rowref[0]+1,] | ||||
|                  | ||||
|         elif len(rowref) == 2: | ||||
|  | ||||
|             # If we are at the second level we first check to see if we | ||||
|             # have the number of children of this row already in the cache | ||||
|             if rowref[0] not in self._num_children_cache: | ||||
|  | ||||
|                 # If not calculate the number of children and put it in the cache. | ||||
|                 self._num_children_cache[rowref[0]] = self._cursor.get_n_children([rowref[0],]) | ||||
|  | ||||
|             num_children = self._num_children_cache[rowref[0]] | ||||
|  | ||||
|             # Now increment the second element of the iter path until we | ||||
|             # reach the number of children. | ||||
|             if rowref[1]+1 < num_children: | ||||
|                 ret = [rowref[0],rowref[1]+1] | ||||
|             else: | ||||
|                 ret = None | ||||
|         else: | ||||
|             # We only support two levels. | ||||
|             ret = None | ||||
|  | ||||
|         return ret | ||||
|  | ||||
|  | ||||
|     def on_iter_children(self, rowref): | ||||
|         """ | ||||
|         Return the first child of the given rowref. | ||||
|         """ | ||||
|         if rowref: | ||||
|             # If the rowref is not none then we must be | ||||
|             # asking for the second level so the first | ||||
|             # child is always 0. | ||||
|             ret = [rowref[0],0] | ||||
|         else: | ||||
|             # If rowref is None the we are asking for the | ||||
|             # top level and that is always [0] | ||||
|             ret = [0,] | ||||
|              | ||||
|         return ret | ||||
|  | ||||
|     def on_iter_has_child(self, rowref): | ||||
|         if rowref: | ||||
|             ret = self._cursor.has_children(rowref) | ||||
|         else: | ||||
|             ret = range(0,self._length) | ||||
|         return ret | ||||
|  | ||||
|     def on_iter_n_children(self, rowref):         | ||||
|         if rowref: | ||||
|             # If we are at the second level we first check to see if we | ||||
|             # have the number of children of this row already in the cache | ||||
|             if rowref[0] not in self._num_children_cache: | ||||
|  | ||||
|                 # If not calculate the number of children and put it in the cache. | ||||
|                 self._num_children_cache[rowref[0]] = self._cursor.get_n_children([rowref[0],]) | ||||
|  | ||||
|             ret = self._num_children_cache[rowref[0]] | ||||
|         else: | ||||
|             ret = self._length | ||||
|         return ret | ||||
|  | ||||
|     def on_iter_nth_child(self, parent, n): | ||||
|         if parent: | ||||
|             ret = [parent[0], n] | ||||
|         else: | ||||
|             ret = [n,] | ||||
|         return ret | ||||
|  | ||||
|     def on_iter_parent(self, child): | ||||
|         if len(child) > 1: | ||||
|             return [child[0]] | ||||
|         return None | ||||
| @@ -1,177 +0,0 @@ | ||||
|  | ||||
| import cPickle | ||||
| import logging | ||||
| log = logging.getLogger(".") | ||||
|  | ||||
| class ListCursor(object): | ||||
|     """ | ||||
|     Provide a wrapper around the cursor class that provides fast | ||||
|     traversal using treeview paths for models that are LISTONLY, i.e. | ||||
|     they have no tree structure. | ||||
|  | ||||
|     It keeps track of the current index that the cursor is pointing | ||||
|     at. | ||||
|      | ||||
|     @ivar _index: The current index pointed to by the cursor. | ||||
|      | ||||
|     To speed up lookups the cursor is kept as close as possible to the | ||||
|     likely next lookup and is moved using next()/prev() were ever | ||||
|     possible. | ||||
|  | ||||
|     @ivar _object_cache: A cache of previously fetched records. These are | ||||
|     indexed by the values of the L{_index}. | ||||
|     """ | ||||
|      | ||||
|     def __init__(self,cursor): | ||||
|         """ | ||||
|         @param cursor: The cursor used to fetch the records. | ||||
|         @type cursor: An object supporting the cursor methods of a U{BSDB | ||||
|         cursor<http://pybsddb.sourceforge.net/bsddb3.html>}. | ||||
|         It must have a BTREE index type and DB_DUP to support duplicate | ||||
|         records. It should probably also have DB_DUPSORT if you want to | ||||
|         have sorted records. | ||||
|         """ | ||||
|         self._cursor = cursor | ||||
|         self._object_cache = {} | ||||
|  | ||||
|         self.top() | ||||
|  | ||||
|  | ||||
|     def top(self): | ||||
|         self._cursor.first() | ||||
|         self._index = 0 | ||||
|  | ||||
|     def next(self): | ||||
|         """ | ||||
|         Move to the next record. | ||||
|         """ | ||||
|         data = self._cursor.next() | ||||
|  | ||||
|         # If there was a next record that data will | ||||
|         # not be None | ||||
|         if data is not None: | ||||
|             # Up date the index pointers so that | ||||
|             # they point to the current record. | ||||
|             self._index+= 1 | ||||
|              | ||||
|         return data | ||||
|  | ||||
|     def prev(self): | ||||
|         """ | ||||
|         Move to the previous record. | ||||
|         """ | ||||
|         data = self._cursor.prev() | ||||
|  | ||||
|         # If there was a next record that data will | ||||
|         # not be None | ||||
|         if data is not None: | ||||
|             # Up date the index pointers so that | ||||
|             # they point to the current record. | ||||
|             self._index -= 1 | ||||
|              | ||||
|         return data | ||||
|  | ||||
|  | ||||
|     def has_children(self,path): | ||||
|         """ | ||||
|         This cursor is only for simple lists so no records have | ||||
|         children. | ||||
|          | ||||
|         @param path: The path spec to check. | ||||
|         @type path: A TreeView path. | ||||
|         """ | ||||
|          | ||||
|         return False | ||||
|  | ||||
|     def get_n_children(self,path): | ||||
|         """ | ||||
|         Return the number of children that the record at I{path} has. | ||||
|  | ||||
|         @param path: The path spec to check. | ||||
|         @type path: A TreeView path. | ||||
|         """ | ||||
|  | ||||
|         return 0 | ||||
|      | ||||
|     def lookup(self,index,use_cache=True): | ||||
|         """ | ||||
|         Lookup a primary record. | ||||
|  | ||||
|         @param index: The index of the primary record. This is its | ||||
|         possition in the sequence of non_duplicate keys. | ||||
|         @type index: int | ||||
|         @para use_case: If B{True} the record will be looked up in the | ||||
|         object cache and will be returned from there. This will not | ||||
|         update the possition of the cursor. If B{False} the record will | ||||
|         fetched from the cursor even if it is in the object cache and | ||||
|         cursor will be left possitioned on the record. | ||||
|         """ | ||||
|  | ||||
|         # See if the record is in the cache. | ||||
|         if index in self._object_cache and use_cache: | ||||
|             ret = self._object_cache[index] | ||||
|  | ||||
|         # If the record is not in the cache or we are ignoring the | ||||
|         # cache. | ||||
|         else: | ||||
|              | ||||
|             # If the cursor points to the record we want | ||||
|             # the first index will be equal to the | ||||
|             # index required | ||||
|             if index == self._index: | ||||
|                 ret = self._cursor.current() | ||||
|  | ||||
|             # If the current cursor is behind the | ||||
|             # requested index move it forward. | ||||
|             elif index < self._index: | ||||
|                 while index < self._index: | ||||
|                     ret = self.prev() | ||||
|                     if ret is None: | ||||
|                         log.warn("Failed to move back to index = %s" % (str(index))) | ||||
|                         break | ||||
|  | ||||
|                 ret = self._cursor.current() | ||||
|  | ||||
|             # If the current cursor is in front of | ||||
|             # requested index move it backward. | ||||
|             else: | ||||
|                 while index > self._index: | ||||
|                     ret = self.next() | ||||
|                     if ret is None: | ||||
|                         log.warn("Failed to move forward to index = %s" % (str(index))) | ||||
|                         break | ||||
|                      | ||||
|                 ret = self._cursor.current() | ||||
|  | ||||
|             # when we have got the record save it in | ||||
|             # the cache | ||||
|             if ret is not None: | ||||
|                 ret = self._unpickle(ret) | ||||
|                 self._object_cache[index] = ret | ||||
|              | ||||
|         return ret | ||||
|  | ||||
|     def _unpickle(self,rec): | ||||
|         """ | ||||
|         It appears that reading an object from a cursor does not | ||||
|         automatically unpickle it. So this method provides | ||||
|         a convenient way to unpickle the object. | ||||
|         """ | ||||
|         if rec and isinstance(rec[1], str): | ||||
|             tmp = [rec[0],None] | ||||
|             tmp[1] = cPickle.loads(rec[1]) | ||||
|             rec = tmp | ||||
|         return rec | ||||
|  | ||||
|     def lookup_path(self,path): | ||||
|         """ | ||||
|         Lookup a record from a patch specification. | ||||
|  | ||||
|         @param path: The path spec to check. | ||||
|         @type path: A TreeView path. | ||||
|  | ||||
|         """ | ||||
|         return self.lookup(path[0]) | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,325 +0,0 @@ | ||||
|  | ||||
| import cPickle | ||||
| import logging | ||||
| log = logging.getLogger(".") | ||||
|  | ||||
| class PathCursor(object): | ||||
|     """ | ||||
|     Provide a wrapper around the cursor class that provides fast | ||||
|     traversal using treeview paths. | ||||
|  | ||||
|     It keeps track of the current index that the cursor is pointing | ||||
|     at by using a two stage index. The first element of the index is | ||||
|     the sequence number of the record in the list of non_duplicate | ||||
|     keys and the second element is the sequence number of the record | ||||
|     within the duplicate keys to which it is a member. | ||||
|  | ||||
|     For example, with the following table indexed on Surname:: | ||||
|  | ||||
|             Record Value      Index | ||||
|             ============      ===== | ||||
|             | ||||
|             Blogs, Jo         [0,0] | ||||
|             Blogs, Jane       [0,1] | ||||
|             Smith, Wilman     [1,0] | ||||
|             Smith, John       [1,1] | ||||
|  | ||||
|     @ivar _index: The current index pointed to by the cursor. | ||||
|      | ||||
|     To speed up lookups the cursor is kept as close as possible to the | ||||
|     likely next lookup and is moved using next_dup()/prev_dup() were ever | ||||
|     possible. | ||||
|  | ||||
|     @ivar _object_cache: A cache of previously fetched records. These are | ||||
|     indexed by the values of the L{_index}. | ||||
|     """ | ||||
|      | ||||
|     def __init__(self,cursor): | ||||
|         """ | ||||
|         @param cursor: The cursor used to fetch the records. | ||||
|         @type cursor: An object supporting the cursor methods of a U{BSDB | ||||
|         cursor<http://pybsddb.sourceforge.net/bsddb3.html>}. | ||||
|         It must have a BTREE index type and DB_DUP to support duplicate | ||||
|         records. It should probably also have DB_DUPSORT if you want to | ||||
|         have sorted records. | ||||
|         """ | ||||
|         self._cursor = cursor | ||||
|         self._object_cache = {} | ||||
|  | ||||
|         self.top() | ||||
|  | ||||
|  | ||||
|     def top(self): | ||||
|         self._cursor.first() | ||||
|         self._index = [0,0] | ||||
|  | ||||
|     def next_nodup(self): | ||||
|         """ | ||||
|         Move to the next non-duplcate record. | ||||
|         """ | ||||
|         data = self._cursor.next_nodup() | ||||
|  | ||||
|         # If there was a next record that data will | ||||
|         # not be None | ||||
|         if data is not None: | ||||
|             # Up date the index pointers so that | ||||
|             # they point to the current record. | ||||
|             self._index[0] += 1 | ||||
|             self._index[1] = 0 | ||||
|              | ||||
|         return data | ||||
|  | ||||
|     def prev_nodup(self): | ||||
|         """ | ||||
|         Move to the previous non-duplicate record. | ||||
|         """ | ||||
|         data = self._cursor.prev_nodup() | ||||
|  | ||||
|         # If there was a next record that data will | ||||
|         # not be None | ||||
|         if data is not None: | ||||
|             # Up date the index pointers so that | ||||
|             # they point to the current record. | ||||
|             self._index[0] -= 1 | ||||
|             self._index[1] = 0 | ||||
|              | ||||
|         return data | ||||
|  | ||||
|     def next_dup(self): | ||||
|         """ | ||||
|         Move to the next record with a duplicate key to the current record. | ||||
|         """ | ||||
|         data = self._cursor.next_dup() | ||||
|  | ||||
|         # If there was a next record that data will | ||||
|         # not be None | ||||
|         if data is not None: | ||||
|             # Update the secondary index. | ||||
|             self._index[1] += 1             | ||||
|              | ||||
|         return data | ||||
|  | ||||
|     def has_children(self,path): | ||||
|         """ | ||||
|         Check is the I{path} has any children. | ||||
|  | ||||
|         At the moment this method lies. There is no fast way to check | ||||
|         if a given key has any duplicates and the TreeView insists on | ||||
|         checking for every row. So this methods returns True if the | ||||
|         path is 1 element long and False if it is more. This works | ||||
|         for us because we show the first record in a set of duplicates | ||||
|         as the first child. So all top level rows have at least one child. | ||||
|  | ||||
|         @param path: The path spec to check. | ||||
|         @type path: A TreeView path. | ||||
|         """ | ||||
|          | ||||
|         if len(path) == 1: | ||||
|             return True | ||||
|         else: | ||||
|             return False | ||||
|  | ||||
|     def get_n_children(self,path): | ||||
|         """ | ||||
|         Return the number of children that the record at I{path} has. | ||||
|  | ||||
|         @param path: The path spec to check. | ||||
|         @type path: A TreeView path. | ||||
|         """ | ||||
|  | ||||
|         # Only top level records can have children. | ||||
|         if len(path) > 1: | ||||
|             return 0 | ||||
|  | ||||
|         # Fetch the primary record | ||||
|         ret = self.lookup(path[0],use_cache=False) | ||||
|          | ||||
|         if ret is not None: | ||||
|             # Now count the duplicates. We start at 1 because | ||||
|             # we want to include the primary in the duplicates. | ||||
|             count = 1 | ||||
|             ret = self.next_dup() | ||||
|             while ret: | ||||
|                 ret = self.next_dup() | ||||
|                 count += 1 | ||||
|                 self._index[1] += 1 | ||||
|                  | ||||
|             ret = count | ||||
|         else: | ||||
|             # If we failed to find the primary something is | ||||
|             # wrong. | ||||
|             ret = 0 | ||||
|  | ||||
|         return ret | ||||
|      | ||||
|     def lookup(self,index,use_cache=True): | ||||
|         """ | ||||
|         Lookup a primary record. | ||||
|  | ||||
|         @param index: The index of the primary record. This is its | ||||
|         possition in the sequence of non_duplicate keys. | ||||
|         @type index: int | ||||
|         @para use_case: If B{True} the record will be looked up in the | ||||
|         object cache and will be returned from there. This will not | ||||
|         update the possition of the cursor. If B{False} the record will | ||||
|         fetched from the cursor even if it is in the object cache and | ||||
|         cursor will be left possitioned on the record. | ||||
|         """ | ||||
|  | ||||
|         # See if the record is in the cache. | ||||
|         if index in self._object_cache and use_cache: | ||||
|             ret = self._object_cache[index]['primary'] | ||||
|  | ||||
|         # If the record is not in the cache or we are ignoring the | ||||
|         # cache. | ||||
|         else: | ||||
|              | ||||
|             # If the cursor points to a duplicate record | ||||
|             # it will have a second index value of 0.             | ||||
|             if self._index[1] != 0: | ||||
|                 # We need to move the cursor to the | ||||
|                 # first of a set of duplicates so that | ||||
|                 # we can then shift it to the required | ||||
|                 # index. | ||||
|                 self.next_nodup() | ||||
|  | ||||
|                  | ||||
|             # If the cursor points to the record we want | ||||
|             # the first index will be equal to the | ||||
|             # index required | ||||
|             if index == self._index[0]: | ||||
|                 ret = self._cursor.current() | ||||
|  | ||||
|             # If the current cursor is behind the | ||||
|             # requested index move it forward. | ||||
|             elif index < self._index[0]: | ||||
|                 while index < self._index[0]: | ||||
|                     ret = self.prev_nodup() | ||||
|                     if ret is None: | ||||
|                         log.warn("Failed to move back to index = %s" % (str(index))) | ||||
|                         break | ||||
|  | ||||
|                 # Because prev_nodup() leaves the cursor on | ||||
|                 # the last of a set of duplicates we need | ||||
|                 # to go up one further and then back down | ||||
|                 # again. | ||||
|                 ret = self.prev_nodup() | ||||
|                 if ret is None: | ||||
|                     # We are at the start | ||||
|                     self.top() | ||||
|                     ret = self._cursor.current() | ||||
|                 else: | ||||
|                     ret = self.next_nodup() | ||||
|  | ||||
|             # If the current cursor is in front of | ||||
|             # requested index move it backward. | ||||
|             else: | ||||
|                 while index > self._index[0]: | ||||
|                     ret = self.next_nodup() | ||||
|                     if ret is None: | ||||
|                         log.warn("Failed to move forward to index = %s" % (str(index))) | ||||
|                         break | ||||
|                      | ||||
|                 ret = self._cursor.current() | ||||
|  | ||||
|             # when we have got the record save it in | ||||
|             # the cache | ||||
|             if ret is not None: | ||||
|                 ret = self._unpickle(ret) | ||||
|                 self._object_cache[index] = {'primary':ret} | ||||
|              | ||||
|         return ret | ||||
|  | ||||
|     def _unpickle(self,rec): | ||||
|         """ | ||||
|         It appears that reading an object from a cursor does not | ||||
|         automatically unpickle it. So this method provides | ||||
|         a convenient way to unpickle the object. | ||||
|         """ | ||||
|         if rec and isinstance(rec[1], str): | ||||
|             tmp = [rec[0],None] | ||||
|             tmp[1] = cPickle.loads(rec[1]) | ||||
|             rec = tmp | ||||
|         return rec | ||||
|  | ||||
|     def lookup_path(self,path): | ||||
|         """ | ||||
|         Lookup a record from a patch specification. | ||||
|  | ||||
|         @param path: The path spec to check. | ||||
|         @type path: A TreeView path. | ||||
|  | ||||
|         """ | ||||
|  | ||||
|         # If the path is for a primary record it will only | ||||
|         # be 1 element long. | ||||
|         if len(path) == 1: | ||||
|             ret = self.lookup(path[0]) | ||||
|  | ||||
|         # If it is for a secondary object we need to | ||||
|         # traverse the duplicates. | ||||
|         else: | ||||
|  | ||||
|             # First check to see if the record has already | ||||
|             # been fetched. | ||||
|             if path[0] in self._object_cache and \ | ||||
|                path[1] in self._object_cache[path[0]]: | ||||
|  | ||||
|                 # return record from cache. | ||||
|                 ret = self._object_cache[path[0]][path[1]] | ||||
|                  | ||||
|             else: | ||||
|                 # If we already in the duplicates for this | ||||
|                 # primary index then the first index will | ||||
|                 # be the same as the first element of the | ||||
|                 # path. | ||||
|                 if self._index[0] == path[0]: | ||||
|                     # If the second elements match we are | ||||
|                     # already looking at the correct record. | ||||
|                     if self._index[1] == path[1]: | ||||
|                         ret = self._cursor.current() | ||||
|  | ||||
|                     # If the cursor is in front we can | ||||
|                     # move it back. Unfortunately there is no | ||||
|                     # prev_dup() method so we have to | ||||
|                     # reposition of the cursor at the start | ||||
|                     # of the duplicates and step forward | ||||
|                     else: | ||||
|                         self.prev_nodup() | ||||
|                         self.next_nodup() | ||||
|                         ret = self.lookup(path[0],use_cache=False) | ||||
|  | ||||
|                         # If the request if not for the first duplicate | ||||
|                         # we step forward the number of duplicates | ||||
|                         # that have been requested. | ||||
|                         count = 0 | ||||
|                         while count < path[1]: | ||||
|                             ret = self.next_dup() | ||||
|                             count += 1 | ||||
|  | ||||
|                 # If the primary elements do not match we | ||||
|                 # must move the cursor to the start of the | ||||
|                 # duplicates that are requested. | ||||
|                 else: | ||||
|                     self.prev_nodup() | ||||
|                     self.next_nodup() | ||||
|  | ||||
|                     ret = self.lookup(path[0],use_cache=False) | ||||
|  | ||||
|                     # If the request if not for the first duplicate | ||||
|                     # we step forward the number of duplicates | ||||
|                     # that have been requested. | ||||
|                     count = 0 | ||||
|                     while count < path[1]: | ||||
|                         ret = self.next_dup() | ||||
|                         count += 1 | ||||
|  | ||||
|  | ||||
|                 # Put the fetched record in the cache | ||||
|                 if ret is not None: | ||||
|                     ret = self._unpickle(ret) | ||||
|                     self._object_cache[path[0]][path[1]] = ret | ||||
|  | ||||
|         return ret | ||||
|  | ||||
|  | ||||
| @@ -1,19 +0,0 @@ | ||||
|  | ||||
| from _FastFilterModel import FastFilterModel | ||||
| import gen.lib | ||||
|  | ||||
|  | ||||
| class PersonFilterModel(FastFilterModel): | ||||
|     """Provide a fast model interface to the Person table. | ||||
|     """ | ||||
|          | ||||
|     def __init__(self,db,apply_filter): | ||||
|         FastFilterModel.__init__(self,db,apply_filter) | ||||
|  | ||||
|  | ||||
|     def _get_object_class(self,db): | ||||
|         return gen.lib.Person | ||||
|  | ||||
|      | ||||
|     def _get_fetch_func(self,db): | ||||
|         return db.get_person_from_handle | ||||
| @@ -1,32 +0,0 @@ | ||||
|  | ||||
| import gtk | ||||
| import logging | ||||
| log = logging.getLogger(".") | ||||
|  | ||||
| from _ListCursor import ListCursor | ||||
|  | ||||
| from _FastModel import FastModel | ||||
| import gen.lib | ||||
|  | ||||
|  | ||||
| class PersonListModel(FastModel): | ||||
|     """Provide a fast model interface to the Person table. | ||||
|     """ | ||||
|          | ||||
|     def __init__(self,db): | ||||
|         FastModel.__init__(self,db) | ||||
|  | ||||
|     def _get_table(self,db): | ||||
|         return db.surnames | ||||
|  | ||||
|     def _get_cursor(self,db): | ||||
|         return ListCursor(db.surnames.cursor()) | ||||
|  | ||||
|     def _get_object_class(self,db): | ||||
|         return gen.lib.Person | ||||
|  | ||||
|     def _get_length(self,db): | ||||
|         return self._table.stat()['ndata'] | ||||
|      | ||||
|     def on_get_flags(self): | ||||
|         return gtk.TREE_MODEL_LIST_ONLY|gtk.TREE_MODEL_ITERS_PERSIST | ||||
| @@ -1,28 +0,0 @@ | ||||
|  | ||||
| import logging | ||||
| log = logging.getLogger(".") | ||||
|  | ||||
| from _PathCursor import PathCursor | ||||
|  | ||||
| from _FastModel import FastModel | ||||
| import gen.lib | ||||
|  | ||||
|  | ||||
| class PersonTreeModel(FastModel): | ||||
|     """Provide a fast model interface to the Person table. | ||||
|     """ | ||||
|          | ||||
|     def __init__(self,db): | ||||
|         FastModel.__init__(self,db) | ||||
|  | ||||
|     def _get_table(self,db): | ||||
|         return db.surnames | ||||
|  | ||||
|     def _get_cursor(self,db): | ||||
|         return PathCursor(db.surnames.cursor()) | ||||
|  | ||||
|     def _get_object_class(self,db): | ||||
|         return gen.lib.Person | ||||
|  | ||||
|     def _get_length(self,db): | ||||
|         return self._table.stat()['nkeys']  | ||||
| @@ -1,4 +0,0 @@ | ||||
|  | ||||
| from _PersonListModel import PersonListModel | ||||
| from _PersonTreeModel import PersonTreeModel | ||||
| from _PersonFilterModel import PersonFilterModel | ||||
| @@ -1,139 +0,0 @@ | ||||
| import time | ||||
| import gtk | ||||
| import gobject | ||||
|  | ||||
| import sys, os | ||||
| sys.path.append("../src") | ||||
|  | ||||
| #from Models import PersonModel,PersonFilterModel | ||||
| import GenericFilter | ||||
|  | ||||
| ## class ProxyPerson(object): | ||||
| ##     """ | ||||
| ##     This class provides a wrapper around the real object that | ||||
| ##     is stored in the model.  | ||||
| ##     """ | ||||
|      | ||||
| ##     def __init__(self,id,db): | ||||
| ##         self._id = id | ||||
| ##         self._db = db | ||||
| ##         self._obj = None | ||||
|  | ||||
| ##     def row_ref(self): | ||||
| ##         """This should return the value that is used | ||||
| ##         as the row reference in the model.""" | ||||
| ##         return self._id | ||||
|  | ||||
| ##     def __getattr__(self, name): | ||||
| ##         """ | ||||
| ##         Delegate to the real object. | ||||
| ##         """ | ||||
|  | ||||
| ##         # Fetch the object from the database if we | ||||
| ##         # don't already have it | ||||
| ##         if self._obj is None: | ||||
| ##             self._obj = self._get_object() | ||||
|  | ||||
| ##         # Call the method that we were asked | ||||
| ##         # for on the real object. | ||||
| ##         return getattr(self._obj, name) | ||||
|  | ||||
| ##     def _get_object(self): | ||||
| ##         """ | ||||
| ##         Fetch the real object from the database. | ||||
| ##         """ | ||||
| ##         print "getting object = ", self._id | ||||
| ##         return self._db.get_person_from_handle(self._id) | ||||
|  | ||||
|      | ||||
| class PersonWindow(gtk.Window): | ||||
|  | ||||
|     def __init__(self,db): | ||||
|         gtk.Window.__init__(self) | ||||
|  | ||||
|         self.set_default_size(700,300) | ||||
|  | ||||
|         self._db = db | ||||
|  | ||||
|         fil = GenericFilter.GenericFilter() | ||||
|         fil.add_rule(GenericFilter.SearchName(["Taylor"])) | ||||
|  | ||||
|         person_tree = PersonTreeView(db,fil)         | ||||
|         person_tree.show() | ||||
|  | ||||
|         scrollwindow = gtk.ScrolledWindow() | ||||
|         scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) | ||||
|         scrollwindow.set_shadow_type(gtk.SHADOW_ETCHED_IN) | ||||
|         scrollwindow.show()         | ||||
|         scrollwindow.add(person_tree)         | ||||
|         self.add(scrollwindow) | ||||
|  | ||||
|         person_tree.clear_filter() | ||||
|          | ||||
|         #person_tree.set_filter(fil) | ||||
|         #person_model = PersonFilterModel(db,fil) | ||||
| 	#person_tree.set_model(person_model) | ||||
|  | ||||
|         self._person_tree = person_tree | ||||
|         #self._person_model = person_model | ||||
|  | ||||
| 	#gobject.idle_add(self.load_tree().next) | ||||
|          | ||||
|         self._expose_count = 0 | ||||
|  | ||||
| ##     def load_tree(self): | ||||
| ##         self._person_tree.freeze_child_notify() | ||||
|  | ||||
| ##         for i in self._db.get_person_handles(): | ||||
| ##             self._person_model.add(ProxyPerson(i,self._db)) | ||||
| ##             yield True | ||||
|              | ||||
| ##         self._person_tree.thaw_child_notify() | ||||
|          | ||||
| ##         self._person_tree.set_model(self._person_model) | ||||
|  | ||||
| ##         yield False | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     import sys, os | ||||
|     sys.path.append("..") | ||||
|      | ||||
|     import GrampsDb | ||||
|     import const | ||||
|     import logging | ||||
|      | ||||
|     form = logging.Formatter(fmt="%(relativeCreated)d: %(levelname)s: %(filename)s: line %(lineno)d: %(message)s") | ||||
|     stderrh = logging.StreamHandler(sys.stderr) | ||||
|     stderrh.setFormatter(form) | ||||
|     stderrh.setLevel(logging.DEBUG) | ||||
|  | ||||
|     # everything. | ||||
|     l = logging.getLogger() | ||||
|     l.setLevel(logging.DEBUG) | ||||
|     l.addHandler(stderrh) | ||||
|  | ||||
|  | ||||
|     def cb(d): | ||||
|         pass | ||||
|  | ||||
|     def main(): | ||||
| 	print "start", sys.argv[1] | ||||
|          | ||||
|         db = GrampsDb.gramps_db_factory(const.app_gramps)() | ||||
|         db.load(os.path.realpath(sys.argv[1]), | ||||
|                 cb, # callback | ||||
|                 "w") | ||||
|  | ||||
| 	print "window" | ||||
|         w = PersonWindow(db) | ||||
|         w.show() | ||||
|  | ||||
|         w.connect("destroy", gtk.main_quit) | ||||
|  | ||||
| 	print "main" | ||||
|         gtk.main() | ||||
|  | ||||
|     #import profile | ||||
|     #profile.run('main()','profile.out') | ||||
|  | ||||
|     main() | ||||
		Reference in New Issue
	
	Block a user