Kivy Documentation Example error - python

I am trying to learn how to use RecycleView in kivy because apparently, Listview is outdated now. I tried running this code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
Builder.load_string('''
<RV>:
viewclass: 'Label'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
''')
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in range(100)]
class TestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
but I keep getting the error:
kivy.factory.FactoryException: Unknown class
There are no references to ScreenManagement anywhere in the code. I tried importing ScreenManager to my .py file but it didn't work. I don't understand how kivy themselves could put a code that won't work in their own documentation.

Related

Displaying Two RecycleView Widgets Side By Side With Kivy 2.1

I am not too sure as to why I cant display two recycleview widgets side by side within a grid layout or box layout. Anyone have any ideas as to why ? I tried placing them within a boxlayout, gridlayout, and scatter but I can only get Step1 to appear. I tried adjusting the default size hint as well as a couple other parameters within the kv file. Do you think it has something to do with the screen manager. I am trying to really keep most of the display stuff within the kv file if possible. Do you think I should place it within the root widget on the python side of things?
Thanks !
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.pagelayout import PageLayout
from kivy.uix.recycleview import RecycleView
from kivy.uix.screenmanager import ScreenManager, Screen
#from kivy.uix.listview import ListView
import barcode as bc
class Step1(RecycleView):
def __init__(self, **kwargs):
super(Step1, self).__init__(**kwargs)
self.data = [{'text': str(d)} for d in bc.step_one_display()]
class Step2(RecycleView):
def __init__(self, **kwargs):
super(Step2, self).__init__(**kwargs)
self.data = [{'text': str(d)} for d in bc.step_two_display()]
class KVBL(Screen): #Root Wiget
pass
class Setting_Page(Screen):
pass
class KivyOne(App):
def build(self):
sm = ScreenManager()
sm.add_widget(KVBL(name='Main Screen'))
sm.add_widget(Setting_Page(name='Setting Page'))
return sm
KivyOne().run()
kv file
<Step1>:
viewclass: 'Label'
RecycleBoxLayout:
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<Step2>:
viewclass: 'Label'
RecycleBoxLayout:
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<KVBL>:
GridLayout:
cols:2
Step1:
Step2:
<Setting_Page>
GridLayout:
cols: 2
Step1:
GridLayout:
cols:1
Button:
text: 'Move to Origin'
Yeah I am dumb there was not any data in step2 and I changed default_size_hint: 0.5, None. that worked well.
Thanks for the feedback !

When I click a button different button flashes in kivy recycleview

So, I know there have been couple of similar questions but I haven't found a solution on any of those questions.
When I click one button in my RecycleView screen in my kivy app different button flashes. I haven't changed anything about buttons so I don't see any mistake in my code but there might be something that I have not seen.
Here is the code:
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.uix.recycleview import RecycleView
from kivy.app import App
class Kupci_sve(RecycleView, Screen):
def __init__(self, **kwargs):
super(Kupci_sve, self).__init__(**kwargs)
self.data = [{"text": str(i)} for i in range(20)]
self.bar_width = 8
self.scroll_type = ['bars']
kv = Builder.load_string('''
<Kupci_sve>:
name: 'kupci_sve'
viewclass: 'Button'
RecycleBoxLayout:
default_size: (None, 100)
default_size_hint: (1, None)
size_hint_y: None
height: self.minimum_height
orientation: "vertical"
''')
class app(App):
def build(self):
return Kupci_sve()
if __name__ == '__main__':
app().run()
I import this screen to my main.py file and run it from there but I didn't paste that code because I think it has nothing to do with this problem. If you need any other info, just tell me. Thank you.
Without a minimal, complete, reproducible example, I suspect your problem may be in making your Kupci_sve class extend both Screen and RecycleView. A better approach would be to just extend Screen and simply include a RecycleView in the kv rule for <Kupci_sve>.
Here is what I mean:
<Kupci_sve>:
name: 'kupci_sve'
RecycleView:
id: rv # added id
bar_width: 8
scroll_type: ['bars']
viewclass: 'Button'
RecycleBoxLayout:
default_size: (None, 100)
default_size_hint: (1, None)
size_hint_y: None
height: self.minimum_height
orientation: "vertical"
And the Kupci_sve class becomes:
class Kupci_sve(Screen):
def __init__(self, **kwargs):
super(Kupci_sve, self).__init__(**kwargs)
self.ids.rv.data = [{"text": str(i), "on_release":partial(self.butt_release, i)} for i in range(20)]
def butt_release(self, index):
print('butt_release:', index)

