Reputation: 533
just a rundown of what I'm trying to do. I've got two TextInput widgets in a FloaLayout widget, all defined in a .kv file as shown below. (I asked this question on kivy forums but the solution offered didn't work out so I'm asking it here to get some new ideas)
test.kv
<TESTGUI>:
t2: TI2
t4: TI4
fl1: FL1
FloatLayout:
orientation: 'lr-bt'
id: FL1
size: root.size
TextInput:
id: TI2
size_hint: 1, 0.1
pos_hint: {'top': 1}
font_size: 35
on_text: root.realTimeSearch(TI2, TI2.text)
TextInput:
id: TI4
size_hint: 1, 0.1
pos_hint: {'top': 0.86}
font_size: 15
Now when I enter any text in one of the TextInput widgets (t2), what the program does is it searches for that text in a string. This search is performed whenever the TextInput widgets text changes. So basically as soon as you start typing the search begins dynamically. The search result can have many matches (all these matches are stored in a list called result_list (see below code)) and based on the number of matches, I add a GridLayout with the number of buttons that equal to the number of results (i.e. number of elements in the result_list) from the search. Now when I click the button, what happens is that it transfers the button text to the other TextInput widget (t4 as shown above). Below is the entire code from the .py file The program basically is a search utility with auto-complete functionality. The problem that I'm encountering is that clear_widgets doesn't seem to work in the current context. So I get many widgets on top of eachother and I need to click through each of them to get rid of them (refer to the code below for a clear explanation)
I'd suggest you run the code on your machine to get a good idea of what is happening (try entering 'silicon' in t2 textinput widget and you can see how clear_widgets is not working).
import re
import sys
import kivy
kivy.require('1.5.1')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.popup import Popup
from kivy.uix.scrollview import ScrollView
from collections import Counter
from functools import partial
reload(sys)
sys.setdefaultencoding('utf-8')
rtsstr =",,,Substrate1,,,Substrate1,,,Substrate1,,,Substrate1,,,Substrate1,,,Substrate_coating,,,silicon,,,silicon_Substrate,,,substrate_silicon,,,"
#rtsstr is the string on which search is being performed
class TESTGUI(Widget):
t2 = ObjectProperty(None)
t4 = ObjectProperty(None)
fl1 = ObjectProperty(None)
def realTimeSearch(self, instance, value):
"""
this function is used as a real time update of search results based on the search query. It also identifies partial matches (e.g. a search for silicon can result in entries such as silicon, silicon nitride, silicon dioxide etc. being displayed
"""
if value != '':
match = re.findall("(?<=,{3})(?:(?!,{3}).)*?%s.*?(?=,{3})" % value, rtsstr, re.IGNORECASE)
result_list = list(set(match)) #using a set to remove duplicates, if any.
self.create_search(result_list)
def create_search(self, result_list):
layt = GridLayout(cols=3, size_hint_y = None)
layt.bind(minimum_height=layt.setter('height'))
scrlv = ScrollView(size_hint=(1, 0.8), pos_hint={'top': 0.8})
self.fl1.remove_widget(scrlv)
for result in result_list:
buttn2 = Button(text = str(result), size_hint = (0.3, None), height = 40)
buttn2.bind(on_press = partial(self.transferSearchText, buttn2.text, scrlv))
layt.add_widget(buttn2)
scrlv.add_widget(layt)
self.fl1.add_widget(scrlv)
def transferSearchText(self, text, scrlv, *args):
self.t4.insert_text(text + ',')
scrlv.clear_widgets()
self.fl1.remove_widget(scrlv)
self.t2.text = ''
class TestApp(App):
def build(self):
return TESTGUI()
if __name__ == '__main__':
TestApp().run()
Thanks!
Upvotes: 2
Views: 2481
Reputation: 4162
You are trying to clear the Scrollview, where you should be clearing the layout you add to the ScrollView.
Widget.Clear_widget() only clears the current widget's children, it's not recursive and is not meant to be so.
Kivy doesn't provide a traditional editable combo-box. How ever Kivy makes it extremely easy for you to create your own widget combining a TextInput and a DropDown.
You should be using something like a ComboEdit as shown in the snippets wiki, and modifying it to suit your needs.
So, what you are trying to achieve can be done like so::
import re
from kivy.clock import Clock
from kivy.factory import Factory
from kivy.properties import ListProperty, StringProperty
from kivy.lang import Builder
Builder.load_string(''' <DDNButton@Button>
size_hint_y: None
height: dp(45) ''')
class ComboEdit(Factory.TextInput):
options = ListProperty(('', ))
_options = ListProperty(('', ))
options_cls = StringProperty(Factory.DDNButton)
def __init__(self, **kw):
ddn = self.drop_down = Factory.DropDown()
ddn.bind(on_select=self.on_select)
super(ComboEdit, self).__init__(**kw)
def on_options(self, instance, value):
self._options = value
def on__options(self, instance, value):
ddn = self.drop_down
ddn.clear_widgets()
for option in value:
widg = self.options_cls(text=option)
widg.bind(on_release=lambda btn: ddn.select(btn.text))
ddn.add_widget(widg)
def on_select(self, *args):
self.text = args[1]
def on_text(self, instance, value):
if value == '':
instance._options = self.options
else:
r = re.compile(f".*{value}")
match = filter(r.match, instance.options)
#using a set to remove duplicates, if any.
instance._options = list(set(match))
print(instance._options)
try:
instance.drop_down.open(instance)
except Factory.WidgetException:
# instance.drop_down.parent.remove_widget(instance.drop_down)
instance.drop_down.parent = None
Clock.schedule_once(lambda dt: instance.drop_down.open(instance))
def on_focus(self, instance, value):
if value:
self.drop_down.open(self)
self.text = ''
if __name__ == '__main__':
from kivy.base import runTouchApp
runTouchApp(Builder.load_string(''' FloatLayout:
ComboEdit:
size_hint: .5, None
pos: 0, root.top - self.height
options: ['Hello', 'World']
'''))
Upvotes: 3