Simplify and optimize filtered model implementation

RangeFilteredLogModel is gone. The functionality is trivially implemented in
FilteredLogModel now. Changing the range is now O(log n) at worst (was O(n) at
best, for rewriting the arrays). Stacking filtered models is not supported
anymore, which simplifies the code.
This commit is contained in:
René Stadler 2012-08-24 01:50:44 +02:00 committed by Stefan Sauer
parent 3caf64118b
commit 9864042ff8
2 changed files with 53 additions and 220 deletions

View file

@ -233,41 +233,6 @@ class FilteredLogModelBase (LogModelBase):
raise NotImplementedError ("index conversion not supported")
def line_index_to_top (self, line_index):
_log_indices = [line_index]
super_index = line_index
for model in self._iter_hierarchy ():
super_index = model.line_index_to_super (super_index)
_log_indices.append (super_index)
_log_trans = " -> ".join ([str (x) for x in _log_indices])
self.logger.debug ("translated index to top: %s", _log_trans)
return super_index
def line_index_from_top (self, super_index):
_log_indices = [super_index]
line_index = super_index
for model in reversed (list (self._iter_hierarchy ())):
line_index = model.line_index_from_super (line_index)
_log_indices.append (line_index)
_log_trans = " -> ".join ([str (x) for x in _log_indices])
self.logger.debug ("translated index from top: %s", _log_trans)
return line_index
def _iter_hierarchy (self):
model = self
while hasattr (model, "super_model") and model.super_model:
yield model
model = model.super_model
class FilteredLogModel (FilteredLogModelBase):
def __init__ (self, super_model):
@ -276,29 +241,16 @@ class FilteredLogModel (FilteredLogModelBase):
self.logger = logging.getLogger ("filtered-log-model")
self.range_model = RangeFilteredLogModel (super_model)
self.filters = []
self.super_index = []
self.from_super_index = {}
self.reset ()
self.__active_process = None
self.__filter_progress = 0.
def _iter_hierarchy (self):
yield self
yield self.range_model
def reset (self):
range_model = self.range_model
range_model.reset ()
self.line_offsets = range_model.line_offsets
self.line_levels = range_model.line_levels
del self.super_index[:]
self.from_super_index.clear ()
self.line_offsets = self.super_model.line_offsets
self.line_levels = self.super_model.line_levels
self.super_index = xrange (len (self.line_offsets))
del self.filters[:]
@ -312,23 +264,14 @@ class FilteredLogModel (FilteredLogModelBase):
new_line_offsets = []
new_line_levels = []
new_super_index = []
new_from_super_index = {}
level_id = self.COL_LEVEL
func = filter.filter_func
if len (self.filters) == 1:
# This is the first filter that gets applied.
def enum ():
i = 0
for row, offset in self.iter_rows_offset ():
yield (i, row, offset,)
i += 1
else:
def enum ():
i = 0
for row, offset in self.iter_rows_offset ():
line_index = self.super_index[i]
yield (line_index, row, offset,)
i += 1
def enum ():
i = 0
for row, offset in self.iter_rows_offset ():
line_index = self.super_index[i]
yield (line_index, row, offset,)
i += 1
self.logger.debug ("running filter")
progress = 0.
progress_full = float (len (self))
@ -338,7 +281,6 @@ class FilteredLogModel (FilteredLogModelBase):
new_line_offsets.append (offset)
new_line_levels.append (row[level_id])
new_super_index.append (i)
new_from_super_index[i] = len (new_super_index) - 1
y -= 1
if y == 0:
progress += float (YIELD_LIMIT)
@ -348,7 +290,6 @@ class FilteredLogModel (FilteredLogModelBase):
self.line_offsets = new_line_offsets
self.line_levels = new_line_levels
self.super_index = new_super_index
self.from_super_index = new_from_super_index
self.logger.debug ("filtering finished")
self.__filter_progress = 1.
@ -395,112 +336,45 @@ class FilteredLogModel (FilteredLogModelBase):
def line_index_from_super (self, super_line_index):
if len (self.filters) == 0:
# Identity.
return super_line_index
try:
return self.from_super_index[super_line_index]
except KeyError:
raise IndexError ("super index %i not handled" % (super_line_index,))
return bisect_left (self.super_index, super_line_index)
def line_index_to_super (self, line_index):
if len (self.filters) == 0:
# Identity.
return line_index
return self.super_index[line_index]
def __filtered_indices_in_range (self, start, stop):
def set_range (self, super_start, super_stop):
if start < 0:
raise ValueError ("start cannot be negative (got %r)" % (start,))
old_super_start = self.line_index_to_super (0)
old_super_stop = self.line_index_to_super (len (self.super_index) - 1) + 1
super_start = bisect_left (self.super_index, start)
super_stop = bisect_left (self.super_index, stop)
self.logger.debug ("set range (%i, %i), current (%i, %i)",
super_start, super_stop, old_super_start, old_super_stop)
return super_stop - super_start
def set_range (self, start_index, stop_index):
range_model = self.range_model
old_start, old_stop = range_model.line_index_range
range_model.set_range (start_index, stop_index)
if isinstance (self.line_offsets, SubRange):
# FIXME: Can only take this shortcut when shrinking the range.
self.line_offsets = range_model.line_offsets
self.line_levels = range_model.line_levels
if len (self.filters) == 0:
# Identity.
self.super_index = xrange (super_start, super_stop)
self.line_offsets = SubRange (self.super_model.line_offsets,
super_start, super_stop)
self.line_levels = SubRange (self.super_model.line_levels,
super_start, super_stop)
return
super_start, super_stop = range_model.line_index_range
super_start_offset = super_start - old_start
if super_start_offset < 0:
if super_start < old_super_start:
# TODO:
raise NotImplementedError ("Only handling further restriction of the range"
" (start offset = %i)" % (super_start_offset,))
" (start offset = %i)" % (start_offset,))
super_end_offset = super_stop - old_stop
if super_end_offset > 0:
if super_stop > old_super_stop:
# TODO:
raise NotImplementedError ("Only handling further restriction of the range"
" (end offset = %i)" % (super_end_offset,))
" (end offset = %i)" % (stop_offset,))
if super_end_offset < 0:
if not self.super_index:
# Identity; there are no filters.
end_offset = len (self.line_offsets) + super_end_offset
else:
n_filtered = self.__filtered_indices_in_range (super_stop - super_start,
old_stop - super_start)
end_offset = len (self.line_offsets) - n_filtered
stop = len (self.line_offsets) # FIXME?
assert end_offset < stop
start = self.line_index_from_super (super_start)
stop = self.line_index_from_super (super_stop)
self.__remove_range (end_offset, stop)
if super_start_offset > 0:
if not self.super_index:
# Identity; there are no filters.
n_filtered = super_start_offset
start_offset = n_filtered
else:
n_filtered = self.__filtered_indices_in_range (0, super_start_offset)
start_offset = n_filtered
if n_filtered > 0:
self.__remove_range (0, start_offset)
from_super = self.from_super_index
for i in self.super_index:
old_index = from_super[i]
del from_super[i]
from_super[i - super_start_offset] = old_index - start_offset
for i in range (len (self.super_index)):
self.super_index[i] -= super_start_offset
def __remove_range (self, start, stop):
if start < 0:
raise ValueError ("start cannot be negative (got %r)" % (start,))
if start == stop:
return
if stop > len (self.line_offsets):
raise ValueError ("stop value out of range (got %r)" % (stop,))
if start > stop:
raise ValueError ("start cannot be greater than stop (got %r, %r)" % (start, stop,))
self.logger.debug ("removing line range (%i, %i)",
start, stop)
del self.line_offsets[start:stop]
del self.line_levels[start:stop]
for super_index in self.super_index[start:stop]:
del self.from_super_index[super_index]
del self.super_index[start:stop]
self.super_index = SubRange (self.super_index, start, stop)
self.line_offsets = SubRange (self.line_offsets, start, stop)
self.line_levels = SubRange (self.line_levels, start, stop)
class SubRange (object):
@ -511,6 +385,12 @@ class SubRange (object):
if start > stop:
raise ValueError ("need start <= stop (got %r, %r)" % (start, stop,))
if type (l) == type (self):
# Another SubRange, don't stack:
start += l.start
stop += l.start
l = l.l
self.l = l
self.start = start
self.stop = stop
@ -529,51 +409,6 @@ class SubRange (object):
for i in xrange (self.start, self.stop):
yield l[i]
class RangeFilteredLogModel (FilteredLogModelBase):
def __init__ (self, super_model):
FilteredLogModelBase.__init__ (self, super_model)
self.logger = logging.getLogger ("range-filtered-model")
self.line_index_range = None
def set_range (self, start_index, stop_index):
self.logger.debug ("setting range to start = %i, stop = %i",
start_index, stop_index)
self.line_index_range = (start_index, stop_index,)
self.line_offsets = SubRange (self.super_model.line_offsets,
start_index, stop_index)
self.line_levels = SubRange (self.super_model.line_levels,
start_index, stop_index)
def reset (self):
self.logger.debug ("reset")
start_index = 0
stop_index = len (self.super_model)
self.set_range (start_index, stop_index,)
def line_index_to_super (self, line_index):
start_index = self.line_index_range[0]
return line_index + start_index
def line_index_from_super (self, li):
start, stop = self.line_index_range
if li < start or li >= stop:
raise IndexError ("not in range")
return li - start
class LineViewLogModel (FilteredLogModelBase):
def __init__ (self, super_model):
@ -627,4 +462,3 @@ class LineViewLogModel (FilteredLogModelBase):
path = (line_index,)
self.row_deleted (path)

