Saurabh Shrivastava
Saurabh Shrivastava

Reputation: 1484

Getting Index of Kivy Spinner

Suppose i have a spinner with id: fruit and values "apple","banana","watermelon".

I have some code like:

if(self.ids['fruit'].text=='apple'):
   a=2

Now, in my app i have a language selection option. So if i select "Spanish", i will change apple to Spanish language and my above code will fail(id will remain in english only since i wont change it). However Index of selected value will remain same.

So, is there index support for kivy spinner, i mean something like:

if(self.ids['fruit'].id==1):
   a=2

Upvotes: 1

Views: 1153

Answers (2)

zeeMonkeez
zeeMonkeez

Reputation: 5177

For the purpose of keeping values (and current selection) separate from the text displayed, I wrote a subclass of Spinner. It separates values for display (values) from the logical representation of the values (hidden_values). Furthermore, it has an extra attribute text_transformer, which can transform the selected_text (current item of values) and selected_value (current item of hidden_values) into a display text. The code contains an example, too. For my original version (with original examples), have a look at this gist.

# -*- coding: utf-8 -*-

import itertools
from kivy.factory import Factory

from kivy.compat import string_types
from kivy.uix.spinner import Spinner, SpinnerOption
from kivy.properties import ListProperty, ObjectProperty, StringProperty
from kivy.logger import Logger

class HVSpinnerOption(SpinnerOption):
    """Represents an option in HVSpinner. It adds :attr:`value` to represent a hidden value not shown to user."""
    value = ObjectProperty(None)

class HVSpinner(Spinner):
    """Implements a spinner that keeps hidden values associated with
    options and displays a human readable text.
    """

    hidden_values = ListProperty()
    """Values associated with every item generated from :attr:`values`.
    :attr:`hidden_values`: is a :class:`~kivy.properties.ListProperty` and defaults to `range(len(self.values))`.
    The list may contain any kind of object.
    """

    selected_text = StringProperty()
    """Text representation of the selected item. By default, this is `None`. Upon selection of an item,
    it will be the corresponding element of :attr:`values`.
    """

    selected_value = ObjectProperty(None)
    """Representation of the selected item. Defaults to `None`. Upon selection of an item,
    it will be the corresponding element of :attr:`hidden_values`.
    """

    option_cls = ObjectProperty(HVSpinnerOption)
    '''Extension of class to display options. It must conform to requirements laid out for :attr:`~kivy.uix.spinner.option_cls`. In addition, the class must have a `value` property (should be :class:`~kivy.properties.ObjectProperty`).

    Additional :parameter:
    `text_transformer`: `None` or `callable`. Function that is used to set the :attr:`text` of :class:`HVSpinner` once
    an item has been selected. It should return a string and it should take 3 arguments:

    - :attr:`caller`: instance of :class:`HVSpinner` making the call
    - :attr:`text`: same as :attr:`selected_text`
    - :attr:`value`: same as :attr:`selected_value`

    Example::

        from kivy.base import runTouchApp
        from kivy.uix.gridlayout import GridLayout
        from kivy.uix.boxlayout import BoxLayout
        def print_current_val(obj, *largs):
            print "Spinner {} has text [{}], selected_text [{}] and hidden value [{}]".format(obj, obj.text, obj.selected_text, obj.selected_value)

        def transformator(caller, text, value):
            return "Now at >{}< ({})".format(text, value)

        vals = ["item {}".format(i) for i in range(8)]
        hvals  = ["<{}>".format(i + 17) for i in range(len(vals)) ]
        gl = GridLayout(rows=1)

        hvs1 = HVSpinner(values=vals, hidden_values=hvals, text='Select Me',
             size_hint=(.3, None), height="30dp")
        hvs2 = HVSpinner(values=vals, text='No, Me', size_hint=(.3, None), height="40dp")
        hvs3 = HVSpinner(values=vals, hidden_values=range(len(vals)), text='Please, me!', size_hint=(.3, None), height="30dp",
            text_transformer=transformator)
        hvs1.bind(selected_value=print_current_val)
        hvs2.bind(selected_value=print_current_val)
        hvs3.bind(selected_value=print_current_val)
        gl.add_widget(hvs1)
        gl.add_widget(hvs2)
        gl.add_widget(hvs3)
        runTouchApp(gl)

    '''

    def __init__(self, *args, **kwargs):
        self.text_transformer = kwargs.pop('text_transformer', None)
        super(HVSpinner, self).__init__(*args, **kwargs)

    def _update_dropdown(self, *largs):
        dp = self._dropdown
        cls = self.option_cls
        if isinstance(cls, string_types):
            cls = Factory.get(cls)
        dp.clear_widgets()
        if len(self.hidden_values) == 0:
            self.hidden_values = range(len(self.values))
        if len(self.values) != len(self.hidden_values):
            Logger.warning("HVSpinner: 'values' and 'hidden_values' have different lengths.")
        for tv, hv in itertools.izip(self.values, self.hidden_values):
            item = cls(text=tv, value=hv)
            item.bind(on_release=lambda option: dp.select((option.text, option.value)))
            dp.add_widget(item)

    def _on_dropdown_select(self, instance, data, *largs):
        if callable(self.text_transformer):
            self.text = self.text_transformer(self, data[0], data[1])
        else:
            self.text = data[0]
        self.selected_text = data[0]
        self.selected_value = data[1]
        self.is_open = False

Factory.register('HVSpinner', cls=HVSpinner)

def main():
    from kivy.app import App
    from kivy.lang import Builder
    from kivy.clock import Clock

    kvstr = '''
GridLayout:
    cols: 2
    HVSpinner:
        id: my_spinner
        text: "Pick one"
        values: ["apple", "banana", "watermelon"]
    Button:
        text: "Do something with it!"
        on_press: app.do_something()
    Label:
        text: "Current language: {}".format(app.current_language)
    Button:
        text: "Change Language"
        on_press: app.change_language()

    '''


    item_list = ["apple", "banana", "watermelon"]
    item_list_spanish = ["manzana", "banana", "sandía"]


    class AnApp(App):
        current_language = StringProperty('English')
        def build(self):
            Clock.schedule_once(self.set_up_spinner, 0)
            return Builder.load_string(kvstr)
        def change_language(self):
            if self.current_language == 'English':
                self.current_language = 'Spanish'
                self.root.ids.my_spinner.values = item_list_spanish
            else:
                self.current_language = 'English'
                self.root.ids.my_spinner.values = item_list

        def set_up_spinner(self, dt=0):
            def transformator(caller, text, value):
                if self.current_language=='English':
                    return "You picked: {}".format(text)
                else:
                    return "Escogió: {}".format(text)
            self.root.ids.my_spinner.text_transformer = transformator
        def do_something(self):
            if self.root.ids.my_spinner.selected_value == 0:
                print "An apple was picked!"

    AnApp().run()


if __name__ == '__main__':
    main()

Upvotes: 1

Peter Badida
Peter Badida

Reputation: 12189

Spinner values are in a ListProperty, therefore you can use enumerate on them like this:

for index, text in enumerate(self.ids.fruit.values):
    if index == 1:
        a=2

Even if you couldn't use enumerate on ListProperty, you can still make your own list from values at the beginning of your function. Of course enumerate starts from zero, so index + 1 would be necessary if you want to use 1 in your code.

Upvotes: 0

Related Questions