如何将多个列放入 kivy RecycleView?

How to put multiple columns into a kivy RecycleView?

我想把一个(csv-)table的数据放到一个kivy recycleview中。

如果我将固定文本分配给 kv 中的标签,我设法用一行插入多列,但我无法用字典列表中的数据填充标签。这是到目前为止的代码,我用来测试这个概念:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout
import csv

items = [{'SP1': 'Artikelnummer', 'SP2': 'Name', 'SP3': 'Groesse'},
    {'SP1': '510001', 'SP2': 'Big Pump', 'SP3': '1.50 L'},
    {'SP1': '523001', 'SP2': 'Leonie Still', 'SP3': '1.50 L'},
    {'SP1': '641301', 'SP2': 'Cola Mix', 'SP3': '1.50 L'}
]

class Tabelle(BoxLayout):
    def __init__(self, **kwargs):
        super(Tabelle, self).__init__(**kwargs)

    def insert_SP(self, data):
        for i in data:
            self.spalte1_SP = i['SP1']
            #print(self.spalte1_SP)
            self.spalte2_SP = i['SP2']
            self.spalte3_SP = i['SP3']

Builder.load_string('''
<Tabelle>:
    orientation: 'horizontal'
    spalte1_SP: 'spalte1'
    spalte2_SP: 'spalte2'
    spalte3_SP: 'spalte3'
    Label:
        id: Spalte1
        text: root.spalte1_SP
    Label:
        id: Spalte2
        text: root.spalte2_SP
    Label:
        id: Spalte3
        text: root.spalte3_SP

<RV>:
    viewclass: 'Tabelle'
    RecycleBoxLayout:
        default_size: None, dp(20)
        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 = []
        x = Tabelle()
        x.insert_SP(items)

class TestApp(App):
    def build(self):
        return RV()

if __name__ == '__main__':
    TestApp().run()

我希望在 3 列中看到来自 items 的数据,但由于某些原因它们仍然是空的。

它是空的,因为 data 没有填充。

解决方案

  • 删除class Tabelle()
  • 中的所有编码
  • pass 添加到 class Tabelle()
  • 将以下内容添加到构造函数中,__init__() of class RV()

片段

self.data = [{'spalte1_SP': str(x['SP1']), 'spalte2_SP': str(x['SP2']), 'spalte3_SP': str(x['SP3'])} for x in items]

Kivy RecycleView » data

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.

data

The data used by the current view adapter. This is a list of dicts whose keys map to the corresponding property names of the viewclass.

data is an AliasProperty that gets and sets the data used to generate the views.

例子

main.py

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout

items = [{'SP1': 'Artikelnummer', 'SP2': 'Name', 'SP3': 'Groesse'},
         {'SP1': '510001', 'SP2': 'Big Pump', 'SP3': '1.50 L'},
         {'SP1': '523001', 'SP2': 'Leonie Still', 'SP3': '1.50 L'},
         {'SP1': '641301', 'SP2': 'Cola Mix', 'SP3': '1.50 L'}
         ]


class Tabelle(BoxLayout):
    pass


Builder.load_string('''
<Tabelle>:
    orientation: 'horizontal'
    spalte1_SP: 'spalte1'
    spalte2_SP: 'spalte2'
    spalte3_SP: 'spalte3'
    Label:
        id: SP1
        text: root.spalte1_SP
    Label:
        id: SP2
        text: root.spalte2_SP
    Label:
        id: SP3
        text: root.spalte3_SP

<RV>:
    viewclass: 'Tabelle'
    RecycleBoxLayout:
        default_size: None, dp(20)
        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 = [{'spalte1_SP': str(x['SP1']), 'spalte2_SP': str(x['SP2']), 'spalte3_SP': str(x['SP3'])} for x in items]


class TestApp(App):
    def build(self):
        return RV()


if __name__ == '__main__':
    TestApp().run()

输出

在前面的解决方案的基础上,这里有一个代码,它的解释要容易得多。此外,其中一列是一个复选框。

结果:

Python 文件:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout

# data
items = [{'number': '510001', 'name': 'Big Pump', 'size': '1.50 L', 'in_stock': True},
         {'number': '523001', 'name': 'Leonie Still', 'size': '1.60 L', 'in_stock': False},
         {'number': '641301', 'name': 'Apple Mix', 'size': '1.30 L', 'in_stock': True},
         {'number': '681301', 'name': 'Orange Mix', 'size': '1.40 L', 'in_stock': True}
        ]


class MultiFieldLine(BoxLayout):
    # class layout defined in kv file
    pass


class AppGUI(GridLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.rv.data = [{'label_1': str(x['number']), 'label_2': str(x['name']), 'label_3': str(x['size']), 'checkbox_1': x['in_stock']} for x in items]


class ExploreRecycleViewMultipleFieldApp(App):
    def build(self):
        return AppGUI()


if __name__ == '__main__':
    ExploreRecycleViewMultipleFieldApp().run()

KV文件:

<MultiFieldLine>:
    orientation: 'horizontal'
    label_1: ''
    label_2: ''
    label_3: ''
    checkbox_1: False
    Label:
        text: root.label_1
    Label:
        text: root.label_2
    Label:
        text: root.label_3
    CheckBox:
        active: root.checkbox_1
<AppGUI>: # inherit from GridLayout
    rv: rv_id
    cols:  1
    rows: 2
    GridLayout: # col titles
        cols: 4
        rows: 1
        size_hint_y: 0.04
        Label:
            text: 'Number'
        Label:
            text: 'Name'
        Label:
            text: 'Size'
        Label
            text: 'In stock'
    GridLayout: # data
        cols: 1
        rows: 1
        size_hint_y: 0.96
        RecycleView:
            id: rv_id
            viewclass: 'MultiFieldLine'
            RecycleBoxLayout:
                default_size: None, dp(20)
                default_size_hint: 1, None
                size_hint_y: None
                height: self.minimum_height
                orientation: 'vertical'

下面的版本添加了行选择以及名称列的更大标签宽度以及每次选择一行时在 gui class 中调用的方法。:

结果:

Python 文件:

from kivy.app import App
from kivy.properties import BooleanProperty
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout

# data
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleview.views import RecycleDataViewBehavior

items = [{'number': '510001', 'name': 'Big Pump', 'size': '1.50 L', 'in_stock': True},
         {'number': '523001', 'name': 'Leonie Still very, very,very long, long name', 'size': '1.60 L', 'in_stock': False},
         {'number': '641301', 'name': 'Apple Mix', 'size': '1.30 L', 'in_stock': True},
         {'number': '681301', 'name': 'Orange Mix', 'size': '1.40 L', 'in_stock': True}
        ]


class MultiFieldLine(RecycleDataViewBehavior, BoxLayout):
    ''' class layout defined in kv file '''
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)
    
    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.rv = rv
        self.appGUI = rv.appGUI
        self.index = index
        
        return super(MultiFieldLine, self).refresh_view_attrs(
            rv, index, data)
    
    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(MultiFieldLine, 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):
        # instance variable used in .kv file to change the selected item
        # color !
        self.selected = is_selected
  
        if is_selected:
            self.appGUI.outputSelectedData(rv.data[index])


class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
                                 RecycleBoxLayout):
    ''' Adds selection and focus behaviour to the view. '''
    
    # required to authorise unselecting a selected item
    touch_deselect_last = BooleanProperty(True)