View file

@ -41,8 +41,7 @@ from GstDebugViewer.GUI.filters import (CategoryFilter,
from GstDebugViewer.GUI.models import (FilteredLogModel,
LazyLogModel,
LineViewLogModel,
LogModelBase,
RangeFilteredLogModel)
LogModelBase)
def action (func):
@ -119,8 +118,8 @@ class LineView (object):
line_index = path[0]
line_model = view.get_model ()
log_model = self.log_view.get_model ()
top_index = line_model.line_index_to_top (line_index)
log_index = log_model.line_index_from_top (top_index)
super_index = line_model.line_index_to_super (line_index)
log_index = log_model.line_index_from_super (super_index)
path = (log_index,)
self.log_view.scroll_to_cell (path, use_align = True, row_align = .5)
sel = self.log_view.get_selection ()
@ -131,7 +130,7 @@ class LineView (object):
log_model = view.get_model ()
line_index = path[0]
top_line_index = log_model.line_index_to_top (line_index)
super_index = log_model.line_index_to_super (line_index)
line_model = self.line_view.get_model ()
if line_model is None:
return
@ -143,14 +142,14 @@ class LineView (object):
else:
position = 0
if len (line_model) > 1:
other_index = line_model.line_index_to_top (position - 1)
other_index = line_model.line_index_to_super (position - 1)
else:
other_index = -1
if other_index == top_line_index and position != 1:
if other_index == super_index and position != 1:
# Already have the line.
pass
else:
line_model.insert_line (position, top_line_index)
line_model.insert_line (position, super_index)
self.clear_action.props.sensitive = True
def handle_log_view_selection_changed (self, selection):
@ -165,7 +164,7 @@ class LineView (object):
return
path = model.get_path (tree_iter)
line_index = model.line_index_to_top (path[0])
line_index = model.line_index_to_super (path[0])
if len (line_model) == 0:
line_model.insert_line (0, line_index)
@ -409,7 +408,7 @@ class Window (object):
super_index = None
self.logger.debug ("no line selected")
else:
super_index = model.line_index_to_top (line_index)
super_index = model.line_index_to_super (line_index)
self.logger.debug ("pushing selected line %i (abs %i)",
line_index, super_index)
@ -419,7 +418,7 @@ class Window (object):
if vis_range is not None:
start_path, end_path = vis_range
start_index = start_path[0]
self.default_start_index = model.line_index_to_top (start_index)
self.default_start_index = model.line_index_to_super (start_index)
def update_model (self, model = None):
@ -445,7 +444,7 @@ class Window (object):
if selected_index is not None:
try:
select_index = model.line_index_from_top (selected_index)
select_index = model.line_index_from_super (selected_index)
except IndexError as exc:
self.logger.debug ("abs line index %i filtered out, not reselecting",
selected_index)
@ -467,7 +466,7 @@ class Window (object):
yield i
for current_index in traverse ():
try:
target_index = model.line_index_from_top (current_index)
target_index = model.line_index_from_super (current_index)
except IndexError:
continue
else:
@ -574,16 +573,16 @@ class Window (object):
return
if after:
first_index = model.line_index_to_top (0)
last_index = model.line_index_to_top (filtered_line_index)
first_index = model.line_index_to_super (0)
last_index = model.line_index_to_super (filtered_line_index)
self.logger.info ("hiding lines after %i (abs %i), first line is abs %i",
filtered_line_index,
last_index,
first_index)
else:
first_index = model.line_index_to_top (filtered_line_index)
last_index = model.line_index_to_top (len (model) - 1)
first_index = model.line_index_to_super (filtered_line_index)
last_index = model.line_index_to_super (len (model) - 1)
self.logger.info ("hiding lines before %i (abs %i), last line is abs %i",
filtered_line_index,