How can I access element of RecycleView Kivy?

I wonder to know how to access widgets in RecycleView. I constructed simple example:
main.py
from kivy.app import App
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
class ShowBoxLayout(BoxLayout):
keys = ListProperty()
def __init__(self, **kwargs):
super(ShowBoxLayout, self).__init__(**kwargs)
self.keys = [x for x in range(5)]
def print_list(self):
#here I expect textinputs id but got empty dict
print(self.ids)
class TestApp(App):
def build(self):
bl = ShowBoxLayout()
return bl
app = TestApp()
app.run()
test.kv
<ShowBoxLayout>:
RecycleView:
viewclass: 'TextInput'
data: [{'id': str(x)} for x in range(10)]
RecycleGridLayout:
cols: 1
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: False
touch_multiselect: False
Button:
text : 'Hello world'
on_press : root.print_list()
In that case I cannot access TextInput inside of it with ids nor with anything else. How should I access it in order to get text in them?
This is how screen looks like.
and this is what I get after button is pressed : {}.
The problem is that you cannot assign to the ids dictionary in python. That can only be done in kv. So another way to access the items is to assign an id to the RecycleGridLayout, then visit each of its children. You can also define a method of your viewclass to display the entered text:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
class ShowBoxLayout(BoxLayout):
keys = ListProperty()
def __init__(self, **kwargs):
super(ShowBoxLayout, self).__init__(**kwargs)
self.keys = [x for x in range(5)]
def print_list(self):
#here I expect textinputs id but got empty dict
for child in self.ids.grid.children:
print(child, child.text, child.id)
class MyTextInput(TextInput):
def __init__(self, **kwargs):
super(MyTextInput, self).__init__(**kwargs)
self.multiline = False
self.on_text_validate = self.get_text
def get_text(self):
print('get_text:', self.text)
Builder.load_string('''
<ShowBoxLayout>:
RecycleView:
viewclass: 'MyTextInput'
data: [{'id': str(x)} for x in range(10)]
RecycleGridLayout:
id: grid
cols: 1
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: False
touch_multiselect: False
Button:
text : 'Hello world'
on_press : root.print_list()
''')
class TestApp(App):
def build(self):
bl = ShowBoxLayout()
return bl
app = TestApp()
app.run()
(I used Builder.load_string() as a convenience for myself)
Note that since this is a RecycleView, the viewclass items are recycled, so the print_list() method may not visit an item for every data element, but only the ones currently displayed.

How can I use the RecycleView of Kivy on kv language with ScreenManager?

I have a database on Firebase of Google working well, I can save my data there easily. I would like to return this data for my app, but before I have problems with this, I can't list anything on Kivy.
I would want to use the ListView of Kivy, but in the documentation is recommended to use the RecycleView. But I can't understand the documentation. I have some doubts.
If you can read the docs of RecycleView, you'll see this as an example:
Builder.load_string('''
<RV>:
viewclass: 'Label'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
''')
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in range(100)]
class TestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
But I'm using the ScreenManager to control my screens, then, in the TestApp class I return 'sm', like this example of the documentation:
# Declare both screens
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
pass
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
If you see the syntaxes are different, and it's here where I don't know how to code this. I would like to keep using the ScreenManager to control the screens and use a RecycleView to return my data in a list.
How can I use the RecycleView with my ScreenManager? This is my main.py, I configure the screen in another document, and I use the ki language too. So if you all can to do an example to me I will be grateful.
import kivy
from kivy.app import App, Builder
from kivy.config import Config
from kivy.uix.screenmanager import ScreenManager
from telas.telas import Acesso, Comprando, Vendendo, CadastrarEvento
kivy.require('1.10.1')
Builder.load_file('ing.kv')
Config.read('config.ini')
sm = ScreenManager()
sm.add_widget(Acesso(name='acesso'))
sm.add_widget(Comprando(name='comprando'))
sm.add_widget(Vendendo(name='vendendo'))
sm.add_widget(CadastrarEvento(name='cadastrarEvento'))
sm.add_widget(ListaEventos(name='listaEventos'))
class IngApp(App):
def build(self):
return sm
if __name__ == '__main__':
IngApp().run()
Here the kv that I tried the first time
<ListaEventos>:
canvas:
Rectangle:
source: 'design/fundo.png'
size: self.width, self.height
viewclass: 'Label'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
ListaEventos:
class ListaEvento(Screen, RecycleView):
def __init__(self, **kwargs):
super(ListaEvento, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in range(20)]
You should not inherit from 2 widgets but what widget is going to be painted? For example, if you want an image that behaves like a button, you must inherit from the Image widget and the ButtonBehavior class, that is, visually it is an image but added the button behavior.
So to solve your problem it is not correct to use the inheritance but the composition, that is, to add the RecyclerView as a son of the Screen.
*.py
import kivy
from kivy.app import App, Builder
from kivy.config import Config
from kivy.uix.screenmanager import ScreenManager, Screen
class ListaEventos(Screen):
def __init__(self, **kwargs):
super(ListaEventos, self).__init__(**kwargs)
# assigning data in RecyclerView
self.rv.data = [{'text': str(x)} for x in range(100)]
kivy.require('1.10.1')
Builder.load_file('ing.kv')
Config.read('config.ini')
sm = ScreenManager()
sm.add_widget(ListaEventos(name='listaEventos'))
class IngApp(App):
def build(self):
return sm
if __name__ == '__main__':
IngApp().run()
ing.kv
<ListaEventos>:
rv: rv # expose the widget
canvas:
Rectangle:
source: 'design/fundo.png'
size: self.width, self.height
RecycleView:
id: rv
viewclass: 'Label'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'

