Skip to content

Instantly share code, notes, and snippets.

@geojeff
Last active December 20, 2015 01:49
Show Gist options
  • Save geojeff/6052347 to your computer and use it in GitHub Desktop.
Save geojeff/6052347 to your computer and use it in GitHub Desktop.
in properties.pxd:
cdef class RecordingListProperty(Property):
cdef int op_started
cdef object op_info
cdef int sort_started
cdef list sort_largs
cdef list sort_kwds
cdef str sort_op
cdef list presort_indices
cdef list presort_items
class RecordingObservableList(list):
# Adds range-observing and other intelligence to a Python list, passing
# op_info for use by an observer.
def __init__(self, *largs):
self.prop = largs[0]
self.obj = ref(largs[1])
super(RecordingObservableList, self).__init__(*largs[2:])
# TODO: setitem and delitem are supposed to handle slices, instead of the
# deprecated setslice() and delslice() methods.
def __setitem__(self, key, value):
cdef Property prop = self.prop
self.prop.start_op()
list.__setitem__(self, key, value)
self.prop.record_op_info(('ROL_setitem', (key, key)))
observable_list_dispatch(self)
self.prop.stop_op()
def __delitem__(self, key):
cdef Property prop = self.prop
prop.start_op()
list.__delitem__(self, key)
prop.record_op_info(('ROL_delitem', (key, key)))
observable_list_dispatch(self)
prop.stop_op()
def __setslice__(self, *largs):
cdef Property prop = self.prop
prop.start_op()
#
# Python docs:
#
# operator.__setslice__(a, b, c, v)
#
# Set the slice of a from index b to index c-1 to the sequence v.
#
# Deprecated since version 2.6: This function is removed in Python
# 3.x. Use setitem() with a slice index.
#
start_index = largs[0]
end_index = largs[1] - 1
list.__setslice__(self, *largs)
prop.record_op_info(('ROL_setslice', (start_index, end_index)))
observable_list_dispatch(self)
prop.stop_op()
def __delslice__(self, *largs):
cdef Property prop = self.prop
prop.start_op()
# Delete the slice of a from index b to index c-1. del a[b:c],
# where the args here are b and c.
# Also deprecated.
start_index = largs[0]
end_index = largs[1] - 1
list.__delslice__(self, *largs)
prop.record_op_info(('ROL_delslice', (start_index, end_index)))
observable_list_dispatch(self)
prop.stop_op()
def __iadd__(self, *largs):
cdef Property prop = self.prop
prop.start_op()
start_index = len(self)
end_index = start_index + len(largs) - 1
list.__iadd__(self, *largs)
prop.record_op_info(('ROL_iadd', (start_index, end_index)))
observable_list_dispatch(self)
prop.stop_op()
def __imul__(self, *largs):
cdef Property prop = self.prop
prop.start_op()
num = largs[0]
start_index = len(self)
end_index = start_index + (len(self) * num)
list.__imul__(self, *largs)
prop.record_op_info(('ROL_imul', (start_index, end_index)))
observable_list_dispatch(self)
prop.stop_op()
def append(self, *largs):
cdef Property prop = self.prop
prop.start_op()
index = len(self)
list.append(self, *largs)
prop.record_op_info(('ROL_append', (index, index)))
observable_list_dispatch(self)
prop.stop_op()
def remove(self, *largs):
cdef Property prop = self.prop
prop.start_op()
index = self.index(largs[0])
list.remove(self, *largs)
prop.record_op_info(('ROL_remove', (index, index)))
observable_list_dispatch(self)
prop.stop_op()
def insert(self, *largs):
cdef Property prop = self.prop
prop.start_op()
index = largs[0]
list.insert(self, *largs)
prop.record_op_info(('ROL_insert', (index, index)))
observable_list_dispatch(self)
prop.stop_op()
def pop(self, *largs):
cdef Property prop = self.prop
prop.start_op()
if largs:
index = largs[0]
else:
index = len(self) - 1
cdef object result = list.pop(self, *largs)
prop.record_op_info(('ROL_pop', (index, index)))
observable_list_dispatch(self)
prop.stop_op()
return result
def extend(self, *largs):
cdef Property prop = self.prop
prop.start_op()
start_index = len(self)
end_index = start_index + len(largs[0]) - 1
list.extend(self, *largs)
prop.record_op_info(('ROL_extend', (start_index, end_index)))
observable_list_dispatch(self)
prop.stop_op()
def start_sort_op(self, op, *largs, **kwds):
cdef Property prop = self.prop
prop.start_op()
prop.sort_largs = largs
prop.sort_kwds = kwds
prop.sort_op = op
# Trigger the "sort is starting" callback to the adapter, so it can do
# pre-sort writing of the current arrangement of indices and data.
prop.start_sort_op()
prop.stop_op()
def finish_sort_op(self):
cdef Property prop = self.prop
prop.start_op()
largs = prop.sort_largs
kwds = prop.sort_kwds
sort_op = prop.sort_op
# Perform the sort.
if sort_op == 'ROL_sort':
list.sort(self, *largs, **kwds)
else:
list.reverse(self, *largs)
# Finalize. Will go back to adapter for handling cached_views,
# selection, and prep for triggering data_changed on ListView.
prop.record_op_info((sort_op, (0, len(self) - 1)))
prop.stop_op()
prop.stop_sort_op()
def sort(self, *largs, **kwds):
cdef Property prop = self.prop
prop.start_sort_op('ROL_sort', *largs, **kwds)
def reverse(self, *largs):
cdef Property prop = self.prop
prop.start_sort_op('ROL_reverse', *largs)
cdef class RecordingListProperty(Property):
'''Property that represents a list, adding granular dispatching per op.
Only lists are allowed. Tuple or any other classes are forbidden.
'''
def __cinit__(self):
self.op_started = 0
self.op_info = None
self.sort_started = False
self.sort_largs = []
self.sort_kwds = []
self.sort_op = ''
# This association has keys as the indices of the owner's
# cached_views and the owner's data items, for use in post-sort
# widget reordering. It is set by the owner when needed. It is
# cleared by the owner at the end of its sort op callback.
self.presort_indices = []
self.presort_items = []
def __init__(self, defaultvalue=None, **kw):
defaultvalue = defaultvalue or []
self.op_started = 0
self.op_info = None
self.sort_started = False
self.sort_largs = []
self.sort_kwds = []
self.sort_op = ''
super(RecordingListProperty, self).__init__(defaultvalue, **kw)
cpdef link(self, EventDispatcher obj, str name):
Property.link(self, obj, name)
cdef PropertyStorage ps = obj.__storage[self._name]
ps.value = RecordingObservableList(self, obj, ps.value)
cdef check(self, EventDispatcher obj, value):
if Property.check(self, obj, value):
return True
if type(value) is not RecordingObservableList:
raise ValueError('{}.{} accept only {}'.format(
obj.__class__.__name__,
self.name,
RecordingObservableList.__name__))
cpdef set(self, EventDispatcher obj, value):
value = RecordingObservableList(self, obj, value)
Property.set(self, obj, value)
def record_op_info(self, op_info):
self.op_info = op_info
def start_op(self):
print dir(self)
self.op_started = True
def stop_op(self):
self.op_started = False
def start_sort_op(self):
self.sort_started = True
def stop_sort_op(self):
self.sort_started = False
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment