What i'm trying to do is find a way to refresh a table made by using recycleview,
If you press one of the label created, my code delete data from table, but not refresh the table on a graphic level
Obviously if you press the same label 2 times, python gives ValueError...
Here's my code:
temp.py
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.properties import BooleanProperty
from kivy.properties import ListProperty
class Sfondo_tabella(RecycleDataViewBehavior, Label):
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos) and self.selectable:
x = Screen_1()
x.remove(self.text)
class Screen_1(Screen):
data_items = ListProperty([])
data_items = ['1', '2', '3']
def remove(self, data):
self.data_items.remove(data)
class TempApp(App):
pass
if __name__ == '__main__':
TempApp().run()
temp.kv
ScreenManager:
id: screen_manager
name: "screen_manager"
Screen_1:
id: screen_1
name: "Screen_1"
manager: screen_manager
<Sfondo_tabella>:
# Draw a background to indicate selection
color: 0,0,0,1
font_size: self.height * 0.5
text_size: self.width, None
valign: 'top'
halign: 'center'
canvas.before:
Color:
rgba: (1, 1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
canvas:
Color:
rgba:0,0,0,1
Line:
width:0.5
rectangle:(self.x,self.y,self.width,self.height)
<Screen_1>:
BoxLayout:
RecycleView:
viewclass: 'Sfondo_tabella'
data: [{'text': str(x)} for x in root.data_items]
RecycleGridLayout:
cols: 10
size_hint: 1, None
default_size: None, dp(20)
default_size_hint: 1, None
height: self.minimum_height
width: self.minimum_width
In your code you are not updating the RecycleView data. The refresh_view_attrs function should be overloaded to get the index of the label clicked/touched upon and then the data needs to be updated accordingly. Following is the updated code for temp.py:
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.properties import BooleanProperty
from kivy.properties import ListProperty
class Sfondo_tabella(RecycleDataViewBehavior, Label):
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
self.index = index
return super(Sfondo_tabella, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos) and self.selectable:
x = Screen_1()
x.remove(self.text)
self.parent.parent.data.pop(self.index)
class Screen_1(Screen):
data_items = ListProperty([])
data_items = ['1', '2', '3']
def remove(self, data):
self.data_items.remove(data)
class TempApp(App):
pass
if __name__ == '__main__':
TempApp().run()
This will update properly the data of RecycleView and the GUI accordingly.
Related
In kivy 1.11.1. and python 3. I have in my root class a FloatLayout which contains a Label and a dropdown[text input + RecycleView], but when writing example in my text input, the text input goes up and hide my label, I would like it to be fixes and the dropdown going down. I try to dive into the refresh_view_layout method but without success. Any ideas ?? thanks for you're help :). Here is my python 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
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:
self.parent.parent.parent.children[1].text = self.text # ajout test
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:
print("selection changed to {0}".format(rv.data[index]))
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
class DropDownWidget(BoxLayout):
txt_input = ObjectProperty()
rv = ObjectProperty()
class MyTextInput(TextInput):
txt_input = ObjectProperty()
flt_list = ObjectProperty()
word_list = ListProperty()
#this is the variable storing the number to which the look-up will start
starting_no = NumericProperty(1)
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):
super(Body, self).__init__(**kwargs)
self.add_widget(Label(text = 'This Label is hidden by the dropdown if you type in the text input : "row" or "example" \n and I would like the Text Input to be fixed and not going up when changing text',
pos_hint = {'center_x':.5,'center_y':.6},
color = [100,100,100,1]))
widget_1 = DropDownWidget(pos_hint = {'center_x':.5,'center_y':.5}, \
size_hint = (None, None), size = (600, 60))
widget_1.ids.txt_input.word_list = ['row1', 'row2', 'row3', 'row4', 'row5', 'line1','line2',
'example1','example2','example3','example4','example5','example6','example7','example8','example9','example10']
self.add_widget(widget_1)
class MyApp(App):
def build(self):
return Body()
if __name__ == "__main__":
MyApp().run()
and my kv file :
<Body>:
canvas:
Color:
rgba:(1, 1, 1, 1) # white
Rectangle:
pos: self.pos
size: self.size
<DropDownWidget>:
canvas:
Color:
rgba:(1, 1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
orientation: 'vertical'
spacing: 2
txt_input: txt_input
rv: rv
MyTextInput:
id: txt_input
size_hint_y: None
height: 50
RV:
id: rv
<MyTextInput>:
readonly: False
multiline: False
<SelectableLabel>:
# Draw a background to indicate selection
id: selabel
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
Actually, when I run your code, I don't see the behavior that you described. But if you add the Label and DropDownWidget to the kv, you can take advantage of the bindings that kv sets up for you. Here is how I might do this (there are lots of options). First, I added a ListProperty to the DropDownWidget to hold the word_list:
class DropDownWidget(BoxLayout):
txt_input = ObjectProperty()
rv = ObjectProperty()
word_list = ListProperty([])
And the Body class can just be:
class Body(FloatLayout):
pass
Then, the beginning of the kv can be:
<Body>:
canvas:
Color:
rgba:(1, 1, 1, 1) # white
Rectangle:
pos: self.pos
size: self.size
Label:
id: lab
text: 'This Label is hidden by the dropdown if you type in the text input : "row" or "example"'
pos_hint: {'center_x':.5,'center_y':.6}
color: [100,100,100,1]
size_hint: None, None
size: self.texture_size # use minimal size for the Label
DropDownWidget:
pos_hint: {'center_x':.5}
y: lab.y - self.height # position DropDownWidget just below the Label
size_hint: None, None
size: 600, 60
word_list: ['row1', 'row2', 'row3', 'row4', 'row5', 'line1','line2', 'example1','example2','example3','example4','example5','example6','example7','example8','example9','example10']
<DropDownWidget>:
canvas:
Color:
rgba:(1, 1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
orientation: 'vertical'
spacing: 2
txt_input: txt_input
rv: rv
MyTextInput:
id: txt_input
size_hint_y: None
height: 50
word_list: root.word_list # use the word_list from DropDownWidget
RV:
id: rv
The remainder of the kv is unchanged.
Sorry if this is a ridiculous question. I'm new to programming and so far I've been able to lurk my way through problems that have already been answered, but I can't find anything that answers this particular issue, so here I am with my first ever question to the Stack community. Yay!
I'm writing a fresh produce stock management app in Python/Kivy, using an SQLite database. I've got a RecycleView list with SelectableButton viewclass, and have it contained within a GridLayout.
I have a class method (getItems) within ItemList that extracts the item(string) and quantity(real) from the database, and adds it to a ListProperty variable (data_all), which is where the SelectableButton should in turn get its labels from.
In my example, I've commented out this method, and populated the list in the format that the getItems should do. Actually, as I write this, I wonder if there is a critical difference between the way SQLite stores REAL values, and how python stores float values.
Anyway, ultimately, I'm having trouble displaying the item next to it's quantity in the Recycle view list.
The closest I've come to having it work is to make a 2-column GridLayout, with the item name on the left as a Button, and the quantity on the right as a Label. However, when the list exceeds the bottom of the screen, they scroll independently of one another, as per example 1 below. This is not what I want as the items and quanitities should be next to one another.
Python:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.properties import ObjectProperty, BooleanProperty, ListProperty, StringProperty, ObjectProperty, NumericProperty, DictProperty
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
import string
#import sqlite3
class EditItemPopup(Popup):
obj = ObjectProperty(None)
obj_text = StringProperty("")
#The full code also includes quantity data to be displayed here, but that's irrelevant to the question
def __init__(self, obj, **kwargs):
super(EditItemPopup, self).__init__(**kwargs)
self.obj = obj
self.obj_text = self.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 = EditItemPopup(self)
popup.open()
def update_changes(self, txt):
self.text = txt
class ItemList(Widget):
'''User selects, adds, edits, and deletes items and quanitities'''
#this is how the data is extracted from the SQLite db
data_all = ListProperty(['Apples', 3.0, 'Bananas', 12.0, 'Lettuce', 16.5, 'Asparagus', 3.0])
def __init__(self, **kwargs):
super(ItemList, self).__init__(**kwargs)
# self.getItems()
return
#This method, and the invocation for it in the __init__ methd is how I would typically pull data from the SQLite db. I've left it commented here and open to constructive criticism
# def getItems(self):
# c.execute("SELECT item, quantity FROM itemTable ORDER BY item ASC")
# rows = c.fetchall()
# for row in rows:
# for col in row:
# self.data_all.append(col)
class ColStock(App):
def build(self):
return ItemList()
if __name__ == "__main__":
ColStock().run()
Kivy:
<EditItemPopup>:
title: "Edit Item"
size_hint: None, None
size: 400, 500
auto_dismiss: False
BoxLayout:
orientation: "vertical"
GridLayout:
size_hint_y: None
height: 50
cols: 3
Label:
text: root.obj_text
Button:
size_hint_y: None
height: 50
text: "OK, thanks"
on_press: root.dismiss()
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected==True else (0, 0, 0, 0)
Rectangle:
pos: self.pos
size: self.size
<ItemList>:
GridLayout:
size: root.width,root.height
cols: 2
RecycleView:
viewclass: 'SelectableButton'
data: [{'text': str(x)} for x in root.data_all[0::2]]
SelectableRecycleGridLayout:
cols: 1
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
RecycleView:
viewclass: 'Label'
data: [{'text': str(x)} for x in root.data_all[1::2]]
RecycleGridLayout:
cols: 1
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
I tried working around this by instead creating one GridLayout column, with the item name, a string seperater, and quantity all on the same button, and an rpartition("......qty: ') in the EditItemPopup init method to extract only the item name.
However, everything I've tried returns an error, usually a TypeError.
Python:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.properties import ObjectProperty, BooleanProperty, ListProperty, StringProperty, ObjectProperty, NumericProperty, DictProperty
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
import string
#import sqlite3
class EditItemPopup(Popup):
obj = ObjectProperty(None)
obj_text = StringProperty("")
item_name = StringProperty("")
item_qty = StringProperty("")
#The full code also includes quantity data to be displayed here, but that's irrelevant to the question
def __init__(self, obj, **kwargs):
super(EditItemPopup, self).__init__(**kwargs)
self.obj = obj
self.obj_text = self.obj.text
self.item_tuple = self.obj_text.rpartition("......qty: ")
self.item_name = self.item_tuple[0]
self.item_qty = self.item_tuple[2]
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 = EditItemPopup(self)
popup.open()
def update_changes(self, txt):
self.text = txt
class ItemList(Widget):
'''User selects, adds, edits, and deletes items and quanitities'''
#this is how the data is extracted from the SQLite db
data_all = ListProperty(['Apples', 3.0, 'Bananas', 12.0, 'Lettuce', 16.5, 'Asparagus', 3.0])
def __init__(self, **kwargs):
super(ItemList, self).__init__(**kwargs)
# self.getItems()
return
#This method, and the invocation for it in the __init__ methd is how I would typically pull data from the SQLite db. I've left it commented here and open to constructive criticism
# def getItems(self):
# c.execute("SELECT item, quantity FROM itemTable ORDER BY item ASC")
# rows = c.fetchall()
# for row in rows:
# for col in row:
# self.data_all.append(col)
class ColStock(App):
def build(self):
return ItemList()
if __name__ == "__main__":
ColStock().run()
Kivy:
<EditItemPopup>:
title: "Edit Item"
size_hint: None, None
size: 400, 500
auto_dismiss: False
BoxLayout:
orientation: "vertical"
GridLayout:
size_hint_y: None
height: 50
cols: 3
Label:
text: root.item_name
Label:
text: root.item_qty
Button:
size_hint_y: None
height: 50
text: "OK, thanks"
on_press: root.dismiss()
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected==True else (0, 0, 0, 0)
Rectangle:
pos: self.pos
size: self.size
<ItemList>:
GridLayout:
size: root.width,root.height
cols: 1
RecycleView:
viewclass: 'SelectableButton'
#added self.obj_text.rpartition("......qty: ") for the popup window to extract item name as string
data: [{'text': str(x) + "......qty: " + "Quantity goes here"} for x in root.data_all[0::2]]
SelectableRecycleGridLayout:
cols: 1
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
I've probably violated every convention in programming, and I know it's a hack, but I'd be happy with anything that works. Thank you in advance to anyone who can shed some light on this. :)
So, I've been stuck on this for about 2 or 3 weeks, and the day I ask for help on S.E is the day I discover a solution through trial and error that works for my needs.
I can use the following syntax in the kivy file which adds both item and quantity to each SelectableButton by iterating each pair in the data_all list:
data: [{'text': str(x) + "......qty: " + str(y)} for x,y in zip(root.data_all[0::2],root.data_all[1::2])]
I then add the following to the Popup's init method (python file) to extract only the item name for the label:
self.obj_tuple = self.obj_text.rpartition("......qty: ")
self.item_name = self.obj_tuple[0]
The self.item_name can then also be used to reference the correct row in the SQLite db.
Other suggestions are welcome of course, but I hope this helps someone who is facing similar issues.
I am currently trying to replace some features in an app using the ListView with the RecycleView.
From the documentation I can't figure out how to that though.
The current code looks similar to this:
ListView:
id: x
adapter:
sla.SimpleListAdapter(data=[], cls=label.Label)
x.adapter.data.append(‘frank’)
Are there any resources or tips on how to achieve this?
I am trying to use the recycleview because the ListView seems to be deprecated now.
ListView is removed in Kivy version 1.11.0. Below snippets show the equivalent in RecycleView. There are two examples in Kivy documentation for RecycleView.
The snippets below show the equivalent using RecycleView.
Snippets: kv file
RecycleView:
id: x
viewclass: 'Label'
RecycleBoxLayout:
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Snippets: py file
x.data.append({'text‘: 'frank’)
Examples
The following three examples illustrate Kivy RecycleView.
How to fetch data from database and show in table
How to add vertical scroll in RecycleView
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
Builder.load_string('''
<SelectableLabel>:
# 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>:
id: rv
# Optional: ScrollView attributes
bar_width: 5
bar_color: 1, 0, 0, 1 # red
bar_inactive_color: 0, 0, 1, 1 # blue
effect_cls: "ScrollEffect"
scroll_type: ['bars', 'content']
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
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
''')
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 = is_selected
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in range(100)]
self.data.insert(0, {'text': 'frank'})
self.data.append({'text': 'liam'})
class TestApp(App):
title = 'Kivy RecycleView Demo'
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
Output
I'm trying to better understand how the RecycleView functions. Seems like only practical examples will teach me. Docs aren't helping. Kindly have a look at my current screen in the pic below.
Now following are what I'm trying to achieve.
Add/Remove new rows to/from the list.
The first column sl/no should maintain the order even after deleting from in between.
How to achieve 'sort' function? Is it by taking the data into python and do the sort and then update that to the RV again ?
Please find the both .py and .kv codes below.
rv_main.py
import os
os.environ['KIVY_GL_BACKEND'] = 'gl'
import kivy
kivy.require('1.11.0')
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.properties import ListProperty, BooleanProperty
from kivy.properties import NumericProperty
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
#-----------------------------------------------------------------------
class RecycleViewRow(RecycleDataViewBehavior, BoxLayout):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
slno = StringProperty('')
typ = StringProperty('')
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(RecycleViewRow, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(RecycleViewRow, 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:
pass
else:
pass
def delete_row(self,rv, index, is_selected):
if is_selected:
rv.data.pop(index)
rv.layout_manager.clear_selection()
#-----------------------------------------------------------------------
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
#fetch data from the database
self.data = [{'slno': str(x+1),'typ': 'default'} for x in range(4)]
#-----------------------------------------------------------------------
class DataTable(BoxLayout):
def addRow(self):
pass
def removeRow(self):
pass
#-----------------------------------------------------------------------
class RvMainApp(App):
def build(self):
return DataTable()
if __name__ == '__main__':
RvMainApp().run()
rvmain.kv
#: kivy 1.11.0
<RecycleViewRow>:
id: rv
slno: ""
typ: ""
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0.4, 0.4, 0.4, 1)
Rectangle:
pos: self.pos
size: self.size
orientation: 'horizontal'
size_hint: 1.0, 1.0
Label:
text: root.slno
size_hint_x : 1.0
Label:
text: root.typ
size_hint_x : 1.0
#----------------------------------------------------------------
<RV>:
id : rv
viewclass: 'RecycleViewRow'
SelectableRecycleBoxLayout:
default_size: None, dp(40)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
#----------------------------------------------------------------
<DataTable>:
orientation : 'vertical'
Button:
BoxLayout:
Button:
RV:
Button:
BoxLayout:
Button:
text: "Add"
on_release: rv.data.append({"slno": "?", "typ": 'default'})
Button:
text: "Remove"
on_release:
You edit the data list of the recycleview. The data will be sorted as it is sorted in that list.
So here is an example of the add remove feature:
from kivy.app import App
from kivy.lang import Builder
KV = '''
<Row#BoxLayout>:
ind: 1
Button:
text: str(root.ind)
Button:
text: "default"
BoxLayout:
ind: 1
orientation: "vertical"
Button:
BoxLayout:
Button:
RecycleView:
id: rv
data: [{"text":"first","ind":1}]
viewclass: 'Row'
RecycleBoxLayout:
default_size_hint: 1, None
default_size: None, dp(56)
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Button
BoxLayout:
Button:
text: "Add"
on_release:
root.ind += 1
rv.data.append({"ind": root.ind})
Button:
text: "Remove"
on_release:
root.ind = root.ind - 1 if root.ind > 0 else 0
if len(rv.data): rv.data.pop(-1)
'''
class Test(App):
def build(self):
self.root = Builder.load_string(KV)
return self.root
Test().run()
And here is an example on how to sort the data list by some key. In this case ind.
from kivy.app import App
from kivy.lang import Builder
KV = '''
RecycleView:
viewclass: 'Label'
data: sorted([{"text":"!", "ind":3},{"text":"world", "ind":2},{"text":"hello", "ind":1}], key=lambda k: k["ind"])
RecycleBoxLayout:
id: layout
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
'''
class TestApp(App):
def build(self):
return Builder.load_string(KV)
if __name__ == '__main__':
TestApp().run()
How can I pass data from RequestRecycleView to refresh_view_data? I tried with global variables and instantiating data in RequestRecycleView but still can't trigger refresh_view_data by appending Observable data. It is working when I return RequestRecycleView as root but I want ScreenManager to be my root.
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
from random import sample
from string import ascii_lowercase
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
kv = """
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManager:
transition: FadeTransition()
RequestsScreen:
<RequestRow#BoxLayout>
size_hint_y: None
orientation: 'vertical'
row_index: id_row_index.text
row_index:''
pos: self.pos
size: self.size
Label:
id: id_row_index
text: root.row_index
<RequestRecycleView#RecycleView>:
#id: rv
viewclass: 'RequestRow'
SelectableRecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
<RequestsScreen>
name: 'RequestsScreen'
BoxLayout:
orientation: 'vertical'
Label:
text: 'recycle'
RequestRecycleView:
"""
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
class RequestRow(RecycleDataViewBehavior):
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
self.row_index = str(index)
self.row_content = data['text']
return super(RequestRow, self).refresh_view_attrs(
rv, index, data)
class ScreenManagement(ScreenManager):
pass
class RequestRecycleView(RecycleView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.data = []
for r in range(30):
row = {'text': ''.join(sample(ascii_lowercase, 6))}
self.data.append(row)
class RequestsScreen(Screen):
pass
Builder.load_string(kv)
sm = ScreenManagement()
sm.add_widget(RequestsScreen(name = 'requests'))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
By modifying
<RequestRow#BoxLayout>
size_hint_y: None
orientation: 'vertical'
row_index: id_row_index.text
row_index:''
pos: self.pos
size: self.size
Label:
id: id_row_index
text: root.row_index
to
<RequestRow#BoxLayout>
text: 'abba'
size_hint_y: None
orientation: 'vertical'
row_index: id_row_index.text
row_index:''
pos: self.pos
size: self.size
Label:
id: id_row_index
text: self.parent.text
results in working code. Note that the dictionary keys in the data are expected to be attributes of the viewclass. The added text attribute in RequestRow, provides that attribute, and the text: self.parent.text in the Label places that text (from the viewclass) into the Label. Also, you can replace the lines:
Builder.load_string(kv)
sm = ScreenManagement()
sm.add_widget(RequestsScreen(name = 'requests'))
with just:
sm = Builder.load_string(kv)
since your kv file specifies the ScreenManager as the root object.