50357

Kivy - python - multiple widgets in recycleview row

Question:

I would like to make a recycleview that has multiple labels in each recycleview row. In my specfic example I would like to have 3 labels in each row: 1 label containing the item index, one label containing an item from one dataset, and another label from another dataset

In this example (taken from the kivy examples) we have a recycleview where each row in the recycleview contains a single label:

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>: viewclass: 'SelectableLabel' 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 ''') items_1= {'apple', 'banana', 'pear', 'pineapple'} items_2= {'dog', 'cat', 'rat', 'bat'} 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 if is_selected: print("selection changed to {0}".format(rv.data[index])) else: print("selection removed for {0}".format(rv.data[index])) class RV(RecycleView): def __init__(self, **kwargs): super(RV, self).__init__(**kwargs) self.data = [{'text': str(x)} for x in items_1] class TestApp(App): def build(self): return RV() if __name__ == '__main__': TestApp().run()

I would like each recycleview row to have 3 labels: first label is the index, second label is items_1 and third label is items_2. Like this:

0 apple dog

1 banana cat

2 pear rat

3 pineapple bat

Thank you!

Answer1:

<a href="https://i.stack.imgur.com/kSqrn.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/kSqrn.png" data-original="https://i.stack.imgur.com/kSqrn.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

the easiest way is to change from a RecycleBoxLayout to RecycleGridLayout with 3 columns and using the following list items = [0, "apple", "dog", 1, "banana", "cat", 2, "pear", "rat", 3, "pineapple", "bat"] Obviously, you could stick to you original list data structure and merge them together to form the list above, but I will leave that for you ;).

Another option which should be possible is to add to the RecycleBoxLayout a RecycleBoxLayout with an horizontal orientation per row.

<hr />

This is all the python code.

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.recyclegridlayout import RecycleGridLayout 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>: viewclass: 'SelectableLabel' SelectableRecycleGridLayout: 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 cols: 3 ''') items = [0, "apple", "dog", 1, "banana", "cat", 2, "pear", "rat", 3, "pineapple", "bat"] class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior, RecycleGridLayout): ''' 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 if is_selected: print("selection changed to {0}".format(rv.data[index])) else: print("selection removed for {0}".format(rv.data[index])) class RV(RecycleView): def __init__(self, **kwargs): super(RV, self).__init__(**kwargs) self.data = [{'text': str(x)} for x in items] class TestApp(App): def build(self): return RV() if __name__ == '__main__': TestApp().run()

Answer2:

I was looking for a similar solution and could not find it. I do not think PalimPalim has really answered the question since I think the Ben t was looking for multiple label widgets treated as a single line.

In this case you use a custom widget for GridLayout and specify it's structure.

<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 label1_text: 'label 1 text' label2_text: 'label 2 text' label3_text: 'label 3 text' pos: self.pos size: self.size Label: id: id_label1 text: root.label1_text Label: id: id_label2 text: root.label2_text Label: id: id_label3 text: root.label3_text

In applying your data into the RV, you will need to restructure the dictionary to reflect the label layout

class RV(RecycleView): def __init__(self, **kwargs): super(RV, self).__init__(**kwargs) paired_iter = zip(items_1, items_2) self.data = [] for i1, i2 in paired_iter: d = {'label2': {'text': i1}, 'label3': {'text': i2}} self.data.append(d)

Finally in the refresh_view_attrs, you will specify .label_text which is bound to each label, or you can use label id's.

def refresh_view_attrs(self, rv, index, data): ''' Catch and handle the view changes ''' self.index = index self.label1_text = str(index) self.label2_text = data['label2']['text'] self.ids['id_label3'].text = data['label3']['text'] # As an alternate method of assignment return super(SelectableLabel, self).refresh_view_attrs( rv, index, data)

The entire code is below:

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.uix.gridlayout import GridLayout 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 label1_text: 'label 1 text' label2_text: 'label 2 text' label3_text: 'label 3 text' pos: self.pos size: self.size Label: id: id_label1 text: root.label1_text Label: id: id_label2 text: root.label2_text Label: id: id_label3 text: root.label3_text <RV>: viewclass: 'SelectableLabel' 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 ''') items_1 = {'apple', 'banana', 'pear', 'pineapple'} items_2 = {'dog', 'cat', 'rat', 'bat'} class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout): ''' Adds selection and focus behaviour to the view. ''' class SelectableLabel(RecycleDataViewBehavior, GridLayout): ''' Add selection support to the Label ''' index = None selected = BooleanProperty(False) selectable = BooleanProperty(True) cols = 3 def refresh_view_attrs(self, rv, index, data): ''' Catch and handle the view changes ''' self.index = index self.label1_text = str(index) self.label2_text = data['label2']['text'] self.ids['id_label3'].text = data['label3']['text'] # As an alternate method of assignment 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 RV(RecycleView): def __init__(self, **kwargs): super(RV, self).__init__(**kwargs) paired_iter = zip(items_1, items_2) self.data = [] for i1, i2 in paired_iter: d = {'label2': {'text': i1}, 'label3': {'text': i2}} self.data.append(d) # can also be performed in a complicated one liner for those who like it tricky # self.data = [{'label2': {'text': i1}, 'label3': {'text': i2}} for i1, i2 in zip(items_1, items_2)] class TestApp(App): def build(self): return RV() if __name__ == '__main__': TestApp().run()

Recommend

  • Highlight search text in the search results using angularJS filter
  • Comprator sorting one object always on top
  • Count duplicates in an array [duplicate]
  • “Correlation matrix” for strings. Similarity of nominal data
  • How to select multiple value from a listbox
  • Conditionally merge primary and secondary dictionary in Ansible
  • Home Brew PHP 7.2.5 Install with cURL
  • PHP SwiftMailer or PEAR Mail
  • Zend Failed opening Zend/Application.php
  • How to configure PhpUnit in Xampp?
  • JFrame attached on the side of another JFrame
  • How to update data into a file in a particular position in js
  • Why is this code not working? Hangman
  • Why is JSON.NET adding all these backslashes
  • Getting a generic method to infer the type parameter from the runtime type
  • Finding parents in a tree hierarchy for a given child LINQ (lambda expression)
  • Open an application in a space using applescripts
  • how to populate a SQLite database and use that database in phonegap?
  • UIAlertController button function not working
  • Mysql - How to search for 26 records that each begins with the letter of the alphabet?
  • Refering to the class itself from within a class mehod in Objective C
  • Java: can you cast Class into a specific interface?
  • FileReader+canvas image loading problem
  • Using jQuery closest() method with class selector
  • Insert into database using onclick function
  • Can I display google adwords (AdView) in javafx on android
  • Align navbar back button on right side
  • How to add date and time under each post in guestbook in google app engine
  • How to set/get protobuf's extension field in Go?
  • DirectX11 ClearRenderTargetViewback with transparent buffer?
  • Validaiting emails with Net.Mail MailAddress
  • sending/ receiving email in Java
  • Web-crawler for facebook in python
  • Akka Routing: Reply's send to router ends up as dead letters
  • Cannot Parse HTML Data Using Android / JSOUP
  • Java static initializers and reflection
  • unknown Exception android
  • XCode 8, some methods disappeared ? ex: layoutAttributesClass() -> AnyClass
  • Observable and ngFor in Angular 2
  • Unable to use reactive element in my shiny app