Ajout du GUI
This commit is contained in:
194
kivy/uix/recycleview/datamodel.py
Normal file
194
kivy/uix/recycleview/datamodel.py
Normal file
@@ -0,0 +1,194 @@
|
||||
'''
|
||||
RecycleView Data Model
|
||||
======================
|
||||
|
||||
.. versionadded:: 1.10.0
|
||||
|
||||
The data model part of the RecycleView model-view-controller pattern.
|
||||
|
||||
It defines the models (classes) that store the data associated with a
|
||||
:class:`~kivy.uix.recycleview.RecycleViewBehavior`. Each model (class)
|
||||
determines how the data is stored and emits requests to the controller
|
||||
(:class:`~kivy.uix.recycleview.RecycleViewBehavior`) when the data is
|
||||
modified.
|
||||
'''
|
||||
|
||||
from kivy.properties import ListProperty, ObservableDict, ObjectProperty
|
||||
from kivy.event import EventDispatcher
|
||||
from functools import partial
|
||||
|
||||
__all__ = ('RecycleDataModelBehavior', 'RecycleDataModel')
|
||||
|
||||
|
||||
def recondition_slice_assign(val, last_len, new_len):
|
||||
if not isinstance(val, slice):
|
||||
return slice(val, val + 1)
|
||||
|
||||
diff = new_len - last_len
|
||||
|
||||
start, stop, step = val.start, val.stop, val.step
|
||||
if stop <= start:
|
||||
return slice(0, 0)
|
||||
|
||||
if step is not None and step != 1:
|
||||
assert last_len == new_len
|
||||
if stop < 0:
|
||||
stop = max(0, last_len + stop)
|
||||
stop = min(last_len, stop)
|
||||
|
||||
if start < 0:
|
||||
start = max(0, last_len + start)
|
||||
start = min(last_len, start)
|
||||
|
||||
return slice(start, stop, step)
|
||||
|
||||
if start < 0:
|
||||
start = last_len + start
|
||||
if stop < 0:
|
||||
stop = last_len + stop
|
||||
|
||||
# whatever, too complicated don't try to compute it
|
||||
if (start < 0 or stop < 0 or start > last_len or stop > last_len or
|
||||
new_len != last_len):
|
||||
return None
|
||||
|
||||
return slice(start, stop)
|
||||
|
||||
|
||||
class RecycleDataModelBehavior(object):
|
||||
""":class:`RecycleDataModelBehavior` is the base class for the models
|
||||
that describes and provides the data for the
|
||||
:class:`~kivy.uix.recycleview.RecycleViewBehavior`.
|
||||
|
||||
:Events:
|
||||
`on_data_changed`:
|
||||
Fired when the data changes. The event may dispatch
|
||||
keyword arguments specific to each implementation of the data
|
||||
model.
|
||||
When dispatched, the event and keyword arguments are forwarded to
|
||||
:meth:`~kivy.uix.recycleview.RecycleViewBehavior.\
|
||||
refresh_from_data`.
|
||||
"""
|
||||
|
||||
__events__ = ("on_data_changed", )
|
||||
|
||||
recycleview = ObjectProperty(None, allownone=True)
|
||||
'''The
|
||||
:class:`~kivy.uix.recycleview.RecycleViewBehavior` instance
|
||||
associated with this data model.
|
||||
'''
|
||||
|
||||
def attach_recycleview(self, rv):
|
||||
'''Associates a
|
||||
:class:`~kivy.uix.recycleview.RecycleViewBehavior` with
|
||||
this data model.
|
||||
'''
|
||||
self.recycleview = rv
|
||||
if rv:
|
||||
self.fbind('on_data_changed', rv.refresh_from_data)
|
||||
|
||||
def detach_recycleview(self):
|
||||
'''Removes the
|
||||
:class:`~kivy.uix.recycleview.RecycleViewBehavior`
|
||||
associated with this data model.
|
||||
'''
|
||||
rv = self.recycleview
|
||||
if rv:
|
||||
self.funbind('on_data_changed', rv.refresh_from_data)
|
||||
self.recycleview = None
|
||||
|
||||
def on_data_changed(self, *largs, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class RecycleDataModel(RecycleDataModelBehavior, EventDispatcher):
|
||||
'''An implementation of :class:`RecycleDataModelBehavior` that keeps the
|
||||
data in a indexable list. See :attr:`data`.
|
||||
|
||||
When data changes this class currently dispatches `on_data_changed` with
|
||||
one of the following additional keyword arguments.
|
||||
|
||||
`none`: no keyword argument
|
||||
With no additional argument it means a generic data change.
|
||||
`removed`: a slice or integer
|
||||
The value is a slice or integer indicating the indices removed.
|
||||
`appended`: a slice
|
||||
The slice in :attr:`data` indicating the first and last new items
|
||||
(i.e. the slice pointing to the new items added at the end).
|
||||
`inserted`: a integer
|
||||
The index in :attr:`data` where a new data item was inserted.
|
||||
`modified`: a slice
|
||||
The slice with the indices where the data has been modified.
|
||||
This currently does not allow changing of size etc.
|
||||
'''
|
||||
|
||||
data = ListProperty([])
|
||||
'''Stores the model's data using a list.
|
||||
|
||||
The data for a item at index `i` can also be accessed with
|
||||
:class:`RecycleDataModel` `[i]`.
|
||||
'''
|
||||
|
||||
_last_len = 0
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.fbind('data', self._on_data_callback)
|
||||
super(RecycleDataModel, self).__init__(**kwargs)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.data[index]
|
||||
|
||||
@property
|
||||
def observable_dict(self):
|
||||
'''A dictionary instance, which when modified will trigger a `data` and
|
||||
consequently an `on_data_changed` dispatch.
|
||||
'''
|
||||
return partial(ObservableDict, self.__class__.data, self)
|
||||
|
||||
def attach_recycleview(self, rv):
|
||||
super(RecycleDataModel, self).attach_recycleview(rv)
|
||||
if rv:
|
||||
self.fbind('data', rv._dispatch_prop_on_source, 'data')
|
||||
|
||||
def detach_recycleview(self):
|
||||
rv = self.recycleview
|
||||
if rv:
|
||||
self.funbind('data', rv._dispatch_prop_on_source, 'data')
|
||||
super(RecycleDataModel, self).detach_recycleview()
|
||||
|
||||
def _on_data_callback(self, instance, value):
|
||||
last_len = self._last_len
|
||||
new_len = self._last_len = len(self.data)
|
||||
op, val = value.last_op
|
||||
|
||||
if op == '__setitem__':
|
||||
val = recondition_slice_assign(val, last_len, new_len)
|
||||
if val is not None:
|
||||
self.dispatch('on_data_changed', modified=val)
|
||||
else:
|
||||
self.dispatch('on_data_changed')
|
||||
elif op == '__delitem__':
|
||||
self.dispatch('on_data_changed', removed=val)
|
||||
elif op == '__setslice__':
|
||||
val = recondition_slice_assign(slice(*val), last_len, new_len)
|
||||
if val is not None:
|
||||
self.dispatch('on_data_changed', modified=val)
|
||||
else:
|
||||
self.dispatch('on_data_changed')
|
||||
elif op == '__delslice__':
|
||||
self.dispatch('on_data_changed', removed=slice(*val))
|
||||
elif op == '__iadd__' or op == '__imul__':
|
||||
self.dispatch('on_data_changed', appended=slice(last_len, new_len))
|
||||
elif op == 'append':
|
||||
self.dispatch('on_data_changed', appended=slice(last_len, new_len))
|
||||
elif op == 'insert':
|
||||
self.dispatch('on_data_changed', inserted=val)
|
||||
elif op == 'pop':
|
||||
if val:
|
||||
self.dispatch('on_data_changed', removed=val[0])
|
||||
else:
|
||||
self.dispatch('on_data_changed', removed=last_len - 1)
|
||||
elif op == 'extend':
|
||||
self.dispatch('on_data_changed', appended=slice(last_len, new_len))
|
||||
else:
|
||||
self.dispatch('on_data_changed')
|
||||
Reference in New Issue
Block a user