i have a question about dynamic listview in python with kivy. So the listview is working, but know i want to get the text from the clicked listview item, to push them to the python file. But i can't finde any solution that works for me. I think one way to do this is with "on_selection_change" but i'm not able to get this run.
So here is my code
BoxLayout:
pos: 130,10
size: 500,400
ListView:
adapter:
ListAdapter(data=root.my_data,
args_converter=lambda row_index, an_obj: {'text': an_obj,'size_hint_y': 28,'height': 40, 'font_size': 20},
selection_mode='single',
allow_empty_selection=True,
cls=ListItemButton)
<ListItemButton>:
#on_selection_change=self.on_selection_change
on_release: app.getname()
and my python file
class PCScreen(GridLayout,Screen):
filename = open("/home/pi/Documents/ppcont/config/name.txt")
my_data = ListProperty(filename)
def getname(self):
pass
As for ListView, use ModalView to control selection and invoke getname() method. Please refer to the two examples (ListView and RecycleView) for details.
Note
ListView has been deprecated since version 1.10.0, use RecycleView instead.
Example 1 - ListView
mainlistview.py
from kivy.app import App
from kivy.uix.modalview import ModalView
from kivy.properties import ObjectProperty
class MyListView(ModalView):
list_view = ObjectProperty(None)
def __init__(self, **kwargs):
super(MyListView, self).__init__(**kwargs)
self.list_view.adapter.bind(on_selection_change=self.callback_function)
def callback_function(self, instance):
print(instance)
App.get_running_app().getname()
class MainListViewApp(App):
title = "ListView ListItemButton Demo"
def build(self):
return MyListView()
def getname(self):
print("App.getname() - Called")
if __name__ == '__main__':
MainListViewApp().run()
mainlistview.kv
#:kivy 1.10.0
#:import lv kivy.uix.listview
#:import la kivy.adapters.listadapter
<MyListView>:
list_view: list_view
ListView:
id: list_view
adapter:
la.ListAdapter(
data=["Item #{0}".format(i) for i in range(100)],
selection_mode='single',
allow_empty_selection=True,
cls=lv.ListItemButton)
Output - ListView
Example 2 - RecycleView
mainrecycleview.py
from kivy.app import App
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.properties import ObjectProperty
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout):
""" Adds selection and focus behaviour to the view. """
pass
class SelectableButton(RecycleDataViewBehavior, Button):
""" Add selection support to the Label """
index = None
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_release(self):
print("Pressed & released on", self.text)
App.get_running_app().getname()
class RV(RecycleView):
rv_layout = ObjectProperty(None)
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': "Button " + str(x), 'id': str(x)} for x in range(100)]
class MainRecycleViewApp(App):
title = "RecycleView Button Demo"
def build(self):
return RV()
def getname(self):
print("RecycleView: App.getname() - Called")
if __name__ == "__main__":
MainRecycleViewApp().run()
mainrecycleview.kv
#:kivy 1.10.0
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (0.0, 0.9, 0.1, 0.3)
Rectangle:
pos: self.pos
size: self.size
<RV>:
rv_layout: layout
viewclass: 'SelectableButton'
SelectableRecycleBoxLayout:
id: layout
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: "vertical"
Output - RecycleView
I really think you should dump ListView, its deprecated (from the docs):
ListAdapter:
Module: kivy.adapters.listadapter Added in 1.5
Deprecated since version 1.10.0: The feature has been deprecated.
you should really use https://kivy.org/docs/api-kivy.uix.recycleview.html
Related
I made a recycleview list of custom cards. For each card i want to pass a custom object called "show", its type is "Content".
The class of custom card is "StreamingShowCard". I can't create the card by KVlang because its class contains some pytho methods.
This is the code:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivymd.uix.card import MDCard
from kivy.properties import StringProperty
from kivy.uix.behaviors import ButtonBehavior
from kivymd.uix.list import OneLineIconListItem
from kivymd.uix.tab import MDTabsBase
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.label import MDLabel
from kivymd.uix.gridlayout import MDGridLayout
from kivymd.uix.fitimage import FitImage
from kivy.uix.image import AsyncImage
from kivymd.uix.button import MDFlatButton
#import datetime
from datetime import datetime
from kivy.uix.image import Image
from kivymd.uix.dialog import MDDialog
from kivymd.uix.screen import MDScreen
from kivymd.uix.responsivelayout import MDResponsiveLayout
from kivymd.uix.progressbar import MDProgressBar
from kivy.clock import Clock
from kivymd.uix.button import MDIconButton
from kivymd.uix.list import IconLeftWidget
from kivymd.uix.recycleview import MDRecycleView
from kivy import properties
KV = '''
<Cover>:
<StreamingShowCard>:
show: root.show
MDScreen:
MDRecycleView:
size_hint_y: 1
size_hint_x: 1
viewclass: 'StreamingShowCard'
id: rv
MDRecycleGridLayout:
cols: 2
height: self.minimum_height
size_hint_y: None
row_default_height: '250dp'
row_force_default: True
padding: dp(10)
spacing: dp(10)
MDFloatingActionButton:
id: fab
icon: "plus"
pos_hint: {"center_x": .5, "center_y": .1}
on_release: app.add_item()
'''
class MD3Card(MDCard, RoundedRectangularElevationBehavior):
'''Implements a material design v3 card.'''
text = StringProperty()
class Content: #eg: film found
name = ""
url = ""
platform = ""
imageUrl = ""
Image = False
#free = False
def __init__(self, name, url, platform, imageUrl):
self.name = name
self.url = url
self.platform = platform
self.imageUrl = imageUrl
class StreamingShowCard(MD3Card):
#show = None #the object of Content class attached to the
show = properties.ObjectProperty()
def __init__(self, **kwargs):
super(StreamingShowCard, self).__init__(**kwargs)
self.__set_cardGraphic()
self.image = Cover(size_hint_min_y=0.7, source="https://kivy.org/doc/stable/_static/logo-kivy.png", opacity= 100 if True else 0, radius= ["10dp", "10dp", "0dp", "0dp"])
grid1 = MDGridLayout(cols=1, size_hint_x=1, size_hint_y=1, rows=3)
grid1.add_widget(self.image)
grid1.add_widget(MDLabel(text=self.show, size_hint_y = 0.3, font_style='Subtitle2', halign='left', valign='middle'))
box1 = MDBoxLayout(orientation = "horizontal", size_hint_x=1, size_hint_y=0.2)
box1.add_widget(MDLabel(text="self.show.platform", size_hint_y = 1, size_hint_x=0.8, theme_text_color="Secondary", font_style='Caption', halign='left', valign='middle'))
grid1.add_widget(box1)
self.add_widget(grid1)
def __set_cardGraphic(self):
self.md_bg_color = "#18222c"
#self.size_hint_y = None
self.elevation = 10
self.padding = "1dp"
self.size_hint_x = 1
self.radius = "10dp"
self.ripple_behavior = True
self.on_press = self.cardPress
def cardPress(self, *args):
dialog = MDDialog(
title = "Aprire la pagina in un browser?",
text = "Text",#self.show.name,
size_hint = (0.8, 0.8),
auto_dismiss = False,
buttons=[
MDFlatButton(text="No", text_color=self.theme_cls.primary_color, on_release=lambda x: dialog.dismiss()),
MDFlatButton(text="Si", text_color=self.theme_cls.primary_color, on_release=lambda x: yesClick())
]
)
dialog.open()
def yesClick():
#openUrl(self.show.url)
dialog.dismiss(force=True)
def loadImage(self, *args):
if not self.show.Image:
self.show.loadImage(True)
self.image.source = self.show.imageUrl
self.image.opacity = 100
self.image.reload()
class Cover(AsyncImage, FitImage):
pass
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
def add_item(self):
for i in range(100):
self.root.ids.rv.data.append({
"show": Content("name", "www.google.com", "platform", "https://kivy.org/doc/stable/_static/logo-kivy.png")
})
MainApp().run()
If the recycleview's viewclass is a Label i can pass the text directly from main by appending dict to RV data, but whit this custom class of recycle view i cannot pass Content to show property.
Is it possible to populate the RecycleView by the main?
Couldn't get your code to run, but if you want to use an ObjectProperty in the data of a RecycleView, you need to handle it a bit differently. Here is an example of using an ObjectProperty in the data:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.uix.recycleview import RecycleView
Builder.load_string('''
<MyObject>:
size_hint_y: None
height: 100
Label:
id: label
text: root.text # uses the text StringProperty
size_hint: None, None
size: 200, 100
<RV>:
viewclass: 'MyObject'
RecycleBoxLayout:
default_size: None, 100
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
''')
class MyObject(BoxLayout):
show = ObjectProperty(None)
text = StringProperty('Abba')
def on_show(self, instance, new_obj):
# handle the ObjectProperty named show
if new_obj.parent:
# remove this obj from any other MyObject instance
new_obj.parent.remove_widget(new_obj)
for ch in self.children:
if isinstance(ch, Image):
# remove any previous obj instances
self.remove_widget(ch)
break
# add the new obj to this MyObject instance
self.add_widget(new_obj)
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x),
'show': Image(source='tester.png', size_hint=(None, None), size=(100, 100),
allow_stretch=True, keep_ratio=True)}
for x in range(100)]
class TestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
beginner here.
Im not completely sure if everything is correct in the code bellow, but it seems to work for making the size of the buttons created by recycleview, to adapt to each button's text.
Question:
How can i access (print for now) the clicked button's id in the on_release_m method?
Thanks for your time!
from kivy.config import Config
Config.set('graphics', 'width', '270')
Config.set('graphics', 'height', '550')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.lang import Builder
# I included this in case you have to use it for the answer instead of the python code:
Builder.load_string("""
<MyRecycleBoxLayout>:
# orientation: 'vertical'
# default_size: None, None
# default_size_hint: 1, None
# size_hint_y: None
# height: self.minimum_height
<MyButton>:
# font_size: 30
# text_size: self.width, None
# size_hint_y: None
# height: self.texture_size[1]
""")
class MyRecycleBoxLayout(RecycleBoxLayout):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.orientation= 'vertical'
self.default_size= (None, None)
self.default_size_hint=( 1, None)
self.size_hint_y= None
self.bind(minimum_height=self.setter("height"))
class MyButton(Button):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.font_size=30
self.bind(width=lambda s, w: s.setter("text_size")(s, (w, None)))
self.size_hint_y = None
self.bind(texture_size=self.setter("size"))
class RV(RecycleView):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.myRecycleBoxLayout=MyRecycleBoxLayout()
self.data.clear()
for i in range(150):
self.data.append({f"text":f"{i} "*50,"id": f"btn_{i}","on_release":self.on_release_m})
self.add_widget(self.myRecycleBoxLayout)
def on_release_m (self,*args):
#Here print the clicked button id and not the first's:
print(self.data[0]["id"])
pass
class RecApp(App):
def build(self):
self.rv=RV()
self.rv.viewclass=MyButton
return self.rv
RecApp().run()
You can pass additional information to your on_release_m() method by using partial:
for i in range(150):
self.data.append({f"text": f"{i} "*50, "id": f"btn_{i}", "on_release": partial(self.on_release_m, i)})
Then, in the on_release_m() method:
def on_release_m(self, i):
#Here print the clicked button id and not the first's:
print(self.data[i]["id"])
In below code I need the button on the edit_button_tab to switch to edit_input_tab. I really need to switch it that way as I need to switch between predefined classes EditButton and EditInput. This is a part of a bigger program with few Buttons in different location of a layout and I cant define them within <MainTabbedPanel> class. I've tried many ways to call switch_to (example in the quotes) but they didn't work.
CODE
from kivy.animation import Animation
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelStrip
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelHeader
from kivy.factory import Factory
theRoot = """
#:import Factory kivy.factory.Factory
<EditButton>
orientation: 'vertical'
Button:
text: 'Switch to Edit Screen'
on_press: root.change_tab('edit_screen')
<EditInput>
orientation: 'vertical'
TextInput:
<UnCloseableHeader>
color: 0,0,0,1
disabled_color: self.color
# variable tab_width
text: 'tabx'
size_hint_x: None
width: self.texture_size[0] + 40
BoxLayout:
pos: root.pos
size_hint: None, None
size_y: 20
padding: 3
Label:
id: lbl
text: root.text
<MainTabbedPanel#BoxLayout>
size_hint: (1, 1)
default_tab: edit_button_tab
tab_width: 130
FloatLayout:
EditButton:
id: edit_button
EditInput:
id: edit_input
UnCloseableHeader:
id: edit_button_tab
text: 'Edit'
content: edit_button.__self__
UnCloseableHeader:
id: edit_input_tab
text: 'Edit Tab'
content: edit_input.__self__
MainTabbedPanel:
"""
class EditInput(BoxLayout):
def __init__(self, **kwargs):
super(EditInput, self).__init__(**kwargs)
class EditButton(BoxLayout):
def __init__(self, **kwargs):
super(EditButton, self).__init__(**kwargs)
def change_tab(self, tab):
print('TAB', tab)
#call switch method from MainTabbedPanel
'''the way I've tried
mtp = MainTabbedPanel
mtp.switch_to('edit_input_tab')'''
class MainTabbedPanel(TabbedPanel):
def __init__(self, **kwargs):
super(MainTabbedPanel, self).__init__(**kwargs)
def switch(self, tab):
print("SWITCH TO", tab, self.ids.keys())
self.switch_to(self.ids[tab])
class UnCloseableHeader(TabbedPanelHeader):
pass
Factory.register('UnCloseableHeader', cls=UnCloseableHeader)
sm = Builder.load_string(theRoot)
class TabbedPanelApp(App):
def build(self):
return sm
if __name__ == '__main__':
TabbedPanelApp().run()
EDIT
I've tried with below snippet. It prints IDS of MainTabbedPanel but does not change the tabs.
class EditButton(BoxLayout):
def __init__(self, **kwargs):
super(EditButton, self).__init__(**kwargs)
def change_tab(self, tab):
print('TAB', tab)
MainTabbedPanel.tab = tab
MainTabbedPanel()
#call switch method from MainTabbedPanel
'''the way I've tried
mtp = MainTabbedPanel
mtp.switch_to('edit_input_tab')'''
class MainTabbedPanel(TabbedPanel):
tab = ''
def __init__(self, **kwargs):
super(MainTabbedPanel, self).__init__(**kwargs)
self.tabs_showing = True
if self.tab != '':
Clock.schedule_once(self.switch)
def switch(self, dt):
print("SWITCH TO", self.tab, self.ids.keys())
self.switch_to(self.ids[self.tab])
Use App.get_running_app() to get an instance of your app
Use root to get an instance of your root
Snippets
def change_tab(self, tab):
print('TAB', tab)
mtp = App.get_running_app().root
mtp.switch_to(mtp.ids.edit_input_tab)
Notes
In your kv, you defined a Dynamic class,
<MainTabbedPanel#BoxLayout>. It should be a class rule,
<MainTabbedPanel> because in your Python code, you have defined
class MainTabbedPanel(TabbedPanel): i.e. inheritance mismatch and
class type mismatch.
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 have this snippet from my design.kv file:
<Track>:
on_release:
root.print_data(self.text)
RecycleView:
viewclass: 'Track'
RecycleGridLayout:
cols: 1
default_size_hint: 1, None
orientation: 'vertical'
However it returns an error:
The class 'Track was defined as seen in the snippet above as well as in my python code.
I tried setting the viewclass to 'Button' and it worked but it just returned a button which is not the intended behavior.
What am I getting wrong here?
Thanks :)
The whole code of my python and kivy files are right here: https://github.com/Jezrianne/ANTS
Just in case the error does not originate from the snippet above :)
Root Widget - Screen
The following example illustrates using Screen widget as the root widget and combined with RecycleView widget.
main.py
from kivy.app import App
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.button import Button
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.11.0
<Track>:
on_release:
root.print_data(self.text)
<RootWidget>:
RecycleView:
id: rv
viewclass: 'Track'
SelectableRecycleGridLayout:
cols: 1
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
''')
class Track(RecycleDataViewBehavior, Button):
def print_data(self, text):
print("\nprint_data: text=", text)
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class RootWidget(Screen):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
self.ids.rv.data = [{'text': str(x)} for x in range(100)]
class TestApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
TestApp().run()
Root Widget - RecycleView
There is no attribute, orientation: for GridLayout. Please remove it from your kv file.
You need to implement the following:
Snippet
class Track(RecycleDataViewBehavior, Button):
def print_data(self, text):
print("\nprint_data: text=", text)
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
Example
main.py
from kivy.app import App
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.button import Button
class Track(RecycleDataViewBehavior, Button):
def print_data(self, text):
print("\nprint_data: text=", text)
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
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()
test.kv
#:kivy 1.11.0
<Track>:
on_release:
root.print_data(self.text)
<RV>:
viewclass: 'Track'
SelectableRecycleGridLayout:
cols: 1
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
Output