Kivy – python – varios widgets en la fila de reciclaje

Me gustaría hacer un reciclaje que tenga varias tags en cada fila de reciclaje. En mi ejemplo específico, me gustaría tener 3 tags en cada fila: 1 etiqueta que contiene el índice del elemento, una etiqueta que contiene un elemento de un conjunto de datos y otra etiqueta de otro conjunto de datos

En este ejemplo (tomado de los ejemplos de kivy) tenemos una vista de reciclaje donde cada fila en la vista de reciclaje contiene una sola etiqueta:

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(''' : # 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 : 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() 

Me gustaría que cada fila de reciclaje tenga 3 tags: la primera etiqueta es el índice, la segunda etiqueta es items_1 y la tercera etiqueta es items_2. Me gusta esto:

0 manzana perro

1 gato plátano

Rata pera 2

3 murciélagos de piña

¡Gracias!

introduzca la descripción de la imagen aquí

la forma más fácil es cambiar de un RecycleBoxLayout a RecycleGridLayout con 3 columnas y usando los siguientes items = [0, "apple", "dog", 1, "banana", "cat", 2, "pear", "rat", 3, "pineapple", "bat"] lista items = [0, "apple", "dog", 1, "banana", "cat", 2, "pear", "rat", 3, "pineapple", "bat"] Obviamente, podrías mantenerte en la estructura de datos de la lista original y unirlos para formar la lista anterior, pero dejaré eso para ti;).

Otra opción que debería ser posible es agregar a RecycleBoxLayout un RecycleBoxLayout con una orientación horizontal por fila.


Este es todo el código de Python.

 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(''' : # 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 : 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() 

Estaba buscando una solución similar y no la encontré. No creo que PalimPalim realmente haya respondido la pregunta, ya que creo que Ben t estaba buscando varios widgets de tags tratados como una sola línea.

En este caso, utiliza un widget personalizado para GridLayout y especifica su estructura.

 : # 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 

Al aplicar sus datos en el RV, deberá reestructurar el diccionario para reflejar el diseño de la etiqueta

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

Finalmente, en refresh_view_attrs, especificará .label_text que está enlazado a cada etiqueta, o puede usar los identificadores de tags.

 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) 

El código completo está abajo:

 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(''' : # 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 : 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) paird_iter = zip(items_1, items_2) self.data = [] for i1, i2 in paird_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()