Kivy: Can't update text input value from another class - python

I'm new on kivy.I'm trying to change text input value (the string value that is shown in TextInput box) from another class, But nothing passes.
I have following widgets in class MyFirstScreen:
One RecycleView with multiple items (each item is a dictionary)
Two TextInputs
What I want to do: When each items in RecycleView selected, TextInputs should be update and load with corresponding values of selected item's dictionary.
But when i try to access current TextInput's value from another class (class SelectableLabel):
class SelectableLabel(RecycleDataViewBehavior, Label):
#...
def update_text_inputs(self, *kwarg):
my_text_input = MyFirstScreen().ids.system_name_text_input_id #<--i can access to widget
print("my_print_val is:", my_text_input.text) #<--but widget's current text value is : ''
my_text_input.text = "Updated Value"
I can access to my_text_input widget but its text (my_text_input.text) is an empty string ('').Furthermore when i change the value of my_text_input.text, nothing happens in TextInput's box.
Does anyone know what am I doing wrong here or how to make this work? Thank you in advance...
Here is my .py code:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
class Manager(ScreenManager):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class MyFirstScreen(Screen):
system_name_text_input_id = ObjectProperty(None)
def __init__(self, **kwarg):
super().__init__(**kwarg)
print("__init__ of MyFirstScreen is Called")
def get_text_inputs(self):
ret_val = self.ids.system_name_text_input_id.text
print("get_text_input is called, ret_val:", ret_val)
return ret_val
class RecycleViewWidget(RecycleView):
def __init__(self, **kwargs):
super(RecycleViewWidget, self).__init__(**kwargs)
self.items_of_rv = []
for i in range(1, 21):
self.items_of_rv.append(
{"color": (0, 0, 0, 1), "font_size": "20", "text": f"User {i}",
"user_id": f"{100 * i}"})
self.data = [item for item in self.items_of_rv]
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout):
""" Adds selection and focus behaviour to the view. """
class SelectableLabel(RecycleDataViewBehavior, Label):
""" Add selection support to the Label """
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
""" Catch and handle the view changes """
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
""" Add selection on touch down """
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
""" Respond to the selection of items in the view. """
self.selected = not is_selected
if is_selected:
rv.data[index].update({'color': (1, 1, 1, 1)})
self.refresh_view_attrs(RecycleViewWidget(), index, rv.data[index])
print("selection changed to {0}".format(rv.data[index]))
self.update_text_inputs()
else:
if rv.data[index].get("color") == (1, 1, 1, 1):
rv.data[index].update({'color': (0, 0, 0, 1)})
self.refresh_view_attrs(RecycleViewWidget(), index, rv.data[index])
self.selected = not self.selected
def update_text_inputs(self, *kwarg):
my_text_input = MyFirstScreen().ids.system_name_text_input_id#<--i can access to widget
print("my_print_val is:", my_text_input.text)#<--but widget's current text value is : ''
# my_text_input.text = "Updated Value"
main_style = Builder.load_file("test.kv")
class MyApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
return main_style
if __name__ == '__main__':
MyApp().run()
And my .kv code:
Manager:
MyFirstScreen:
<SelectableLabel>:
canvas.before:
Color:
rgba: (0, 0, 1, 1) if self.selected else (1, 1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
<RecycleViewWidget>:
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<MyFirstScreen>:
name: 'system_setup_page'
system_name_text_input_id:system_name_text_input_id
GridLayout:
cols: 2
BoxLayout:
cols: 1
padding: 20
RecycleViewWidget:
FloatLayout:
Label:
size_hint: None, None
text: "Name:"
font_size: 22
pos_hint: {'center': (20/100, 90/100)}
TextInput:
id: system_name_text_input_id
size_hint: None, None
hint_text: "Type Your Name..."
size: 200, 30
multiline: False
pos_hint: {'center': (65/100, 90/100)}
on_text: root.get_text_inputs()
Label:
size_hint: None, None
text: "User ID:"
font_size: 20
pos_hint: {'center': (20/100, 70/100)}
TextInput:
size_hint: None, None
size: 200, 30
hint_text: "Type Your ID..."
pos_hint: {'center': (65/100, 70/100)}

Problem is because using MyFirstScreen() in update_text_inputs() you create new instance of class MyFirstScreen but you have to use existing instance.
It should be better way to get it but at this moment my only idea is to use parent for this .
Because there are many widgets between these elements so it needs many parent:
my_text_input = self.parent.parent.parent.parent.parent.system_name_text_input_id
I found it using print(self.parent), next print(self.parent.parent), etc.
def update_text_inputs(self, *kwarg):
#print('parent:', self.parent.parent.parent.parent.parent.system_name_text_input_id)
#print('parent:', self.parent.parent.parent.parent.parent.system_name_text_input_id.text)
my_text_input = self.parent.parent.parent.parent.parent.system_name_text_input_id
print("my_print_val is:", my_text_input.text)
my_text_input.text = "Updated Value"

Related

How to force item in RecycleView to show as selected in Kivy?

I have a recycleview of several thousand songs. I want to force a selection when I shuffle songs. I can force scrolling to the selected song by setting scroll_y but I cannot seem to get the item in the recycleview to display as selected as with the basic kivy recycleview example where you click on an item and it changes background color.
I have a basic example. If you click it should start the random item selection every 0.5 sec and print out the rv.data showing that the items have 'selected': True.
I've tried setting key_selection: 'selected' and I've also set 'selected': True in the recycleview.data.
MPROSRVbug.kv
<Manachan>:
id: manachanID
SelectionScreen:
id: selectscreenID
name: 'selectscreen'
manager: 'manachanID'
<SelectionScreen>:
id: selectionscreenid
BoxLayout:
orientation: 'horizontal'
Video:
id: previewVideo
SongList:
id: songlistid
<SongList>
cols: 1
size_hint_x: 1
BoxLayout:
size_hint: (1,.1)
orientation: 'vertical'
Label:
id: songtitle
canvas.before:
Color:
rgba: (.3, .3, .3, 1)
Rectangle:
pos: self.pos
size: self.size
RV:
id: recycle_grid1
size_hint_x: 1
<RV>:
viewclass: 'SongLinkButton'
SelectableRecycleBoxLayout:
id: recycle_grid2
default_size: None, dp(120)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<SongLinkButton>:
id: songbutton
orientation: 'horizontal'
height: '80dp'
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
Label:
id: textinfo
text_size: self.width, None
MPROSRVbug.py
#MUSIC PLAYER RANDOM ORDERED SORT
import os, sys
from random import *
songDict = {}
songVal = {}
i = 0
for mp3file in ["1","9","7k","r","j","i","7","g","a","2",]:
songDict[i] = mp3file
songVal[str(mp3file)] = 0
i += 1
if i > 7:
break
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.recycleview import RecycleView
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview.views import RecycleKVIDsDataViewBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.properties import *
from kivy.core.audio import SoundLoader
from kivy.uix.scrollview import ScrollView
from kivy.clock import Clock
import traceback
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
#set self.data:
self.data = [{'textinfo.text': x, 'selected': False} for x in [songDict[y] for y in songDict]]
print("self.data item?", self.data[0], len(self.data))
def scroll_to_index(self, index):
box = self.children[0]
pos_index = (box.default_size[1] + box.spacing) * index
scroll = self.convert_distance_to_scroll(
0, pos_index - (self.height * 0.5))[1]
if scroll > 1.0:
scroll = 1.0
elif scroll < 0.0:
scroll = 0.0
self.scroll_y = 1.0 - scroll
def NextSong(self, *args):
rvRef = App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["recycle_grid1"]
randPickInt = randint(0,len(rvRef.data)-1)
for d in rvRef.data:
if d['textinfo.text'] == songDict[randPickInt]:
d['selected'] = True
dictElement = d
target = os.path.normpath(rvRef.data[randPickInt]['textinfo.text'])
App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["songtitle"].text = target
targettextOG = rvRef.data[randPickInt]['textinfo.text']
#PLAN: apply_selection always triggers when reycycleview refreshes, so set a global var of the selected guy. within the widget, if the name is the same as the selected guy, make yourself selected
global curSelectedSong
curSelectedSong = rvRef.data[randPickInt]['textinfo.text']
counter = 0
for d in rvRef.data:
#remove all previously selected
if d['selected']:
d['selected'] = False
#if we found the song, change to selected
if d['textinfo.text'] == targettextOG:
#print("VERSING", d['textinfo.text'], targettextOG, d['textinfo.text'] == targettextOG)
d['selected'] = True
#print("so has d updated?", d)
rvRef.data[counter] = d
print("updated rvRef.data", rvRef.data[counter])
counter += 1
print("WHAT IS RV DATA?", rvRef.data)
App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["recycle_grid1"].scroll_to_index(randPickInt)
rvRef.refresh_from_data()
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
def __init__(self, **kwargs):
super(SelectableRecycleBoxLayout, self).__init__(**kwargs)
def WeightedChoice():
pass
class SongLinkButton(RecycleKVIDsDataViewBehavior,BoxLayout,Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def __init__(self, **kwargs):
super(SongLinkButton, self).__init__(**kwargs)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SongLinkButton, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SongLinkButton, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
global curSelectedSong
try:
if curSelectedSong == rv.data[index]["textinfo.text"]:
self.selected == True
except:
pass
global soundObj
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if is_selected and App.get_running_app().root.current == 'selectscreen':
target = os.path.normpath(rv.data[index]['textinfo.text'])
Clock.schedule_interval(App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["recycle_grid1"].NextSong, 1)
App.get_running_app().root.get_screen('selectscreen').ids["songlistid"].ids["songtitle"].text = target
else:
pass
class Manachan(ScreenManager):
pass
class SongList(GridLayout):
pass
class SelectionScreen(Screen):
pass
class MyApp(App):
def build(self):
self.load_kv('MPROSRVbug.kv')
self.title = "Music Player Random Ordered Sort by Pengindoramu"
return Manachan()
if __name__ == '__main__':
MyApp().run()

How can I search in recycleview

How can I search in recycleview with updated review .I can also search now. But when I type 3 alphabets in txt_input it search for the same result. and gives the list which contains those 3 alphabets.
But i want to make as a search engine. so if I type "TAT" it will show me TATA MOTORS PVT LTD,Tata Elxsi Limited,Tata Steel Limited,etc. But when I type "TATA MO" it doesnot update the recycleview with new search suggestions.
How can I achieve that kind of updated result every time add alphabets?
test.py file
from kivy.app import App
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import NumericProperty, ListProperty, BooleanProperty, ObjectProperty, StringProperty
from kivy.uix.recycleview import RecycleView
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
import pandas as pd
Builder.load_string('''
<Body>:
name: 'body_screen'
canvas:
Color:
rgba:(1, 1, 1, 1)
Rectangle:
# pos: self.pos
size: self.size
<DropDownWidget>:
name: 'DropDownWidget'
canvas:
Color:
rgba:(1, 1, 1, 1)
Rectangle:
# pos: self.pos
size: self.size
# orientation: 'vertical'
spacing: 20
txt_input: txt_input
rv: rv
# txt_input1: txt_input1
MyTextInput:
id: txt_input1
pos: 400,300
size_hint_y: None
height: 50
MyTextInput:
id: txt_input
hint_text:'Enter here'
size_hint_y: None
height: 50
RV:
id: rv
<MyTextInput>:
name: 'MyTextInput'
readonly: False
multiline: False
<SelectableLabel>:
name: 'SelectableLabel'
id: SelectableLabel
# Draw a background to indicate selection
color: 0,0,0,1
canvas.before:
Color:
rgba: (0, 0, 1, .5) if self.selected else (1, 1, 1, 1)
Rectangle:
# pos: self.pos
size: self.size
<RV>:
canvas:
Color:
rgba: 0,0,0,.2
Line:
rectangle: self.x +1 , self.y, self.width - 2, self.height -2
bar_width: 10
scroll_type:['bars']
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(20)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: False
''')
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableLabel(RecycleDataViewBehavior,Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
txt_input1 = ObjectProperty(None)
txt_input = ObjectProperty(None)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if is_selected:
App.get_running_app().root.widget_1.ids.txt_input1.text = str(rv.data[index].get("text"))
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
class DropDownWidget(BoxLayout):
txt_input = ObjectProperty()
rv = ObjectProperty()
txt_input1 = ObjectProperty()
class MyTextInput(TextInput):
txt_input = ObjectProperty()
txt_input1 = ObjectProperty(None)
flt_list = ObjectProperty()
word_list = ListProperty()
# this is the variable storing the number to which the look-up will start
starting_no = NumericProperty(3)
suggestion_text = ''
def __init__(self, **kwargs):
super(MyTextInput, self).__init__(**kwargs)
def on_text(self, instance, value):
# find all the occurrence of the word
self.parent.ids.rv.data = []
matches = [self.word_list[i] for i in range(len(self.word_list)) if
self.word_list[i][:self.starting_no] == value[:self.starting_no]]
# display the data in the recycleview
display_data = []
for i in matches:
display_data.append({'text': i})
self.parent.ids.rv.data = display_data
# ensure the size is okay
if len(matches) <= 10:
self.parent.height = (50 + (len(matches) * 20))
else:
self.parent.height = 240
def keyboard_on_key_down(self, window, keycode, text, modifiers):
if self.suggestion_text and keycode[1] == 'tab':
self.insert_text(self.suggestion_text + ' ')
return True
return super(MyTextInput, self).keyboard_on_key_down(window, keycode, text, modifiers)
class Body(FloatLayout):
def __init__(self, **kwargs):
f = pd.read_csv("stoploss.csv")
fl = len(f.index)
file = pd.DataFrame(f, columns=['Stock Symbol', 'Purchase Price', 'Stock Name', 'Stop Loss(%)'])
j = 0
wl = []
for i in range(fl):
for index in range(1):
columnSeriesObj = file.iloc[:, 2]
# pp = iter(columnSeriesObj.values)
# pp1 = next(pp)
# print(pp1)
wl.append(columnSeriesObj.values[i])
tp = tuple(wl)
print(str(tp))
# def convertTuple(tup):
# str = ''.join(tup)
# return str
# print(convertTuple(tp))
super(Body, self).__init__(**kwargs)
self.widget_1 = DropDownWidget(pos_hint={'center_x': .5, 'center_y': .5},
size_hint=(None, None), size=(600, 60))
self.widget_1.ids.txt_input.word_list = wl
self.widget_1.ids.txt_input.starting_no = 3
self.add_widget(self.widget_1)
class MyApp(App):
def build(self):
return Body()
if __name__ == "__main__":
MyApp().run()
here i am answering my own question.I found the solution from google kivy groups.
change change of code will do the thing.
change the code from:
matches = [self.word_list[i] for i in range(len(self.word_list)) if
self.word_list[i][:self.starting_no] == value[:self.starting_no]]
to:
matches = [word for word in self.word_list
if word[:len(value)].lower() == value[:len(value)].lower()]

Kivy: AttributeError: 'NoneType' object has no attribute 'parent' when scroll down and scroll up again in recycle view

My Kivy App Description:
I have 3 types of widgets in MyFirstScreen:
A RecycleView that has multiple "User"s as its items. (each item is a dictionary)
Three TextInputs that are related to values of each recycle view item. (if you select any items of RecycleView these TextInputs will loaded with corresponding dictionary values)
An "Add New User" Button. (if you enter NEW values in TextInputss and press this button, RecycleView will be updated with: previous items + your new item)
Issue:
An error occurs in these two situations:
Situation A:
I select an item (for example : "User 1").
I scroll DOWN until the selected item hides completely from RecycleView.
I scroll UP to show selected item again.
Situation B:
I select an item (for example : "User 1").
I change the new text values which is loaded in TextInputss.
I press the "Add New User" Button.
In both situations when I want to do step 3, this error occurs:
my_text_input= self.parent.parent.parent.parent.parent.system_name_text_input_id
AttributeError: 'NoneType' object has no attribute 'parent'
Does anyone know what am I doing wrong here or how to make this work? Thank you in advance...
My KivyTest.py file:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
class Manager(ScreenManager):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class MyFirstScreen(Screen):
def __init__(self, **kwarg):
super().__init__(**kwarg)
print("__init__ of MyFirstScreen is Called")
def update_recycle_view(self):
global is_repetitive
system_name_ti = self.ids.user_name_text_input_id.text
system_id_ti = self.ids.user_id_text_input_id.text
current_items_in_recycle_view = self.ids.recycle_view_widget_id.items_of_rv
new_item = {"color": (0, 0, 0, 1), "font_size": "20", "text": "", "user_id": ""}
for item_index in range(len(current_items_in_recycle_view)):
current_item_name = current_items_in_recycle_view[item_index].get("text")
current_item_id = current_items_in_recycle_view[item_index].get("user_id")
print(f"current_item_name: {current_item_name}_______current_item_id: {current_item_id}")
if system_name_ti == current_item_name:
print("Error: Repetitive User Name")
is_repetitive = True
break
elif system_id_ti == current_item_id:
print("Error: Repetitive User ID")
is_repetitive = True
break
else:
is_repetitive = False
if not is_repetitive:
print("else situation")
new_item.update({"text": system_name_ti})
new_item.update({"user_id": system_id_ti})
self.ids.recycle_view_widget_id.add_new_item_to_data(new_item)
class RecycleViewWidget(RecycleView):
def __init__(self, **kwargs):
super(RecycleViewWidget, self).__init__(**kwargs)
self.items_of_rv = []
self.update_my_items()
self.update_my_data()
def update_my_items(self):
for i in range(1, 21):
self.items_of_rv.append(
{"color": (0, 0, 0, 1), "font_size": "20", "text": f"Use {i}",
"user_id": f"{100 * i}"})
def update_my_data(self):
self.data = [item for item in self.items_of_rv]
def add_new_item_to_data(self, new_item):
self.data.append(new_item)
self.refresh_from_data()
print("add_new_item_to_data called")
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout):
""" Adds selection and focus behaviour to the view. """
class SelectableLabel(RecycleDataViewBehavior, Label):
""" Add selection support to the Label """
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
""" Catch and handle the view changes """
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
""" Add selection on touch down """
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
""" Respond to the selection of items in the view. """
self.selected = not is_selected
if is_selected:
rv.data[index].update({'color': (1, 1, 1, 1)})
self.refresh_view_attrs(RecycleViewWidget(), index, rv.data[index])
print("selection changed to {0}".format(rv.data[index]))
self.update_text_inputs(rv.data[index])
else:
print("selection removed from {0}".format(rv.data[index]))
if rv.data[index].get("color") == (1, 1, 1, 1):
rv.data[index].update({'color': (0, 0, 0, 1)})
self.refresh_view_attrs(RecycleViewWidget(), index, rv.data[index])
self.selected = not self.selected
def update_text_inputs(self, selected_system, *kwarg):
user_name_text_input = self.parent.parent.parent.parent.parent.user_name_text_input_id
user_id_text_input = self.parent.parent.parent.parent.parent.user_id_text_input_id
user_name_text_input.text = selected_system.get("text")
user_id_text_input.text = selected_system.get("user_id")
main_style = Builder.load_file("test.kv")
class MyApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
return main_style
if __name__ == '__main__':
MyApp().run()
My test.kv file:
Manager:
MyFirstScreen:
<SelectableLabel>:
canvas.before:
Color:
rgba: (0, 0, 1, 1) if self.selected else (1, 1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
<RecycleViewWidget>:
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<MyFirstScreen>:
name: 'system_setup_page'
user_name_text_input_id:user_name_text_input_id
user_id_text_input_id:user_id_text_input_id
recycle_view_widget_id:recycle_view_widget_id
GridLayout:
cols: 2
BoxLayout:
cols: 1
padding: 20
RecycleViewWidget:
id:recycle_view_widget_id
FloatLayout:
Label:
size_hint: None, None
text: "User Name:"
font_size: 22
pos_hint: {'center': (20/100, 90/100)}
TextInput:
id: user_name_text_input_id
size_hint: None, None
hint_text: "Type Your Name..."
size: 200, 30
multiline: False
pos_hint: {'center': (65/100, 90/100)}
Label:
size_hint: None, None
text: "User ID:"
font_size: 20
pos_hint: {'center': (20/100, 70/100)}
TextInput:
id: user_id_text_input_id
size_hint: None, None
size: 200, 30
hint_text: "Type Your ID..."
pos_hint: {'center': (65/100, 70/100)}
Button:
text: "Add New User"
size_hint: None, None
font_size: 20
size: 300, 50
pos_hint: {'center': (50/100, 30/100)}
on_release: root.update_recycle_view()
It seems it removes Label from RecycleViewWidget when it is not visible - and then Label has no parent.
When Label is visible agant then it puts Label in RecycleViewWidget and it has again parent. But first it executes apply_selection - so it runs it before Label has parent again.
Similar when it crates new Label then it first executes apply_selection before it adds Labal to RecycleViewWidget - so it runs it before Label has parent.
But in this situation you can use rv (which is instance of RecycleViewWidget) to get access to FirstScreen and to get access to TextInput
Now I sends rv and index instead of rv.data[index] to update_text_inputs so I can use it to get rv.parent.parent.parent and to get rv.data[index]
screen = rv.parent.parent.parent
user_name_text_input = screen.user_name_text_input_id
user_id_text_input = screen.user_id_text_input_id
EDIT: I found you can get it also without using parent and without rv
screen = main_style.screens[0]
# or
#screen = main_style.screens[0].ids
user_name_text_input = screen.user_name_text_input_id
user_id_text_input = screen.user_id_text_input_id
def apply_selection(self, rv, index, is_selected):
""" Respond to the selection of items in the view. """
self.selected = is_selected
if is_selected:
rv.data[index].update({'color': (1, 1, 1, 1)})
self.refresh_view_attrs(rv, index, rv.data[index])
print("selection changed to {0}".format(rv.data[index]))
self.update_text_inputs(rv, index)
else:
rv.data[index].update({'color': (0, 0, 0, 1)})
self.refresh_view_attrs(rv, index, rv.data[index])
print("selection removed from {0}".format(rv.data[index]))
def update_text_inputs(self, rv, index, *kwarg):
screen = rv.parent.parent.parent
#screen = main_style.screens[0]
#screen = main_style.screens[0].ids
print('[DEBUG] screen:', screen)
user_name_text_input = screen.user_name_text_input_id
user_id_text_input = screen.user_id_text_input_id
user_name_text_input.text = rv.data[index].get("text")
user_id_text_input.text = rv.data[index].get("user_id")
BTW: I use also rv instead of RecycleViewWidget() in refresh_view_attrs() because it can make the same problem as in previous question - RecycleViewWidget() can creates new instance of RecycleViewWidget and you should work with original first instance of RecycleViewWidget

Display List of Ordereddict in Kivy

I have a list of Ordereddict as follows
list1= [OrderedDict([('Numbers', '15'), ('FirstName', 'John'), ('SecondName', 'Raul'), ('MiddleName', 'Kyle'), ('Grade', 22)]),
OrderedDict([('Names', 'John'), ('NewFirstName', 'Mark'), ('NewSecondName', 'Sachel'), ('NewThirdName', 'Raul'), ('Grade', 15)]),
OrderedDict([('Numbers', '25'), ('FirstName', 'Kyle'), ('SecondName', 'Venn'), ('MiddleName', 'Marcus'), ('Grade', 24)]),
OrderedDict([('Names', 'Sachel'), ('NewFirstName', 'Venn'), ('NewSecondName', 'Kyle'), ('NewThirdName', 'John'), ('Grade', 71)])]
There are 8 unique keys and one common key in it, i would like to create a table from it in kivy with the same order, with keys being the header of the table. My expected output is as below, i am new to kivy ecosystem and i dont see anything like tableview in that, any other views could be used to get this output and how
Expected output in kivy
I took the simpler recycyle view example given in the comment and edited the no of columns to 9 and tried picking the values from Ordereddict and i got the below output, since i am new to kivy i am not sure to pull the values as in expected output
Below are .py and .kv files
check.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
from collections import OrderedDict
list1= [OrderedDict([('Numbers', '15'), ('FirstName', 'John'), ('SecondName', 'Raul'), ('MiddleName', 'Kyle'), ('Grade', 22)]),
OrderedDict([('Names', 'John'), ('NewFirstName', 'Mark'), ('NewSecondName', 'Sachel'), ('NewThirdName', 'Raul'), ('Grade', 15)]),
OrderedDict([('Numbers', '25'), ('FirstName', 'Kyle'), ('SecondName', 'Venn'), ('MiddleName', 'Marcus'), ('Grade', 24)]),
OrderedDict([('Names', 'Sachel'), ('NewFirstName', 'Venn'), ('NewSecondName', 'Kyle'), ('NewThirdName', 'John'), ('Grade', 71)])]
class TextInputPopup(Popup):
obj = ObjectProperty(None)
obj_text = StringProperty("")
def __init__(self, obj, **kwargs):
super(TextInputPopup, self).__init__(**kwargs)
self.obj = obj
self.obj_text = obj.text
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableButton(RecycleDataViewBehavior, Button):
''' Add selection support to the Button '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableButton, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableButton, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
def on_press(self):
popup = TextInputPopup(self)
popup.open()
def update_changes(self, txt):
self.text = txt
class RV(BoxLayout):
# data_items = ListProperty(newlist)
data_items = ListProperty([])
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.get_users()
def get_users(self):
# create data_items
for i in list1:
self.data_items.append(i.values())
class TestApp(App):
title = "Kivy RecycleView & SQLite3 Demo"
def build(self):
return RV()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
<TextInputPopup>:
title: "Popup"
size_hint: None, None
size: 400, 400
auto_dismiss: False
BoxLayout:
orientation: "vertical"
TextInput:
id: txtinput
text: root.obj_text
Button:
size_hint: 1, 0.2
text: "Save Changes"
on_release:
root.obj.update_changes(txtinput.text)
root.dismiss()
Button:
size_hint: 1, 0.2
text: "Cancel Changes"
on_release: root.dismiss()
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
<RV>:
BoxLayout:
orientation: "vertical"
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 9
Label:
text: "Numbers"
Label:
text: "FirstName"
Label:
text: "SecondName"
Label:
text: "MiddleName"
Label:
text: "Grade"
Label:
text: "Names"
Label:
text: "NewFirstName"
Label:
text: "NewSecondName"
Label:
text: "NewThirdName"
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: [{'text': str(x)} for x in root.data_items]
SelectableRecycleGridLayout:
cols: 9
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'horizontal'
multiselect: True
touch_multiselect: True
Question - TableView
There are 8 unique keys and one common key in it, i would like to
create a table from it in kivy with the same order, with keys being
the header of the table.
Solution - Using Kivy RecycleView
Modify the app to extract the keys and values from the collections.OrderedDict
Add a class attribute of type ListProperty e.g. column_headings = ListProperty([])
Implement nested for loop to extract keys or column headings
Implement nested for loop to extract values
Override None with empty string
Snippets
class RV(BoxLayout):
column_headings = ListProperty([])
data_items = ListProperty([])
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.get_users()
def get_users(self):
# extract keys or column headings
for row in list1:
for key in row.keys():
if key not in self.column_headings:
self.column_headings.append(key)
# extract values
values = []
for row in list1:
for key in self.column_headings:
try:
values.append(str(row[key]))
del row[key]
except KeyError:
values.append(None)
# replace None with empty string
self.data_items = ['' if x is None else x for x in values]
Output

Listing Order Numbers, Items and Subitems from database

So, I'm trying to create a list that pulls from a mysql database that displays an order # as a button and a new window expands to display the order numbers items/subitems with checkboxes to have checked when an item is selected. I have the window to pop up; however, nothing populates and my query prints out information/list. After all boxes are checked and a Finish button is clicked on, it will remove the order from the list to show it has been worked on. I found this on the forums; however, it's not pulling anything for me:
How to fetch data from database and show in table in kivy+python
Thank you!
main.py
import pymysql
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
orderNum = "1"
class TextInputPopup(Popup):
obj = ObjectProperty(None)
obj_text = StringProperty("")
def __init__(self, obj, **kwargs):
super(TextInputPopup, self).__init__(**kwargs)
self.obj = obj
self.obj_text = obj.text
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableButton(RecycleDataViewBehavior, Button):
''' Add selection support to the Button '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableButton, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableButton, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
def on_press(self):
popup = TextInputPopup(self)
popup.open()
def update_changes(self, txt):
self.text = txt
class RV(BoxLayout):
data_items = ListProperty([])
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.get_order()
def get_order(self):
connection = pymysql.connect(host='localhost', user='root',passwd='password', db='Shop')
cursor = connection.cursor()
cursor.execute("SELECT itemName, ITEM FROM order_list WHERE orderID=%s", (orderNum,))
rows = cursor.fetchall()
# create data_items
for row in rows:
for col in row:
self.data_items.append(col)
class TestApp(App):
title = "Kivy RecycleView & pymysql Demo"
def build(self):
return RV()
if __name__ == "__main__":
TestApp().run()
kivy.kv
#:kivy 1.10.0
<TextInputPopup>:
title: "Popup"
size_hint: None, None
size: 400, 400
auto_dismiss: False
BoxLayout:
orientation: "vertical"
TextInput:
id: txtinput
text: root.obj_text
Button:
size_hint: 1, 0.2
text: "Finish"
on_release:
root.obj.update_changes(txtinput.text)
root.dismiss()
Button:
size_hint: 1, 0.2
text: "Cancel Changes"
on_release: root.dismiss()
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
<RV>:
BoxLayout:
orientation: "vertical"
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 2
Label:
text: "Order Number"
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: [{'text': str(x)} for x in root.data_items]
SelectableRecycleGridLayout:
cols: 2
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
Here's my list that pulls information and uses a print function:
('Sub', 'Italian')
('Sub', 'Meatball')
('Drink', 'Apple Juice')
('Side', 'Salad')
('Soup', 'Chicken and rice')
What I have and What I'm Trying to Achieve

Categories

Resources