38
34
by a simple recursion.
41
def __init__(self, tree, filtersbank, refresh=True):
37
def __init__(self, tree, filtersbank, name=None, refresh=True):
42
38
""" Construct a layer where filters could by applied
44
40
@param tree: Original tree to filter.
53
self.callcount = {'up':0,'down':0,'both':0}
54
self._queue = processqueue.SyncQueue()
58
#Trying to have an unique string here.
59
#Note that it was "None" before
60
self.root_id = "root*90124810293810238*!@ébpo@@+épboébpon/*»«%«»(+-¡a"
52
# Set root_id by the name of FilteredTree
54
self.root_id = "anonymous_root"
56
self.root_id = "root_%s" % name
61
58
self.nodes[self.root_id] = {'parents': [], 'children': []}
62
59
self.cache_paths = {}
60
self.filter_cache = {}
64
62
# Connect to signals from MainTree
97
95
self.cllbcks[event] = [func,node_id,param]
99
def callback(self, event, node_id, path, neworder=None,async=False):
97
def callback(self, event, node_id, path, neworder=None):
100
98
""" Run a callback.
102
100
To call callback, the object must be initialized and function exists.
120
119
func,nid,param = self.cllbcks.get(event, (None,None,None))
124
self._queue.push(func, node_id, path, neworder)
126
func(node_id,path,neworder)
122
func(node_id,path,neworder)
129
self._queue.push(func, node_id, path)
134
return self._queue.flush()
137
127
#### EXTERNAL MODIFICATION ####################################################
138
128
def __external_modify(self, node_id):
139
129
return self.__update_node(node_id,direction="both")
141
def __update_node(self, node_id,direction):
131
def __update_node(self, node_id, direction):
142
132
'''update the node node_id and propagate the
143
133
change in direction (up|down|both) '''
144
135
if node_id == self.root_id:
147
#Updating the node itself.
148
138
current_display = self.is_displayed(node_id)
149
139
new_display = self.__is_displayed(node_id)
141
for fcname in self.filter_cache:
142
if node_id in self.filter_cache[fcname]['nodes']:
143
self.filter_cache[fcname]['nodes'].remove(node_id)
144
self.filter_cache[fcname]['count'] = len(self.filter_cache[fcname]['nodes'])
151
146
completely_updated = True
153
148
if not current_display and not new_display:
173
168
# Make sure parents are okay if we adding or updating
174
169
if action == 'added' or action == 'modified':
175
170
current_parents = self.nodes[node_id]['parents']
177
new_parents = self.__node_parents(node_id)
179
# for pp in self.__node_parents(node_id):
180
# if pp in self.nodes:
181
# new_parents.append(pp)
171
new_parents = self.__node_parents(node_id)
173
# When using flat filter or a recursive filter, FilteredTree
174
# might not recognize a parent correctly, make sure to check them
175
if action == 'added':
176
node = self.tree.get_node(node_id)
177
for parent_id in node.get_parents():
178
if parent_id not in new_parents and parent_id not in current_parents:
179
self.__update_node(parent_id, direction="up")
181
# Refresh list of parents after doing checkup once again
182
current_parents = self.nodes[node_id]['parents']
183
new_parents = self.__node_parents(node_id)
183
185
self.nodes[node_id]['parents'] = [parent_id for parent_id in new_parents
184
186
if parent_id in self.nodes]
192
194
if direction == "down" and self.root_id in add_to:
193
195
direction = "both"
195
#We update the parents
196
if action == 'added':
197
#This check is for "phantom parents", for example
198
#If we have a flat or leave-only filter, we have to update the
200
node = self.tree.get_node(node_id)
201
for parent in node.get_parents():
202
if parent not in new_parents and parent not in current_parents:
203
self.__update_node(parent,direction="up")
204
197
for parent_id in remove_from:
205
198
self.send_remove_tree(node_id, parent_id)
206
199
self.nodes[parent_id]['children'].remove(node_id)
223
216
#We update the node itself
224
217
#Why should we call the callback only for modify?
225
218
if action == 'modified':
226
self.callcount[direction] += 1
227
219
for path in self.get_paths_for_node(node_id):
228
self.callback(action, node_id, path,async=ASYNC_MODIFY)
220
self.callback(action, node_id, path)
230
222
#We update the children
231
223
current_children = self.nodes[node_id]['children']
241
233
for child_id in children:
242
234
self.send_remove_tree(child_id, node_id)
243
235
self.nodes[child_id]['parents'].remove(node_id)
244
# Remove node from cache
245
for parent_id in self.nodes[node_id]['parents']:
246
self.nodes[parent_id]['children'].remove(node_id)
247
self.__update_node(parent_id,direction="up")
249
del self.nodes[node_id]
251
for child_id in children:
252
236
self.__update_node(child_id,direction="down")
254
238
for path in paths:
255
239
self.callback(action, node_id, path)
257
#We update parents who are not displayed
258
#If the node is only hidden and still exists in the tree
241
# Remove node from cache
242
for parent_id in self.nodes[node_id]['parents']:
243
self.nodes[parent_id]['children'].remove(node_id)
244
self.__update_node(parent_id,direction="up")
246
self.nodes.pop(node_id)
248
# We update parents who are not displayed
249
# If the node is only hidden and still exists in the tree
259
250
if self.tree.has_node(node_id):
260
251
node = self.tree.get_node(node_id)
261
252
for parent in node.get_parents():
262
253
if parent not in self.nodes:
263
self.tree.modify_node(parent)
254
self.__update_node(parent, direction="up")
265
256
return completely_updated
298
289
def test_validity(self):
299
290
for node_id in self.nodes:
300
291
for parent_id in self.nodes[node_id]['parents']:
301
assert node_id in self.nodes[parent_id]['children']
292
assert node_id in self.nodes[parent_id]['children'], "Node '%s' is not in children of '%s'" % (node_id, parent_id)
303
294
if self.nodes[node_id]['parents'] == []:
304
assert node_id == self.root_id
295
assert node_id == self.root_id, "Node '%s' does not have parents" % (node_id)
306
297
for parent_id in self.nodes[node_id]['children']:
307
assert node_id in self.nodes[parent_id]['parents']
298
assert node_id in self.nodes[parent_id]['parents'], "Node '%s' is not in parents of '%s'" % (node_id, parent_id)
310
301
#### OTHER ####################################################################
311
302
def refilter(self):
312
303
# Find out it there is at least one flat filter
304
self.filter_cache = {}
313
305
self.__flat = False
314
306
for filter_name in self.applied_filters:
315
307
filt = self.fbank.get_filter(filter_name)
330
322
while queue != []:
331
323
node_id = queue.pop(0)
332
#FIXME: decide which is the best direction
324
# FIXME: decide which is the best direction
333
325
self.__update_node(node_id, direction="both")
335
327
node = self.tree.get_node(node_id)
439
431
if parent_id not in self.nodes:
440
432
raise Exception("Parent %s does not exists" % parent_id)
441
433
if node_id not in self.nodes[parent_id]['children']:
442
raise Exception("%s is not children of %s\n%s" % (node_id, parent_id,str(self.nodes)))
434
# Dump also state of FilteredTree => useful for debugging
435
s = "\nCurrent tree:\n"
436
for key in self.nodes:
438
s += "\t parents" + str(self.nodes[key]['parents']) + "\n"
439
s += "\t children" + str(self.nodes[key]['children']) + "\n"
440
raise Exception("%s is not children of %s\n%s" % (node_id, parent_id, s))
444
442
for parent_path in self.get_paths_for_node(parent_id):
445
443
mypath = parent_path + (node_id,)
480
478
nodes.remove(self.root_id)
483
def get_n_nodes(self, withfilters=[], include_transparent=True):
485
returns quantity of displayed nodes in this tree
486
if the withfilters is set, returns the quantity of nodes
487
that will be displayed if we apply those filters to the current
488
tree. It means that the currently applied filters are also taken into
490
If include_transparent=False, we only take into account the applied filters
491
that doesn't have the transparent parameters.
493
if withfilters == [] and include_transparent:
481
def get_n_nodes(self, withfilters=[]):
483
returns quantity of displayed nodes in this tree
484
if the withfilters is set, returns the quantity of nodes
485
that will be displayed if we apply those filters to the current
486
tree. It means that the currently applied filters are also taken into
489
return len(self.get_nodes(withfilters=withfilters))
491
def get_nodes(self, withfilters=[]):
493
returns quantity of displayed nodes in this tree
494
if the withfilters is set, returns the quantity of nodes
495
that will be displayed if we apply those filters to the current
496
tree. It means that the currently applied filters are also taken into
499
if withfilters == []:
494
500
# Use current cache
495
return len(self.nodes) - 1
496
elif withfilters != [] and include_transparent:
501
return self.get_all_nodes()
502
#FIXME maybe allow caching multiple withfilters...
503
elif len(withfilters) == 1 and withfilters[0] in self.filter_cache:
504
return self.filter_cache[withfilters[0]]['nodes']
506
# elif withfilters != []:
497
508
# Filter on the current nodes
521
# Recompute every node
523
# 1st step: build list of filters
525
for filter_name in self.applied_filters:
526
filt = self.fbank.get_filter(filter_name)
530
# Skip transparent filters if needed
531
transparent = filt.is_transparent()
532
if not include_transparent and transparent:
537
for filter_name in withfilters:
538
filt = self.fbank.get_filter(filter_name)
543
for node_id in self.tree.get_all_nodes():
546
displayed = filt.is_displayed(node_id)
528
nodes.append(node_id)
555
532
def get_node_for_path(self, path):
556
533
if not path or path == ():
680
def reset_filters(self, refresh=True, transparent_only=False):
657
def reset_filters(self, refresh=True):
682
659
Clears all filters currently set on the tree. Can't be called on
684
Remove only transparents filters if transparent_only is True
687
for f in list(self.applied_filters):
688
filt = self.fbank.get_filter(f)
690
if filt.get_parameters('transparent'):
691
self.applied_filters.remove(f)
693
print "bank is %s" % self.applied_filters
694
raise IndexError('Applied filter %s doesnt' %f +\
695
'exist anymore in the bank')
697
self.applied_filters = []
662
self.applied_filters = []