Kivy - Editable RecycleView

I'm trying to create an editable table with the RecycleView widget using a TextInput widget as the individual element in Kivy. By looking at the examples and some posts on the web, I was able to write a table that takes input from the user.
Now I'm trying to get which row did the user edit. I could find events for on_text of the text input but that doesn't give information about the row number. Also I tried looking at the events available for the RecycleView but couldn't get much help from it.
Could anyone of you guide me in the right path. I'm fairly new to kivy. I have attached herewith, a simple example that I'm working on.
from kivy.app import App
from kivy.uix.recycleview import RecycleView
from kivy.lang import Builder
Builder.load_string('''
<Row#BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
itemText: ''
TextInput:
id:CellText
text:root.itemText
<RV>:
id: rv
viewclass: 'Row'
scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114)
bar_width: dp(10)
RecycleGridLayout:
cols:3
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: dp(1)
''')
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'itemText':'1'}, {'itemText':'John'}, {'itemText':'K'}, {'itemText':'2'}, {'itemText':'David'}, {'itemText':'P'}]
class rvTestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
rvTestApp().run()
I don't know if the following is recommandable but, to do that when I add a box in the recycle view data i pass it the recycle view instance and when the box is created I add it to a list of the recycle view so you can control the rv from each box and you can control each box from the rv:
from kivy.app import App
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import ObjectProperty, ListProperty
from kivy.clock import Clock
Builder.load_string('''
<Row>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
itemText: ''
TextInput:
id:CellText
text:root.itemText
<RV>:
id: rv
viewclass: 'Row'
scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114)
bar_width: dp(10)
RecycleGridLayout:
cols:3
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: dp(1)
''')
class RV(RecycleView):
list_items = ListProperty([])
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'itemText': '1', 'paren': self, 'index':0}, {'itemText': 'John', 'paren': self, 'index':1},
{'itemText': 'K', 'paren': self, 'index':2}, {'itemText': '2', 'paren': self, 'index':3},
{'itemText': 'David', 'paren': self, 'index':4}, {'itemText': 'P', 'paren': self, 'index':5}]
def which_edit(self, *args):
'''This will print the index of the box which is currently edited'''
print args[0].parent.index
class Row(BoxLayout):
paren = ObjectProperty() #the instance of the rv
def __init__(self, **kwargs):
super(Row, self).__init__(**kwargs)
Clock.schedule_once(self.update)
def update(self, *args):
self.paren.list_items.append(self)
self.ids.CellText.bind(text=self.paren.which_edit)
class TestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
SP SP's answer gets a way to work around this problem but I found another way (hoping this to be a better one) to get the row index.
Adding the below over-riding functions into the row class helped me get the row index when ever the user clicks on the textinput.
class Row(BoxLayout, RecycleDataViewBehavior):
index = None
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(Row, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
if super(Row, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos):
global rowIndex
rowIndex = self.index
Thanks again for helping me with your suggestions. Posting my solution in case if any one else is facing the same problem.

Categories

Resources