From a93872a0f3a3c50659d4dc2280a62525461e605b Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Fri, 21 Aug 2015 09:45:59 -0400 Subject: [PATCH] Fix for exponential cost on treeviews; whitespace cleanup --- gramps/gui/views/treemodels/treebasemodel.py | 136 +++++++++---------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/gramps/gui/views/treemodels/treebasemodel.py b/gramps/gui/views/treemodels/treebasemodel.py index 4dae0d559..3c31674c4 100644 --- a/gramps/gui/views/treemodels/treebasemodel.py +++ b/gramps/gui/views/treemodels/treebasemodel.py @@ -85,7 +85,7 @@ class Node(object): def __init__(self, ref, parent, sortkey, handle, secondary): if sortkey: self.name = sortkey - #sortkey must be localized sort, so + #sortkey must be localized sort, so self.sortkey = glocale.sort_key(sortkey) if not self.sortkey: self.sortkey = glocale.sort_key('') @@ -145,7 +145,7 @@ class Node(object): 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. @@ -191,7 +191,7 @@ class NodeMap(object): ## item.parent = None ## item.children = [] self.id2node.clear() - + def add_node(self, node): """ Add a Node object to the map and return id of this node @@ -199,7 +199,7 @@ class NodeMap(object): nodeid = id(node) self.id2node[nodeid] = node return nodeid - + def del_node(self, node): """ Remove a Node object from the map and return nodeid @@ -207,7 +207,7 @@ class NodeMap(object): nodeid = id(node) del self.id2node[nodeid] return nodeid - + def del_nodeid(self, nodeid): """ Remove Node with id nodeid from the map @@ -247,26 +247,26 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): handle2node A dictionary of gramps handles. Each entry is a node object. nodemap A NodeMap, mapping id's of the nodes to the node objects. Node refer to other nodes via id's in a linked list form. - + The model obtains data from database as needed and holds a cache of most recently used data. - As iter for generictreemodel, node is used. This will be the handle for + As iter for generictreemodel, node is used. This will be the handle for database objects. - + Creation: db : the database search : the search that must be shown skip : values not to show scol : column on which to sort order : order of the sort - sort_map : mapping from columns seen on the GUI and the columns + sort_map : mapping from columns seen on the GUI and the columns as defined here - nrgroups : maximum number of grouping level, 0 = no group, + nrgroups : maximum number of grouping level, 0 = no group, 1= one group, .... Some optimizations can be for only - one group. nrgroups=0 should never be used, as then a + one group. nrgroups=0 should never be used, as then a flatbasemodel should be used group_can_have_handle : - can groups have a handle. If False, this means groups + can groups have a handle. If False, this means groups are only used to group subnodes, not for holding data and showing subnodes has_secondary : If True, the model contains two Gramps object types. @@ -284,23 +284,23 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): GObject.GObject.__init__(self) BaseModel.__init__(self) #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 - #iterator as valid. When your model's structure changes, you should - #increment your model's stamp to mark all older iterators as invalid. - #They will be recognised as invalid because they will then have an + #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 + #increment your model's stamp to mark all older iterators as invalid. + #They will be recognised as invalid because they will then have an #incorrect stamp. self.stamp = 0 #two unused attributes pesent to correspond to flatbasemodel self.prev_handle = None self.prev_data = None - + self.__reverse = (order == Gtk.SortType.DESCENDING) self.scol = scol self.nrgroups = nrgroups self.group_can_have_handle = group_can_have_handle self.has_secondary = has_secondary self.db = db - + self._set_base_data() # Initialise data structures @@ -327,9 +327,9 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): if self.has_secondary: self.sort_func2 = self.smap2[scol] self.sort_col = scol - + self._in_build = False - + self.__total = 0 self.__displayed = 0 @@ -353,7 +353,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): self.sort_func2 = None if self.nodemap: self.nodemap.destroy() - + self.nodemap = None self.rebuild_data = None self._build_data = None @@ -364,9 +364,9 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): def _set_base_data(self): """ - This method must be overwritten in the inheriting class, setting + 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 @@ -375,14 +375,14 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): fmap : the map with functions to obtain value of a row with handle """ self.gen_cursor = None - self.number_items = None # function + self.number_items = None # function self.map = None self.smap = None self.fmap = None if self.has_secondary: self.gen_cursor2 = None - self.number_items2 = None # function + self.number_items2 = None # function self.map2 = None self.smap2 = None self.fmap2 = None @@ -392,7 +392,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): Return the number of rows displayed. """ return self.__displayed - + def total(self): """ Return the total number of rows without a filter or search condition. @@ -421,9 +421,9 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): def set_search(self, search): """ - Change the search function that filters the data in the model. + Change the search function that filters the data in the model. When this method is called, make sure: - # you call self.rebuild_data() to recalculate what should be seen + # you call self.rebuild_data() to recalculate what should be seen in the model # you reattach the model to the treeview so that the treeview updates with the new entries @@ -469,7 +469,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): self.search2 = search[2] _LOG.debug("search2 no search parameter") self._build_data = self._rebuild_search - + self.current_filter = self.search if self.has_secondary: self.current_filter2 = self.search2 @@ -477,7 +477,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): def rebuild_data(self, data_filter=None, data_filter2=None, skip=[]): """ Rebuild the data map. - + When called externally (from listview), data_filter and data_filter2 should be None; set_search will already have been called to establish the filter functions. When called internally (from __init__) both @@ -514,24 +514,24 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): items = self.number_items() _LOG.debug("rebuild search primary") - self.__rebuild_search(dfilter, skip, items, + self.__rebuild_search(dfilter, skip, items, self.gen_cursor, self.add_row) if self.has_secondary: _LOG.debug("rebuild search secondary") items = self.number_items2() - self.__rebuild_search(dfilter2, skip, items, + self.__rebuild_search(dfilter2, skip, items, self.gen_cursor2, self.add_row2) def __rebuild_search(self, dfilter, skip, items, gen_cursor, add_func): """ - Rebuild the data map for a single Gramps object type, where a search + Rebuild the data map for a single Gramps object type, where a search condition is applied. """ - pmon = progressdlg.ProgressMonitor(progressdlg.GtkProgressDialog, + pmon = progressdlg.ProgressMonitor(progressdlg.GtkProgressDialog, popup_time=2) status = progressdlg.LongOpStatus(msg=_("Building View"), - total_steps=items, interval=items//20, + total_steps=items, interval=items//20, can_cancel=True) pmon.add_op(status) with gen_cursor() as cursor: @@ -562,7 +562,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): # The tree only has primary data items = self.number_items() _LOG.debug("rebuild filter primary") - self.__rebuild_filter(dfilter, skip, items, + self.__rebuild_filter(dfilter, skip, items, self.gen_cursor, self.map, self.add_row) else: # The tree has both primary and secondary data. The navigation type @@ -570,16 +570,16 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): # secondary data. items = self.number_items2() _LOG.debug("rebuild filter secondary") - self.__rebuild_filter(dfilter2, skip, items, + self.__rebuild_filter(dfilter2, skip, items, self.gen_cursor2, self.map2, self.add_row2) - - def __rebuild_filter(self, dfilter, skip, items, gen_cursor, data_map, + + def __rebuild_filter(self, dfilter, skip, items, gen_cursor, data_map, add_func): """ - Rebuild the data map for a single Gramps object type, where a filter + Rebuild the data map for a single Gramps object type, where a filter is applied. """ - pmon = progressdlg.ProgressMonitor(progressdlg.GtkProgressDialog, + pmon = progressdlg.ProgressMonitor(progressdlg.GtkProgressDialog, popup_time=2) status = progressdlg.LongOpStatus(msg=_("Building View"), total_steps=3, interval=1) @@ -587,7 +587,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): status_ppl = progressdlg.LongOpStatus(msg=_("Loading items..."), total_steps=items, interval=items//10) pmon.add_op(status_ppl) - + self.__total += items with gen_cursor() as cursor: @@ -596,7 +596,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): handle = handle.decode('utf-8') status_ppl.heartbeat() if not handle in skip: - if not dfilter or dfilter.apply(self.db, [handle]): + if not dfilter or dfilter.match(handle, self.db): add_func(handle, data) self.__displayed += 1 status_ppl.end() @@ -606,7 +606,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): secondary=False): """ Add a node to the map. - + parent The parent node for the child. None for top level. If this node does not exist, it will be added under the top level if add_parent=True. For performance, if you have @@ -616,7 +616,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): sortkey A key by which to sort child nodes of each parent. handle The gramps handle of the object corresponding to the node. None if the node does not have a handle. - 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 """ self.clear_path_cache() @@ -652,12 +652,12 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): def _add_dup_node(self, node, parent, child, sortkey, handle, secondary): """ 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 again, and this time setting the handle Otherwise, a node should never be added twice! """ if not self.group_can_have_handle: - print ('WARNING: Attempt to add node twice to the model (%s: %s)' + print ('WARNING: Attempt to add node twice to the model (%s: %s)' % (str(parent), str(child))) return if handle: @@ -687,15 +687,15 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): self.__total -= 1 self.nodemap.del_node(node) del node - + # emit row_deleted signal self.row_deleted(path) - + def reverse_order(self): """ Reverse the order of the map. Only for Gtk 3.9+ does this signal 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 @@ -704,7 +704,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): def _reverse_level(self, node): """ - Reverse the order of a single level in the map and signal + 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 @@ -730,7 +730,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): Return the headings of the levels in the hierarchy. """ raise NotImplementedError - + def add_row(self, handle, data): """ Add a row to the model. In general this will add more than one node by @@ -759,7 +759,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): _LOG.debug(self.__class__.__name__ + ' add_row_by_handle ' + str(time.clock() - cput) + ' sec') - _LOG.debug("displayed %d / total: %d" % + _LOG.debug("displayed %d / total: %d" % (self.__displayed, self.__total)) def delete_row_by_handle(self, handle): @@ -775,7 +775,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): parent = self.nodemap.node(node.parent) self.remove_node(node) - + while parent is not None: next_parent = parent.parent and self.nodemap.node(parent.parent) if not parent.children: @@ -787,10 +787,10 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): else: self.remove_node(parent) parent = next_parent - + _LOG.debug(self.__class__.__name__ + ' delete_row_by_handle ' + str(time.clock() - cput) + ' sec') - _LOG.debug("displayed %d / total: %d" % + _LOG.debug("displayed %d / total: %d" % (self.__displayed, self.__total)) def update_row_by_handle(self, handle): @@ -804,7 +804,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): self.delete_row_by_handle(handle) self.add_row_by_handle(handle) - + # If the node hasn't moved, all we need is to call row_changed. #self.row_changed(path, node) @@ -815,7 +815,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): iter = Gtk.TreeIter() iter.stamp = self.stamp #user data should be an object, so we store the long as str - + iter.user_data = nodeid return iter @@ -823,10 +823,10 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): """ Return an iter from the node. iters are always created afresh - + Will raise IndexError if the maps are not filled yet, or if it is empty. Caller should take care of this if it allows calling with invalid path - + :param path: node as it appears in the treeview :type path: Node """ @@ -843,14 +843,14 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): def get_iter_from_handle(self, handle): """ - Get the iter for a gramps handle. Should return None if iter not + Get the iter for a gramps handle. Should return None if iter not visible """ node = self._get_node(handle) if node is None: return None return self._get_iter(node) - + def get_handle_from_iter(self, iter): """ Get the gramps handle for an iter. Return None if the iter does @@ -926,7 +926,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): raise NotImplementedError cached, data = self.get_cached_value(handle, col) - + if not cached: if not secondary: data = self.map(handle) @@ -934,7 +934,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): data = self.map2(handle) if store_cache: self.set_cached_value(handle, col, data) - + if data is None: return '' if not secondary: @@ -946,7 +946,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): if self.fmap2[col] is None: return '' value = self.fmap2[col](data) - + return value def do_get_iter(self, path): @@ -1002,8 +1002,8 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): if sib_pathtup: # Compute path to here from sibling: # parent_path + sib_path + offset - newtup = (sib_pathtup[:-1] + - (sib_pathtup[-1] + index + 2, ) + + newtup = (sib_pathtup[:-1] + + (sib_pathtup[-1] + index + 2, ) + tuple(reversed(pathlist))) #print("computed path:", iter.user_data, newtup) retval = Gtk.TreePath(newtup) @@ -1050,7 +1050,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel): else: return False, None return True, self._new_iter(nodeid) - + def do_iter_has_child(self, iter): """ Find if the given node has any children.