Repository: kivy/kivy

Issue:
no way to use Dynamic Classes with ListAdapter
Given the very simple `main.py`:

``` python
from kivy.app import App


class DynamicClassTest(App):
    pass

DynamicClassTest().run()
```

It is possible to construct `ListAdapter`s using normal classes or templates. Both these KV Language files work just fine:

``` yaml
#:import la kivy.adapters.listadapter
#:import lb kivy.uix.label

ListView:
    adapter:
        la.ListAdapter(
        data=[str(i) for i in range(10)],
        cls=lb.Label)(venv)(master)
```

and

``` yaml
#:import la kivy.adapters.listadapter

[Dynlabel@Label]:
    text: ctx.text

ListView:
    adapter:
        la.ListAdapter(
        data=[str(i) for i in range(10)],
        template="Dynlabel")
```

The latter syntax is deprecated in favor of dynamic classes, but I cannot find any way to make dynamic classes work with kivy syntax:

``` yaml
#:import la kivy.adapters.listadapter

<Dynlabel@Label>:
    text: ctx.text

ListView:
    adapter:
        la.ListAdapter(
        data=[str(i) for i in range(10)],
        cls=Dynlabel)
```

fails with a `name 'Dynlabel' is not defined` error, while

``` yaml
#:import la kivy.adapters.listadapter

<Dynlabel@Label>:
    text: ctx.text

ListView:
    adapter:
        la.ListAdapter(
        data=[str(i) for i in range(10)],
        cls="Dynlabel")
```

yields a `TypeError: 'str' object is not callable`.

While it would be handy if one of these syntaxes could be supported, it may also be wise to consider changing the API so that it's possible to instantiate a ListAdapter using "kivy magic". The KV Language could then look something like:

``` yaml
ListView:
    adapter:
        ListAdapter:
            data: []
            cls: Dynlabel
```

In any event, the documentation for ListAdapter is out of date because it references KV Lange templates, which are deprecated, and does not support dynamic classes in any way.



Discussion:
Hehehehe, i got exactly the same issue two days ago when i designed a notes application for a kivy tutorial. Even using Factory.<widgetclass> doesn't work.

I think the ListAdapter should take care of the cls as well, if it's a string: automatically resolve it with Factory.


Resolving patch:
diff --git a/kivy/adapters/adapter.py b/kivy/adapters/adapter.py
--- a/kivy/adapters/adapter.py
+++ b/kivy/adapters/adapter.py
@@ -41,7 +41,8 @@
 from kivy.properties import ObjectProperty
 from kivy.lang import Builder
 from kivy.adapters.args_converters import list_item_args_converter
-
+from kivy.factory import Factory
+from kivy.compat import string_types
 
 class Adapter(EventDispatcher):
     '''An :class:`~kivy.adapters.adapter.Adapter` is a bridge between data and
@@ -128,11 +129,29 @@ def bind_triggers_to_view(self, func):
     def get_data_item(self):
         return self.data
 
+    def get_cls(self):
+        '''
+        .. versionadded:: 1.9.0
+
+        Returns the widget type specified by self.cls. If it is already
+        anything other than a string, it is returned directly. If it is a
+        string, the Factory is queried to retrieve the widget class with
+        the given name.
+        '''
+        cls = self.cls
+        if isinstance(cls, string_types):
+            try:
+                cls = getattr(Factory, cls)
+            except AttributeError:
+                raise AttributeError(
+                    'Listadapter cls widget does not exist.')
+        return cls
+
     def get_view(self, index):  # pragma: no cover
         item_args = self.args_converter(self.data)
 
-        if self.cls:
-            instance = self.cls(**item_args)
-            return instance
+        cls = self.get_cls()
+        if cls:
+            return cls(**item_args)
         else:
             return Builder.template(self.template, **item_args)
diff --git a/kivy/adapters/listadapter.py b/kivy/adapters/listadapter.py
--- a/kivy/adapters/listadapter.py
+++ b/kivy/adapters/listadapter.py
@@ -221,8 +221,9 @@ def create_view(self, index):
 
         item_args['index'] = index
 
-        if self.cls:
-            view_instance = self.cls(**item_args)
+        cls = self.get_cls()
+        if cls:
+            view_instance = cls(**item_args)
         else:
             view_instance = Builder.template(self.template, **item_args)
 
diff --git a/kivy/adapters/simplelistadapter.py b/kivy/adapters/simplelistadapter.py
--- a/kivy/adapters/simplelistadapter.py
+++ b/kivy/adapters/simplelistadapter.py
@@ -66,8 +66,9 @@ def get_view(self, index):
 
         item_args = self.args_converter(index, item)
 
-        if self.cls:
-            instance = self.cls(**item_args)
+        cls = self.get_cls()
+        if cls:
+            instance = cls(**item_args)
             return instance
         else:
             return Builder.template(self.template, **item_args)


Test patch: