I'm trying to have self.search_results.data printed on my console, but this is not working. Am I using RecycleView the right way? The textbook I'm studying with is from 2014 and uses ListView. I found on the internet that ListView is deprecated. I can't seem to understand how the RecycleView actually works. I've read the documentation, but still can't see.
Kivy:
WeatherRoot:
<WeatherRoot>:
AddLocationForm
<AddLocationForm>:
orientation: "vertical"#
search_input: search_input
search_results: search_results_list
BoxLayout:
height: "40dp"
size_hint_y:None
TextInput:
id: search_input
size_hint_x: 50
focus: True
multiline: False
on_text_validate: root.search_location()
Button:
text: "Search"
size_hint_x: 25
on_press: root.search_location()
Button:
text: "Current Location"
size_hint_x: 25
RecycleView:
id: search_results_list
data: []
Python:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty, ListProperty
from kivy.network.urlrequest import UrlRequest
from kivy.factory import Factory
import json
class WeatherApp(App):
pass
class WeatherRoot(BoxLayout):
pass
class AddLocationForm(BoxLayout):
search_input = ObjectProperty()
search_results = ObjectProperty()
# do something
def search_location(self):
search_template = "http://api.openweathermap.org/data/2.5/find?q=
{}&type=like"
search_url = search_template.format(self.search_input.text)
request = UrlRequest(search_url, self.found_location)
def found_location(self, request, data):
data = json.loads(data.decode()) if not isinstance(data, dict)
else data
cities = ["{} ({})".format(d['name'], d['sys']['country'])
for d in data['list']]
self.search_results.data= cities
print(self.search_results.data)
if __name__ =='__main__':
WeatherApp().run()
The strings in the list should be printed on the console
Kivy ListView » Deprecated
ListView is no longer defined in the recently released stable Kivy version 1.11.0.
Kivy RecycleView » MVC (Model-View-Controller)
The view is generatad by processing the data, essentially a list
of dicts, and uses these dicts to generate instances of the
viewclass as required. Its design is based on the MVC
(Model-view-controller) pattern.
Model: The model is formed by data you pass in via a list of dicts.
View: The View is split across layout and views and implemented using adapters.
Controller: The controller determines the logical interaction and is implemented by RecycleViewBehavior.
Solution
To create a RecycleView of selectable item, one needs to implement the following classes as part of the viewclass. The item is usually a widget e.g. Label, Button, or a group/row of widgets in a layout (BoxLayout or GridLayout).
viewclass
Selectabel recycle layout class, e.g. SelectableRecycleBoxLayout(), or SelectableRecycleGridLayout()
Selectable widget class, e.g. SelectableLabel(), SelectableButton(), or SelectableRow()
data
Creates a list of dicts for data
Example
The following example illustrates the equivalence of a ListView by using RecycleView. The viewclass is a selectable RecycleBoxLayout of Label widget. The app is using OpenWeatherMap's API to retrieve a sample weather data of London, GB (Great Britain).
Note:
To make calls to OpenWeatherMap using the real API point, you need an API key (APPID).
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty, ObjectProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.network.urlrequest import UrlRequest
from kivy.lang import Builder
import json
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 AddLocationForm(BoxLayout):
search_input = ObjectProperty()
search_results = ObjectProperty()
def search_location(self):
search_template = "https://samples.openweathermap.org/data/2.5/find?q={}&appid=b6907d289e10d714a6e88b30761fae22"
# search_template = "https://api.openweathermap.org/data/2.5/find?q={}&typle=like&appid=xyz"
search_url = search_template.format(self.search_input.text)
request = UrlRequest(search_url, self.found_location)
def found_location(self, request, data):
data = json.loads(data.decode()) if not isinstance(data, dict) else data
cities = ["{} ({})".format(d['name'], d['sys']['country']) for d in data['list']]
self.search_results.data = [{'text': str(x)} for x in cities]
print(f"self.search_results.data={self.search_results.data}")
class WeatherRoot(BoxLayout):
pass
class TestApp(App):
title = "Weather App"
def build(self):
return Builder.load_file("main.kv")
if __name__ == '__main__':
TestApp().run()
main.kv
WeatherRoot:
<WeatherRoot>:
AddLocationForm:
<SelectableLabel>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (1, 0, 0, 1) if self.selected else (.0, 0.9, .1, .3)
Rectangle:
pos: self.pos
size: self.size
Color:
rgba: (0, 0.9, .1, .3)
Rectangle:
pos: self.pos
size: self.size
<AddLocationForm>:
orientation: "vertical"
search_input: search_input
search_results: search_results_list
BoxLayout:
height: "40dp"
size_hint_y:None
TextInput:
id: search_input
size_hint_x: 50
focus: True
multiline: False
hint_text: 'Your city name'
on_text_validate: root.search_location()
Button:
text: "Search"
size_hint_x: 25
on_press: root.search_location()
Button:
text: "Current Location"
size_hint_x: 25
RecycleView:
id: search_results_list
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
Output
Related
I'm learning Kivy and I'm on the MDExpansionPanel widget.
Data source from a JSON I use the keys to assemble my panels and the values to compose my Contents.
It happens that I'm able to do it but an extra line always appears in my contents.
I would like your help to delete this line.
I will post my code below:
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelOneLine
from kivy.properties import StringProperty, ObjectProperty
json_data = {'01001BA476': {'price': '74.73',
'product_code': '000003',
'quantity': '100'},
'0100251633': {'price': '92.07',
'product_code': '000156',
'quantity': '1000'}}
KV = '''
<ClassDetails>
orientation: 'vertical'
adaptive_height: True
OneLineIconListItem:
id: info_line
text: root.text
on_press: root.action()
IconLeftWidget:
icon: 'star'
on_press: print(f'star pressed on line: {info_line.text}')
ScrollView:
MDGridLayout:
id: box
cols: 1
adaptive_height: True
'''
class ClassDetails(MDBoxLayout):
text = StringProperty()
action = ObjectProperty()
class InvoicePanel(MDExpansionPanel):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def fechar_pedido(self):
print('You clicked on the information line')
def on_start(self):
for class_title, class_details in json_data.items():
cd = ClassDetails()
expansion_panel = InvoicePanel(panel_cls=MDExpansionPanelOneLine(text=f'Invoice #: {class_title}'), content=cd)
self.root.ids.box.add_widget(expansion_panel)
for item in class_details.items():
cd.add_widget(ClassDetails(
text=str(class_details.values()), action=self.fechar_pedido))
Test().run()
I think the extra line is coming from setting your initial content to a ClassDetails instance, which has a OneLineIconListItem in its definition. Try replacing the content with a simple MDBoxLayout instead:
def on_start(self):
for class_title, class_details in json_data.items():
print(class_title, class_details)
# cd = ClassDetails()
cd = MDBoxLayout(orientation='vertical', adaptive_height=True)
expansion_panel = InvoicePanel(panel_cls=MDExpansionPanelOneLine(text=f'Invoice #: {class_title}'), content=cd)
self.root.ids.box.add_widget(expansion_panel)
for item in class_details.items():
print('\titem:', item)
cd.add_widget(ClassDetails(
text=str(class_details.values()), action=self.fechar_pedido))
I have two screens. MainScreen and Playlist. MainScreen has a RecycleView method added that displays list of items. These items are created from folder names(directories). I want the files inside each folder(directories) to open on the Playlist Screen depending on which directory name is clicked.
I intend to clear the screen immediately after the user leaves the screen.
from kivymd.app import MDApp
from kivymd.uix.button import MDFlatButton, MDRectangleFlatButton
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview import RecycleView
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.uix.screen import MDScreen
from pathlib import Path, PurePath
# Music Path
storageLocation = Path.cwd()
if Path('Books').is_dir():
storageLocation = Path.cwd() / 'Books'
class RecycleViewRow(BoxLayout):
text = StringProperty()
class MainScreen(MDScreen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
booksdir = [f for f in storageLocation.iterdir() if f.is_dir()]
self.children[0].data = [{'text': str(x), 'id': str(x)} for x in booksdir]
# print(self.children[0].data)
class Playlist(MDScreen):
def on_pre_enter(self, *args):
self.ids.Box.clear_widgets()
#self.ids.Box.add_widget(MDRectangleFlatButton(text="done", ))
print(self.manager.ids)
return
class Main(MDApp):
def build(self):
sm = ScreenManager()
sm.add_widget(MainScreen(name='MainScreen'))
sm.add_widget(Playlist(name='Playlist'))
sm.add_widget(Test(name="Test"))
return sm
Main().run()
.kv
<RecycleViewRow>:
orientation: 'vertical'
Button:
text: root.text
#on_press: app.root.message_box(root.text)
on_release:app.root.current = 'Playlist'
<MainScreen>:
RecycleView:
id: rv
viewclass: 'RecycleViewRow'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<Playlist>
BoxLayout:
orientation: 'vertical'
id:Box
You can trigger a method from your RecycleViewRow, like this:
<RecycleViewRow>:
orientation: 'vertical'
Button:
text: root.text
on_release: app.fill_playlist(root.text)
Then, the the fill_playlist() method. you can fill the PlayList:
def fill_playlist(self, dir):
self.root.current = 'Playlist' # this also clears the play list
playlist = self.root.get_screen('Playlist')
for sub in os.listdir(dir):
playlist.ids.Box.add_widget(MDRectangleFlatButton(text=sub))
The code below displays a RecycleView filled with selectable lines. Initially, no item is selected. Pressing on one of the two buttons selects a line. My question is: how can we preselect an item so that one is selected at the dialog opening ?
A solution would be to be able to set a callback function whiqh is called after the RecycleView has been displayed, but how ?
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty, StringProperty, NumericProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
kv = """
<SelectableLabel>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (0.4, 0.4, 0.4, 1) if self.selected else (0.5, 0.5, 0.5, 1)
Rectangle:
pos: self.pos
size: self.size
<KivyPlayer>:
canvas:
Color:
rgba: 0.3, 0.3, 0.3, 1
Rectangle:
size: self.size
pos: self.pos
orientation: 'vertical'
BoxLayout:
orientation: 'vertical'
BoxLayout:
Button:
id: next_track
text: "Next Track"
on_release: controller.select_next()
Button:
id: previous_track
text: "Previous Track"
on_release: controller.select_previous()
BoxLayout:
RecycleView:
id: media_list
viewclass: 'SelectableLabel'
scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114)
bar_width: dp(10)
SelectableRecycleBoxLayout:
id: controller
key_selection: 'selectable'
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
spacing: dp(2)
"""
Builder.load_string(kv)
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
# required to forbid unselecting a selected item. An item can be unselected
# only by selecting another item
touch_deselect_last = BooleanProperty(False)
def get_nodes(self):
nodes = self.get_selectable_nodes()
if self.nodes_order_reversed:
nodes = nodes[::-1]
if not nodes:
return None, None
selected = self.selected_nodes
if not selected: # nothing selected, select the first
self.select_node(nodes[0])
return None, None
if len(nodes) == 1: # the only selectable node is selected already
return None, None
last = nodes.index(selected[-1])
self.clear_selection()
return last, nodes
def select_next(self):
last, nodes = self.get_nodes()
if not nodes:
return
if last == len(nodes) - 1:
self.select_node(nodes[0])
else:
self.select_node(nodes[last + 1])
def select_previous(self):
last, nodes = self.get_nodes()
if not nodes:
return
if not last:
self.select_node(nodes[-1])
else:
self.select_node(nodes[last - 1])
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
if is_selected:
print("selection changed to {0}".format(rv.data[index]))
else:
print("selection removed for {0}".format(rv.data[index]))
class KivyPlayer(BoxLayout):
''' Main Kivy class for creating the initial BoxLayout '''
def __init__(self, **kwargs):
super(KivyPlayer, self).__init__(**kwargs)
# Set media_list data
self.ids.media_list.data = [{'text': str(x), 'selectable': True} for x in range(100)]
class KivyApp(App):
def build(self):
return KivyPlayer()
KivyApp().run()
In your KivyPlayer class, you can specify the selected node in the SelectableRecycleBoxLayout using the selected_nodes list:
class KivyPlayer(BoxLayout):
''' Main Kivy class for creating the initial BoxLayout '''
def __init__(self, **kwargs):
super(KivyPlayer, self).__init__(**kwargs)
# Set media_list data
self.ids.media_list.data = [{'text': str(x), 'selectable': True} for x in range(100)]
# specify pre-selected node by its index in the data
self.ids.controller.selected_nodes = [17]
Am working on a Project that has a RecycleView that contains details of patients.My aim is that when i click on a specific patient on the recycleView row, it should take me to specific dynamic page layout containing specific details of the selected patient in the RecycleView without using the Screen Manager, the switching of dynamic pages.how can i go about this?
i have created a method called change_dynamic_Layout() for switching dynamic pages and it works fine when using the normal button to call it. I have used a print statement in the method to show if the method is executed or not. But when i use the SelectableButton,the print statement is executed but the statement for changing page-layout is not. No errors is shown and the dynamic page does not change. Here is the code try to run it and see what am talking about?
Demo.py
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty, ListProperty,
NumericProperty
from kivy.uix.behaviors import ButtonBehavior
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.factory import Factory
from kivy.uix.screenmanager import ScreenManager, Screen
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.uix.button import Button
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
lost = Builder.load_file('Demo.kv')
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableButton(RecycleDataViewBehavior, Button):
''' 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(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
#selectablebutton to call the change_dynamic_Layout() method in patient class
def on_enter(self):
layout=Patient()
layout.change_dynamic_Layout()
class victor(BoxLayout):
pass
class Patient(Screen):
manage_prescription: ObjectProperty(None)
#Method to change the dynamic pagelayout
def change_dynamic_Layout(self):
layout = Factory.victor()
self.manage_prescription.clear_widgets()
self.manage_prescription.add_widget(layout)
print ('pressed')
class DemoApp(App):
title = 'Hospital Management System'
def build(self):
n= Patient()
return n
if __name__ == "__main__":
DemoApp().run()
demo.kv
<Patient>:
manage_prescription:manage_prescription
BoxLayout:
GridLayout :
cols:1
BoxLayout:
id:manage_prescription
orientation:'vertical'
BoxLayout:
size_hint_y:None
height:40
Button:
text:"NO."
font_size: 25
Button:
text:"Date"
font_size: 25
Button:
text:"Patient"
font_size: 25
Button:
text:"Doctor"
font_size: 25
on_press: root.change_dynamic_Layout()
BoxLayout:
RecycleView:
bar_width: 10
bar_color: 1, 0, 0, 1 # red
bar_inactive_color: 0, 0, 1, 1 # blue
#effect_cls: "ScrollEffect"
scroll_type: ['bars']
viewclass: 'SelectableButton'
data:[{'text': str(x)} for x in range(20)]
SelectableRecycleGridLayout:
cols:4
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
<victor>:
Label:
text:" Switched to specific page for patient's details"
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (1, 1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
on_press: root.on_enter()
Any insight or help is much appreciated, thanks in advance.
The problem is that your on_enter method of the SelectableButton is creating a new Patient layout (Screen), and calling the change_dynamic_Layout method of that new Patient Screen. That newly created Patient Screen is not the one displayed in your app, so it has no effect on what you see. You actually want to call change_dynamic_Layout on the displayed Patient Screen. One way of doing this is to access it via App.get_running_app().root. So that your on_enter method can be changed to:
def on_enter(self):
#layout=Patient()
layout = App.get_running_app().root
layout.change_dynamic_Layout()
Here is the entire python file:
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty, ListProperty, NumericProperty
from kivy.uix.behaviors import ButtonBehavior
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.factory import Factory
from kivy.uix.screenmanager import ScreenManager, Screen
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.uix.button import Button
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
lost = Builder.load_file('Demo.kv')
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableButton(RecycleDataViewBehavior, Button):
''' 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(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
#selectablebutton to call the change_dynamic_Layout() method in patient class
def on_enter(self):
#layout=Patient()
layout = App.get_running_app().root
layout.change_dynamic_Layout()
class victor(BoxLayout):
pass
class Patient(Screen):
manage_prescription: ObjectProperty(None)
#Method to change the dynamic pagelayout
def change_dynamic_Layout(self):
layout = Factory.victor()
self.manage_prescription.clear_widgets()
self.manage_prescription.add_widget(layout)
print ('pressed')
class DemoApp(App):
title = 'Hospital Management System'
def build(self):
n= Patient()
return n
if __name__ == "__main__":
DemoApp().run()
And here is the Demo.kv:
<Patient>:
manage_prescription:manage_prescription
BoxLayout:
GridLayout :
cols:1
BoxLayout:
id:manage_prescription
orientation:'vertical'
BoxLayout:
size_hint_y:None
height:40
Button:
text:"NO."
font_size: 25
Button:
text:"Date"
font_size: 25
Button:
text:"Patient"
font_size: 25
Button:
text:"Doctor"
font_size: 25
on_press: root.change_dynamic_Layout()
BoxLayout:
RecycleView:
bar_width: 10
bar_color: 1, 0, 0, 1 # red
bar_inactive_color: 0, 0, 1, 1 # blue
#effect_cls: "ScrollEffect"
scroll_type: ['bars']
viewclass: 'SelectableButton'
data:[{'text': str(x)} for x in range(20)]
SelectableRecycleGridLayout:
cols:4
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
<victor>:
Label:
text:" Switched to specific page for patient's details"
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (1, 1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
on_press: root.on_enter()
I am trying to make a listview in which the selected buttons produce a popup with different information depending on which list button was selected. Any suggestions? Thank you
ListView has been deprecated since Kivy version 1.10.0. In the example below, we are using Recycleview.
Example
main.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.uix.popup import Popup
from kivy.properties import ListProperty, StringProperty, ObjectProperty
class MessageBox(Popup):
def popup_dismiss(self):
self.dismiss()
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout):
""" Adds selection and focus behaviour to the view. """
selected_value = StringProperty('')
btn_info = ListProperty(['Button 0 Text', 'Button 1 Text', 'Button 2 Text'])
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_press(self):
self.parent.selected_value = 'Selected: {}'.format(self.parent.btn_info[int(self.id)])
def on_release(self):
MessageBox().open()
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(3)]
class TestApp(App):
title = "RecycleView Button Popup Demo"
def build(self):
return RV()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
<MessageBox>:
title: 'Popup Message Box'
size_hint: None, None
size: 400, 400
BoxLayout:
orientation: 'vertical'
Label:
text: app.root.rv_layout.selected_value
Button:
size_hint: 1, 0.2
text: 'OK'
on_press:
root.dismiss()
<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(56)
default_size_hint: 0.1, None
size_hint_y: None
height: self.minimum_height
orientation: "vertical"
Output