pkm
pkm

Reputation: 2783

Search Box with suggestions in kivy?

I am new to kivy wanted to know how we can bind textinput box to suggestion so that user and touch and select the suggestions. I have long list of buttons out of which i want to select on the basis of name.

Upvotes: 6

Views: 5932

Answers (3)

bagdenaj
bagdenaj

Reputation: 45

Incase anyone is wondering how can get the value of the selected item from the list, I have a little updated version here:

from kivymd.icon_definitions import md_icons
from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.list import OneLineListItem
from kivy.uix.screenmanager import Screen

KV = '''
<ListSelect>
    BoxLayout:
        orientation: 'vertical'
        spacing: dp(10)
        padding: dp(20)
        pos_hint:{'center_x': 0.5, 'y': 0.85}

        BoxLayout:
            size_hint_y: None
            height: self.minimum_height

            MDIconButton:
                icon: 'magnify'

            MDTextField:
                id: search_field
                hint_text: 'Search icon'
                on_text: 
                    root.set_list(self.text)

    RecycleView:
        pos_hint:{'center_x': 0.5, 'center_y': 0.4}

        MDList:
            id: container
'''


class ListSelect(Screen):

    def pressed(self, value):
        # value here is the OneLineListItem
        self.ids.container.clear_widgets()
        # set TextField text to selected list item
        self.ids.search_field.text = value.text
        print(value.text)


    def set_list(self, text=" "): 
        # text defaults to blank space to not show any icons initally
        # each OneLineListItem takes the pressed func on press
        self.ids.container.clear_widgets() # refresh list
        for icon in md_icons.keys():
            # using casefold() to make the input case insensitve
            if icon.startswith(text.casefold()):
                self.ids.container.add_widget(
                    OneLineListItem(text=icon, on_press=self.pressed)
                )



class Test(MDApp):
    def build(self):
        Builder.load_string(KV)
        self.screen = ListSelect()
        return self.screen

    def on_start(self):
        self.screen.set_list()

Test().run()

One catch: the search needs quite a long time to finish. If anyone has any hints on how to speed it up, you are very welcome!

Upvotes: 0

Aqeglipa beast
Aqeglipa beast

Reputation: 95

i am using kivymd i dont real know how to do it in kivy but here are the codes for kivymd

from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.uix.screenmanager import Screen

from kivymd.icon_definitions import md_icons
from kivymd.app import MDApp
from kivymd.uix.list import OneLineIconListItem


Builder.load_string(
'''
#:import images_path kivymd.images_path


<CustomOneLineIconListItem>:

  IconLeftWidget:
     icon: root.icon


<PreviousMDIcons>:

  BoxLayout:
    orientation: 'vertical'
    spacing: dp(10)
    padding: dp(20)

    BoxLayout:
        size_hint_y: None
        height: self.minimum_height

        MDIconButton:
            icon: 'magnify'

        MDTextField:
            id: search_field
            hint_text: 'Search icon'
            on_text: root.set_list_md_icons(self.text, True)

    RecycleView:
        id: rv
        key_viewclass: 'viewclass'
        key_size: 'height'

        RecycleBoxLayout:
            padding: dp(10)
            default_size: None, dp(48)
            default_size_hint: 1, None
            size_hint_y: None
            height: self.minimum_height
            orientation: 'vertical'
 '''
 )


class CustomOneLineIconListItem(OneLineIconListItem):
     icon = StringProperty()


class PreviousMDIcons(Screen):

    def set_list_md_icons(self, text="", search=False):
      '''Builds a list of icons for the screen MDIcons.'''

     def add_icon_item(name_icon):
        self.ids.rv.data.append(
            {
                "viewclass": "CustomOneLineIconListItem",
                "icon": name_icon,
                "text": name_icon,
                "callback": lambda x: x,
            }
        )

    self.ids.rv.data = []
    for name_icon in md_icons.keys():
        if search:
            if text in name_icon:
                add_icon_item(name_icon)
        else:
            add_icon_item(name_icon)


class MainApp(MDApp):
  def __init__(self, **kwargs):
     super().__init__(**kwargs)
     self.screen = PreviousMDIcons()

  def build(self):
      return self.screen

  def on_start(self):
      self.screen.set_list_md_icons()


MainApp().run()

some results here is the screen shot result when your not searching for anything

here is a screen shot when i am trying to search

second

here is a result when the word am searching does not exist

Upvotes: 2

Hussam F. Alkdary
Hussam F. Alkdary

Reputation: 727

Here is a simple example that will use the use text input to search in the option_list and will display the suggestion under the text input widget. I used the kivymd widget to get a nice look design you replaced with the normal kivy widgets if you want

from kivy.properties import ListProperty
from kivymd.app import MDApp
from kivymd.uix.list import OneLineAvatarIconListItem
from kivymd.uix.textfield import MDTextField

kv = """

Screen:
    BoxLayout:
        orientation: 'vertical'
        spacing: 1
        BoxLayout:
            size_hint_y: 1/5
            canvas.before:
                Color:
                    rgba:  0, 0, 0, 1
                Rectangle:
                    pos: self.pos
                    size: self.size[0], 2
            MDIconButton:
                icon: 'magnify'
                size_hint_y: 1
            SearchTextInput:
                id: Search_TextInput_id
                size_hint_y: .97
                pos_hint:{ 'left':0 , 'top': 1}
                hint_text: 'search'
                hint_text_color: 1,1,1,1
                icon_left: 'magnify'
                mode: "fill"
                helper_text_mode: "persistent"
                helper_text: "Search"
                line_color: [1,1,1,1]
                color_normal: [1,1,1,1]

                font_size: .35 * self.height
                active_line: False
                multiline: False
 
            MDIconButton:
                icon: 'close'
                size_hint_y:1
                text_color: 0,0,0,1

        BoxLayout:
            orientation: 'vertical'
            padding: 4
            RecycleView:
                viewclass: 'Search_Select_Option'
                data:app.rv_data
                RecycleBoxLayout:
                    spacing: 15
                    padding : 10
                    default_size: None, None
                    default_size_hint: 1, None
                    size_hint_y: None
                    height: self.minimum_height
                    orientation: 'vertical'
<Search_Select_Option>:
    on_release: print(self.text)
    IconRightWidget:
        icon: "arrow-top-left"
"""


class Search_Select_Option(OneLineAvatarIconListItem):
    pass


class SearchTextInput(MDTextField):
    option_list = 'one1,two1,two2,three1,three2,three3,four1,four2,four3,four4,five1,five2,five3,five4,five5'.split(',')

    def on_text(self, instance, value):
        app = MDApp.get_running_app()
        option_list = list(set(self.option_list + value[:value.rfind(' ')].split(' ')))
        val = value[value.rfind(' ') + 1:]
        if not val:
            return
        try:
            app.option_data = []
            for i in range(len(option_list)):
                word = [word for word in option_list if word.startswith(val)][0][len(val):]
                if not word:
                    return
                if self.text + word in option_list:
                    if self.text + word not in app.option_data:
                        popped_suggest = option_list.pop(option_list.index(str(self.text + word)))
                        app.option_data.append(popped_suggest)
                app.update_data(app.option_data)

        except IndexError:

            pass


class RVTestApp(MDApp):
    rv_data = ListProperty()

    def update_data(self, rv_data_list):
        self.rv_data = [{'text': item} for item in rv_data_list]
        print(self.rv_data, 'update')

    def build(self):
        return Builder.load_string(kv)


RVTestApp().run() 

Upvotes: 1

Related Questions