class AppGUI(GridLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.rv.data = [{'label_1': str(x['number']), 'label_2': str(x['name']), 'label_3': str(x['size']), 'checkbox_1': x['in_stock']} for x in items]

    def outputSelectedData(self, data):
    # method called when a line is selected
        print(data)

class ExploreRecycleViewMultipleFieldBoxLayoutApp(App):
    def build(self):
        return AppGUI()


if __name__ == '__main__':
    ExploreRecycleViewMultipleFieldBoxLayoutApp().run()

KV文件:

<MultiFieldLine>: # inherit from RecycleDataViewBehavior and BoxLayout
    orientation: 'horizontal'
    label_1: ''
    label_2: ''
    label_3: ''
    checkbox_1: False
    # add selection background
    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)
    Label:
        size_hint_x: 0.1
        text: root.label_1
    Label:
        size_hint_x: 0.7
        text: root.label_2
    Label:
        size_hint_x: 0.1
        text: root.label_3
    CheckBox:
        size_hint_x: 0.1
        active: root.checkbox_1
<AppGUI>: # inherit from GridLayout
    rv: rv_id
    cols:  1
    rows: 2
    GridLayout: # col titles
        cols: 4
        rows: 1
        size_hint_y: 0.04
        Label:
            size_hint_x: 0.1
            text: 'Number'
        Label:
            size_hint_x: 0.7
            text: 'Name'
        Label:
            size_hint_x: 0.1
            text: 'Size'
        Label
            size_hint_x: 0.1
            text: 'In stock'
    GridLayout: # data
        cols: 1
        rows: 1
        size_hint_y: 0.96
        RecycleView:
            id: rv_id
            appGUI: root
            viewclass: 'MultiFieldLine'
            SelectableRecycleBoxLayout:
                default_size: None, dp(20)
                default_size_hint: 1, None
                size_hint_y: None
                height: self.minimum_height
                orientation: 'vertical'