mirror of https://github.com/kivy/kivy.git
Changed arguments checking in ListView to account for possible use of kv in the definition of a ListView. Added several new examples and tests, and new entries in ListView docs. Also deleted ObserverView, which is not used anywhere. The pre-commit hook was not allowing this commit, because a file was deleted and the style check failed to find it. So, modified the pre-commit hook to use a different method to find staged files.
This commit is contained in:
parent
e9c5b01231
commit
254497d07b
|
@ -44,9 +44,6 @@ class FruitDetailView(GridLayout):
|
|||
|
||||
self.redraw()
|
||||
|
||||
# Used in the list_cascade_oo.py example (ObjectAdapter and ObserverView
|
||||
# example).
|
||||
#
|
||||
class FruitObserverDetailView(GridLayout):
|
||||
fruit_name = StringProperty('')
|
||||
|
||||
|
@ -124,7 +121,7 @@ class FruitImageDetailView(BoxLayout):
|
|||
# Is selected_object an instance of ThumbnailedListItem (composite)?
|
||||
#
|
||||
# Or is it a ListItemButton?
|
||||
#
|
||||
#
|
||||
if hasattr(selected_object, 'fruit_name'):
|
||||
self.fruit_name = selected_object.fruit_name
|
||||
else:
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
from kivy.uix.modalview import ModalView
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.lang import Builder
|
||||
|
||||
Builder.load_string("""
|
||||
<ListViewModal>:
|
||||
size_hint: None,None
|
||||
size: 400,400
|
||||
ListView:
|
||||
size_hint: .8,.8
|
||||
item_strings: [str(index) for index in xrange(100)]
|
||||
""")
|
||||
|
||||
|
||||
class ListViewModal(ModalView):
|
||||
def __init__(self, **kwargs):
|
||||
super(ListViewModal, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class MainView(GridLayout):
|
||||
"""Implementation of a list view declared in a kv template.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs['cols'] = 1
|
||||
kwargs['size_hint'] = (1.0, 1.0)
|
||||
super(MainView, self).__init__(**kwargs)
|
||||
|
||||
listview_modal = ListViewModal()
|
||||
|
||||
self.add_widget(listview_modal)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from kivy.base import runTouchApp
|
||||
runTouchApp(MainView(width=800))
|
|
@ -0,0 +1,49 @@
|
|||
from kivy.uix.modalview import ModalView
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.lang import Builder
|
||||
from kivy.factory import Factory
|
||||
|
||||
# Note the special nature of indentation in the adapter declaration, where
|
||||
# the adapter: is on one line, then the value side must be given at one level
|
||||
# of indentation.
|
||||
|
||||
Builder.load_string("""
|
||||
#:import label kivy.uix.label
|
||||
#:import sla kivy.adapters.simplelistadapter
|
||||
|
||||
<ListViewModal>:
|
||||
size_hint: None,None
|
||||
size: 400,400
|
||||
ListView:
|
||||
size_hint: .8,.8
|
||||
adapter:
|
||||
sla.SimpleListAdapter(
|
||||
data=["Item #{0}".format(i) for i in xrange(100)],
|
||||
cls=label.Label)
|
||||
""")
|
||||
|
||||
|
||||
class ListViewModal(ModalView):
|
||||
def __init__(self, **kwargs):
|
||||
super(ListViewModal, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class MainView(GridLayout):
|
||||
"""
|
||||
Implementation of a ListView using the kv language.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs['cols'] = 1
|
||||
kwargs['size_hint'] = (1.0, 1.0)
|
||||
super(MainView, self).__init__(**kwargs)
|
||||
|
||||
listview_modal = ListViewModal()
|
||||
|
||||
self.add_widget(listview_modal)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from kivy.base import runTouchApp
|
||||
runTouchApp(MainView(width=800))
|
|
@ -88,9 +88,69 @@ class ListViewTestCase(unittest.TestCase):
|
|||
del list_view.adapter.data[49]
|
||||
self.assertEqual(len(list_view.adapter.data), 99)
|
||||
|
||||
def test_list_view_bad_instantiation(self):
|
||||
with self.assertRaises(Exception) as cm:
|
||||
listview = ListView()
|
||||
def test_list_view_declared_in_kv_with_item_strings(self):
|
||||
from kivy.lang import Builder
|
||||
from kivy.uix.modalview import ModalView
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import StringProperty, ObjectProperty, \
|
||||
BooleanProperty
|
||||
|
||||
msg = 'ListView: item_strings needed or an adapter'
|
||||
self.assertEqual(str(cm.exception), msg)
|
||||
Builder.load_string("""
|
||||
#:import label kivy.uix.label
|
||||
#:import sla kivy.adapters.simplelistadapter
|
||||
|
||||
<ListViewModal>:
|
||||
size_hint: None,None
|
||||
size: 400,400
|
||||
lvm: lvm
|
||||
ListView:
|
||||
id: lvm
|
||||
size_hint: .8,.8
|
||||
item_strings: ["Item #{0}".format(i) for i in xrange(100)]
|
||||
""")
|
||||
|
||||
class ListViewModal(ModalView):
|
||||
def __init__(self, **kwargs):
|
||||
super(ListViewModal, self).__init__(**kwargs)
|
||||
|
||||
list_view_modal = ListViewModal()
|
||||
|
||||
list_view = list_view_modal.lvm
|
||||
|
||||
self.assertEqual(len(list_view.adapter.data), 100)
|
||||
|
||||
def test_list_view_declared_in_kv_with_adapter(self):
|
||||
from kivy.lang import Builder
|
||||
from kivy.uix.modalview import ModalView
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import StringProperty, ObjectProperty, \
|
||||
BooleanProperty
|
||||
|
||||
Builder.load_string("""
|
||||
#:import label kivy.uix.label
|
||||
#:import sla kivy.adapters.simplelistadapter
|
||||
|
||||
<ListViewModal>:
|
||||
size_hint: None,None
|
||||
size: 400,400
|
||||
lvm: lvm
|
||||
ListView:
|
||||
id: lvm
|
||||
size_hint: .8,.8
|
||||
adapter:
|
||||
sla.SimpleListAdapter(
|
||||
data=["Item #{0}".format(i) for i in xrange(100)],
|
||||
cls=label.Label)
|
||||
""")
|
||||
|
||||
class ListViewModal(ModalView):
|
||||
def __init__(self, **kwargs):
|
||||
super(ListViewModal, self).__init__(**kwargs)
|
||||
|
||||
list_view_modal = ListViewModal()
|
||||
|
||||
list_view = list_view_modal.lvm
|
||||
|
||||
self.assertEqual(len(list_view.adapter.data), 100)
|
||||
|
|
|
@ -28,9 +28,28 @@ srcdir = join(kivydir, 'kivy')
|
|||
script = join(srcdir, 'tools', 'pep8checker', 'pep8kivy.py')
|
||||
|
||||
# Only check the files that were staged
|
||||
proc = Popen(['git', 'diff', '--cached', '--name-only', 'HEAD'], stdout=PIPE)
|
||||
#proc = Popen(['git', 'diff', '--cached', '--name-only', 'HEAD'], stdout=PIPE)
|
||||
#targets = [join(kivydir, target) for target in proc.stdout]
|
||||
|
||||
# Correction: only check the files that were staged, but do not include
|
||||
# deleted files.
|
||||
proc = Popen(['git', 'diff', '--cached', '--name-status', 'HEAD'], stdout=PIPE)
|
||||
proc.wait()
|
||||
targets = [join(kivydir, target) for target in proc.stdout]
|
||||
|
||||
# This gives output like the following:
|
||||
#
|
||||
# A examples/widgets/lists/list_simple_in_kv.py
|
||||
# A examples/widgets/lists/list_simple_in_kv_2.py
|
||||
# D kivy/uix/observerview.py
|
||||
#
|
||||
# So check for D entries and remove them from targets.
|
||||
#
|
||||
targets = []
|
||||
for target in proc.stdout:
|
||||
parts = [p.strip() for p in target.split()]
|
||||
if parts[0] != 'D':
|
||||
targets.append(join(kivydir, target))
|
||||
|
||||
call(['git', 'stash', 'save', '--keep-index', '--quiet'])
|
||||
retval = call([sys.executable, script, srcdir] + targets)
|
||||
call(['git', 'stash', 'pop', '--quiet'])
|
||||
|
|
|
@ -33,6 +33,8 @@ from the names of the examples that they illustrate the "ramping up" from
|
|||
simple to advanced:
|
||||
|
||||
* kivy/examples/widgets/lists/list_simple.py
|
||||
* kivy/examples/widgets/lists/list_simple_in_kv.py
|
||||
* kivy/examples/widgets/lists/list_simple_in_kv_2.py
|
||||
* kivy/examples/widgets/lists/list_master_detail.py
|
||||
* kivy/examples/widgets/lists/list_two_up.py
|
||||
* kivy/examples/widgets/lists/list_kv.py
|
||||
|
@ -78,6 +80,44 @@ In its simplest form, we make a listview with 100 items::
|
|||
from kivy.base import runTouchApp
|
||||
runTouchApp(MainView(width=800))
|
||||
|
||||
Or, we could declare the listview in using the kv language::
|
||||
|
||||
from kivy.uix.modalview import ModalView
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.lang import Builder
|
||||
|
||||
Builder.load_string("""
|
||||
<ListViewModal>:
|
||||
size_hint: None,None
|
||||
size: 400,400
|
||||
ListView:
|
||||
size_hint: .8,.8
|
||||
item_strings: [str(index) for index in xrange(100)]
|
||||
""")
|
||||
|
||||
|
||||
class ListViewModal(ModalView):
|
||||
def __init__(self, **kwargs):
|
||||
super(ListViewModal, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class MainView(GridLayout):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs['cols'] = 1
|
||||
kwargs['size_hint'] = (1.0, 1.0)
|
||||
super(MainView, self).__init__(**kwargs)
|
||||
|
||||
listview_modal = ListViewModal()
|
||||
|
||||
self.add_widget(listview_modal)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from kivy.base import runTouchApp
|
||||
runTouchApp(MainView(width=800))
|
||||
|
||||
Using an Adapter
|
||||
-------------------
|
||||
|
||||
|
@ -108,6 +148,56 @@ strings. Each item string is set by
|
|||
:class:`~kivy.adapters.simplelistadapter.SimpleListAdapter` as the *text*
|
||||
argument for each Label instantiation.
|
||||
|
||||
You can declare a ListView with an adapter in a kv file, with special attention
|
||||
given to the way longer python blocks are indented::
|
||||
|
||||
from kivy.uix.modalview import ModalView
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.lang import Builder
|
||||
from kivy.factory import Factory
|
||||
|
||||
# Note the special nature of indentation in the adapter declaration, where
|
||||
# the adapter: is on one line, then the value side must be given at one
|
||||
# level of indentation.
|
||||
|
||||
Builder.load_string("""
|
||||
#:import label kivy.uix.label
|
||||
#:import sla kivy.adapters.simplelistadapter
|
||||
|
||||
<ListViewModal>:
|
||||
size_hint: None,None
|
||||
size: 400,400
|
||||
ListView:
|
||||
size_hint: .8,.8
|
||||
adapter:
|
||||
sla.SimpleListAdapter(
|
||||
data=["Item #{0}".format(i) for i in xrange(100)],
|
||||
cls=label.Label)
|
||||
""")
|
||||
|
||||
|
||||
class ListViewModal(ModalView):
|
||||
def __init__(self, **kwargs):
|
||||
super(ListViewModal, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class MainView(GridLayout):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs['cols'] = 1
|
||||
kwargs['size_hint'] = (1.0, 1.0)
|
||||
super(MainView, self).__init__(**kwargs)
|
||||
|
||||
listview_modal = ListViewModal()
|
||||
|
||||
self.add_widget(listview_modal)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from kivy.base import runTouchApp
|
||||
runTouchApp(MainView(width=800))
|
||||
|
||||
ListAdapter and DictAdapter
|
||||
---------------------------
|
||||
|
||||
|
@ -804,15 +894,22 @@ class ListView(AbstractView, EventDispatcher):
|
|||
|
||||
def __init__(self, **kwargs):
|
||||
# Check for an adapter argument. If it doesn't exist, we
|
||||
# assume that item_strings is to be used with SimpleListAdapter
|
||||
# to make a simple list. In this case, if item_strings was not
|
||||
# provided, raise an exception.
|
||||
# check for item_strings in use with SimpleListAdapter
|
||||
# to make a simple list.
|
||||
if 'adapter' not in kwargs:
|
||||
if 'item_strings' not in kwargs:
|
||||
raise Exception('ListView: item_strings needed or an adapter')
|
||||
|
||||
list_adapter = SimpleListAdapter(data=kwargs['item_strings'],
|
||||
cls=Label)
|
||||
# Could be missing, or it could be that the ListView is
|
||||
# declared in a kv file. If kv is in use, and item_strings is
|
||||
# declared there, then item_strings will not be set until after
|
||||
# __init__(). So, the data=[] set will temporarily serve for
|
||||
# SimpleListAdapter instantiation, with the binding to
|
||||
# item_strings_changed() handling the eventual set of the
|
||||
# item_strings property from the application of kv rules.
|
||||
list_adapter = SimpleListAdapter(data=[],
|
||||
cls=Label)
|
||||
else:
|
||||
list_adapter = SimpleListAdapter(data=kwargs['item_strings'],
|
||||
cls=Label)
|
||||
kwargs['adapter'] = list_adapter
|
||||
|
||||
self.register_event_type('on_scroll_complete')
|
||||
|
@ -823,6 +920,7 @@ class ListView(AbstractView, EventDispatcher):
|
|||
|
||||
self.bind(size=self._trigger_populate,
|
||||
pos=self._trigger_populate,
|
||||
item_strings=self.item_strings_changed,
|
||||
adapter=self._trigger_populate)
|
||||
|
||||
# The bindings setup above sets self._trigger_populate() to fire
|
||||
|
@ -832,6 +930,11 @@ class ListView(AbstractView, EventDispatcher):
|
|||
# bindings back to the view updating function here.
|
||||
self.adapter.bind_triggers_to_view(self._trigger_populate)
|
||||
|
||||
# Added to set data when item_strings is set in a kv template, but it will
|
||||
# be good to have also if item_strings is reset generally.
|
||||
def item_strings_changed(self, *args):
|
||||
self.adapter.data = self.item_strings
|
||||
|
||||
def _scroll(self, scroll_y):
|
||||
if self.row_height is None:
|
||||
return
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
'''
|
||||
ObserverView
|
||||
============
|
||||
|
||||
.. versionadded:: 1.5
|
||||
|
||||
:class:`ObserverView` is a container for a view that observes an object held
|
||||
by an :class:`SelectionAdapter` instance. It is similar to :class:`ListView`
|
||||
in the way an adapter and view instance are held, and in the way a get_view()
|
||||
method works in concert with the adapter. It only has to manage one object,
|
||||
however, so is a bit simpler. The update() method is the analog of the
|
||||
populate() method in :class:`ListView`.
|
||||
'''
|
||||
|
||||
from kivy.adapters.selectionadapter import SelectionAdapter
|
||||
from kivy.adapters.collectionadapter import CollectionAdapter
|
||||
from kivy.properties import ObjectProperty, ListProperty, StringProperty
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
|
||||
|
||||
class ObserverView(BoxLayout):
|
||||
|
||||
adapter = ObjectProperty(None)
|
||||
'''This is always a :class:`SelectionAdapter` instance. The obj value in
|
||||
the object adapter is at the heart of operations here. This is the
|
||||
observed value whose changes trigger refreshing of the contained view.
|
||||
'''
|
||||
|
||||
view_instance = ObjectProperty(None, allownone=True)
|
||||
'''The view_instance property holds the present contained view.
|
||||
'''
|
||||
|
||||
target_property_name = StringProperty('')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if not 'observed' in kwargs:
|
||||
raise Exception("ObserverView: observed object required")
|
||||
if not 'target_property_name' in kwargs:
|
||||
raise Exception("ObserverView: target_property_name required")
|
||||
if not 'adapter' in kwargs:
|
||||
if isinstance(kwargs['observed'], CollectionAdapter):
|
||||
kwargs['adapter'] = SelectionAdapter(**kwargs)
|
||||
|
||||
print kwargs
|
||||
super(ObserverView, self).__init__(**kwargs)
|
||||
|
||||
if isinstance(self.adapter.observed, CollectionAdapter):
|
||||
self.adapter.bind(obj=self.update)
|
||||
|
||||
self.update(self.adapter)
|
||||
|
||||
def set_view(self, view):
|
||||
self.view_instance = view
|
||||
|
||||
def get_view(self):
|
||||
return self.view_instance
|
||||
|
||||
def update(self, adapter, *args):
|
||||
print 'ObserverView, update', adapter
|
||||
self.clear_widgets()
|
||||
self.view_instance = adapter.get_view()
|
||||
v = self.get_view()
|
||||
if v is not None:
|
||||
if type(self.adapter) is SelectionAdapter:
|
||||
self.adapter.bind(obj=v.setter(self.target_property_name))
|
||||
self.add_widget(v)
|
Loading…
Reference in New Issue