How do I take a Python List of Dictionary Values and display in Kivy UI as a table (Using ListView Widget)?
Question: **Background:** I am working in Python 2.7.10 on Red Hat Linux 6. I have Kivy
1.9.2 installed and I am developing an app that will display some data from
Oracle Database tables. I am using cx_Oracle to connect and query my Oracle
Database.

Currently, I am able to query my database and return a list of tuples that I
am converting into a list of dictionaries.

See "Figure 1" below for the dictionary of values I'd like to display in a
ListView widget.

**Problem:** I've spent some time searching and have referenced Kivy's
documentation on ListProperty, DictProperty as well as ListAdapter and
DictAdapter at the following links: <https://kivy.org/docs/api-
kivy.properties.html>

<https://kivy.org/docs/api-kivy.adapters.adapter.html>

I have not been able to find a source that explains the exact case I am
working with here:

**I have a list of Dictionary key, value pairs for each row from the database
that I am returning. How can I take this list of Dictionary key, value pairs
and successfully display as ListItemLabels formatted like a result returned
from the database?**

Error: The error I am receiving is `ValueError: too many values to unpack`
that can be seen in "Figure 4" below

Please let me know what other information might be helpful. Thanks

======================================

Figure 1 - List of Dictionary Values

    
    
    [{'PLAYER_NAME': 'NAME', 'LOST': 'LOST', 'GP': 'GP', 'CAR': 'CAR', 'LNG': 'LNG', 'TEAM': 'Nebraska', 'YDSG': 'YDS/G', 'TD': 'TD', 'FUM': 'FUM', 'YDS': 'YDS'},
    {'PLAYER_NAME': 'Homerecord', 'LOST': '0', 'GP': '7', 'CAR': '262', 'LNG': '55', 'TEAM': 'Nebraska', 'YDSG': '174.3', 'TD': '14', 'FUM': '0', 'YDS': '1220'},
    {'PLAYER_NAME': 'Awayrecord', 'LOST': '0', 'GP': '5', 'CAR': '172', 'LNG': '69', 'TEAM': 'Nebraska', 'YDSG': '158.8', 'TD': '6', 'FUM': '0', 'YDS': '794'},
    {'PLAYER_NAME': 'vsAPrankedteams', 'LOST': '0', 'GP': '2', 'CAR': '74', 'LNG': '21', 'TEAM': 'Nebraska', 'YDSG': '158', 'TD': '5', 'FUM': '0', 'YDS': '316'},
    {'PLAYER_NAME': 'vsUSArankedteams', 'LOST': '0', 'GP': '2', 'CAR': '74', 'LNG': '21', 'TEAM': 'Nebraska', 'YDSG': '158', 'TD': '5', 'FUM': '0', 'YDS': '316'},
    {'PLAYER_NAME': 'vs.ConferenceTeams', 'LOST': '0', 'GP': '8', 'CAR': '289', 'LNG': '69', 'TEAM': 'Nebraska', 'YDSG': '154.4', 'TD': '15', 'FUM': '0', 'YDS': '1235'},
    {'PLAYER_NAME': 'vs.non-ConferenceTeams', 'LOST': '0', 'GP': '4', 'CAR': '145', 'LNG': '32', 'TEAM': 'Nebraska', 'YDSG': '194.8', 'TD': '5', 'FUM': '0', 'YDS': '779'},
    {'PLAYER_NAME': 'Inwins', 'LOST': '0', 'GP': '5', 'CAR': '189', 'LNG': '69', 'TEAM': 'Nebraska', 'YDSG': '211.2', 'TD': '10', 'FUM': '0', 'YDS': '1056'},
    {'PLAYER_NAME': 'Inlosses', 'LOST': '0', 'GP': '7', 'CAR': '245', 'LNG': '55', 'TEAM': 'Nebraska', 'YDSG': '136.9', 'TD': '10', 'FUM': '0', 'YDS': '958'},
    {'PLAYER_NAME': 'September', 'LOST': '0', 'GP': '4', 'CAR': '145', 'LNG': '32', 'TEAM': 'Nebraska', 'YDSG': '194.8', 'TD': '5', 'FUM': '0', 'YDS': '779'},
    {'PLAYER_NAME': 'October', 'LOST': '0', 'GP': '5', 'CAR': '177', 'LNG': '69', 'TEAM': 'Nebraska', 'YDSG': '149', 'TD': '9', 'FUM': '0', 'YDS': '745'},
    {'PLAYER_NAME': 'November', 'LOST': '0', 'GP': '3', 'CAR': '112', 'LNG': '38', 'TEAM': 'Nebraska', 'YDSG': '163.3', 'TD': '6', 'FUM': '0', 'YDS': '490'},
    {'PLAYER_NAME': 'Finalmargin0-7', 'LOST': '0', 'GP': '6', 'CAR': '214', 'LNG': '55', 'TEAM': 'Nebraska', 'YDSG': '153.8', 'TD': '9', 'FUM': '0', 'YDS': '923'},
    {'PLAYER_NAME': 'Finalmargin8-14', 'LOST': '0', 'GP': '3', 'CAR': '106', 'LNG': '28', 'TEAM': 'Nebraska', 'YDSG': '152', 'TD': '5', 'FUM': '0', 'YDS': '456'},
    {'PLAYER_NAME': 'Finalmargin15+', 'LOST': '0', 'GP': '3', 'CAR': '114', 'LNG': '69', 'TEAM': 'Nebraska', 'YDSG': '211.7', 'TD': '6', 'FUM': '0', 'YDS': '635'}]
    

Figure 2 - The Python Code I am working with

    
    
    from kivy.app import App
    from kivy.uix.widget import Widget
    from kivy.uix.tabbedpanel import TabbedPanel
    from kivy.uix.gridlayout import GridLayout
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.floatlayout import FloatLayout
    from kivy.uix.listview import * #ListItemLabel, ListItemButton
    from kivy.lang import Builder
    from kivy.properties import *
    from kivy.event import *
    import cx_Oracle
    import os
    import json
    from decimal import Decimal
    os.environ["ORACLE_HOME"] = "/u01/app/oracle..." #related to cx_Oracle
    os.environ["LD_LIBRARY_PATH"] = "/u01/app/oracle..." #related to cx_Oracle
    print(os.environ["ORACLE_HOME"])
    print(os.environ["LD_LIBRARY_PATH"])
    
    
    class TabData(TabbedPanel): #Root Widget
        first = ListProperty()
        search_input = ObjectProperty()
    
        def on_enter(self):
            self.return_data()
    
        def query(self):
            search = TabData()
            con = cx_Oracle.connect('SCOTT/*******@localhost/j1db') #cx_Oracle connection object
            cur = con.cursor()
            statement = 'select * from FBS_SPLT_RUSH where TEAM = :t'
            exe = cur.execute(statement, {'t': str(self.search_input.text)})
            columns = [i[0] for i in cur.description]
            exe2 = [dict(zip(columns, row)) for row in cur]
            return exe2
    
        def return_data(self):
            for row in self.query():
                self.first.append(row)
    
            print(self.first)
            print self.search_input
            return self.first
    
        def args_converter(self, index, data_item):
            key, value = data_item
            for key, value in data_item:
                return {'text': (key, value)}
    
    
    class TeamStatsApp(App):
        def build(self):
            return Builder.load_file('/usr/games/team stats/TeamStats.kv')
    
    if __name__ == '__main__':
        TeamStatsApp().run()
    

Figure 3 - .kv kivy file I have set up to display ListView and some other
widgets

    
    
    #: kivy 1.0
    #: import main main
    #: import ListAdapter kivy.adapters.listadapter.ListAdapter
    #: import DictAdapter kivy.adapters.dictadapter.DictAdapter
    #: import sla kivy.adapters.simplelistadapter
    #: import Label kivy.uix.label
    #: import ListItemLabel kivy.uix.listview.ListItemLabel
    #: import ListItemButton kivy.uix.listview.ListItemButton
    #: import CompositeListItem kivy.uix.listview.CompositeListItem
    #: import ut kivy.utils
    
    TabData:
        id: rootrun
        do_default_tab: False
        search_input: search_box
        TabbedPanelItem:
            text: "hello"
            BoxLayout:
                orientation: "vertical"
                TextInput:
                    id: search_box
                    focus: True
                    size_hint_y: .1
                    multiline: False
                    on_text_validate: root.on_enter()
                Button:
                    size_hint_y: .1
                    text: "Return"
                    on_press: root.return_data()
                GridLayout:
                    cols: 5
                    ListView:
                        adapter:
                            ListAdapter(data=root.first, cls=ListItemButton, args_converter=root.args_converter)
    

Figure 4 - Log with Error after running this code

    
    
    [ [1;32mINFO [0m   ] [Base        ] Leaving application in progress...
    
     Traceback (most recent call last):
       File "/usr/games/team stats/main.py", line 57, in <module>
         TeamStatsApp().run()
       File "/usr/local/lib/python2.7/site-packages/kivy/app.py", line 828, in run
         runTouchApp()
       File "/usr/local/lib/python2.7/site-packages/kivy/base.py", line 487, in runTouchApp
         EventLoop.window.mainloop()
       File "/usr/local/lib/python2.7/site-packages/kivy/core/window/window_sdl2.py", line 622, in mainloop
         self._mainloop()
       File "/usr/local/lib/python2.7/site-packages/kivy/core/window/window_sdl2.py", line 365, in _mainloop
         EventLoop.idle()
       File "/usr/local/lib/python2.7/site-packages/kivy/base.py", line 327, in idle
         Clock.tick()
       File "/usr/local/lib/python2.7/site-packages/kivy/clock.py", line 515, in tick
         self._process_events()
       File "/usr/local/lib/python2.7/site-packages/kivy/clock.py", line 647, in _process_events
         event.tick(self._last_tick, remove)
       File "/usr/local/lib/python2.7/site-packages/kivy/clock.py", line 406, in tick
         ret = callback(self._dt)
       File "/usr/local/lib/python2.7/site-packages/kivy/uix/listview.py", line 950, in _spopulate
         self.populate()
       File "/usr/local/lib/python2.7/site-packages/kivy/uix/listview.py", line 998, in populate
         item_view = self.adapter.get_view(index)
       File "/usr/local/lib/python2.7/site-packages/kivy/adapters/listadapter.py", line 211, in get_view
         item_view = self.create_view(index)
       File "/usr/local/lib/python2.7/site-packages/kivy/adapters/listadapter.py", line 228, in create_view
         item_args = self.args_converter(index, item)
       File "/usr/games/team stats/main.py", line 47, in args_converter
         key, value = data_item
     ValueError: too many values to unpack
    


Answer: I would suggest doing it without a listview, which might be more clear for
somebody new to kivy. Here is a simple, minimalistic example of creating a
table out of list of dicts:

test.kv:

    
    
    #:kivy 1.9.0
    
    <PlayerRecord>:
        size_hint_y: None
        height: '30dp'
        width: '100dp'
    
        canvas.before:
            Color:
                rgb: 0.2, 0.2, 0.2
            Rectangle:
                pos: self.pos
                size: self.size
    
    <TableHeader>
        size_hint_y: None
        height: '30dp'
        width: '100dp'
    
        canvas.before:
            Color:
                rgb: 0.5, 0.5, 0.5
            Rectangle:
                pos: self.pos
                size: self.size
    
    AnchorLayout:
        anchor_x: 'center'
        anchor_y: 'center'
    
        ScrollView:
            size_hint_y: None
            height: '200dp'
    
            MyGrid:
                cols: 3
                size_hint_y: None
                height: self.minimum_height
                spacing: '1dp'
    

main.py:

    
    
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    from kivy.app import App
    from kivy.uix.gridlayout import GridLayout
    from kivy.uix.label import Label
    
    
    class TableHeader(Label):
        pass
    
    
    class PlayerRecord(Label):
        pass
    
    
    class MyGrid(GridLayout):
    
        def __init__(self, **kwargs):
            super(MyGrid, self).__init__(**kwargs)
            self.fetch_data_from_database()
            self.display_scores()
    
        def fetch_data_from_database(self):
            self.data = [
                {'name': 'name', 'score': 'score', 'car': 'car'},
                {'name': 'przyczajony', 'score': '1337', 'car': 'Fiat 126p'},
                {'name': 'Krusader Jake', 'score': '777', 'car': 'Ford'},
                {'name': 'dummy', 'score': '0', 'car': 'none'},
                {'name': 'dummy', 'score': '0', 'car': 'none'},
                {'name': 'dummy', 'score': '0', 'car': 'none'},
                {'name': 'dummy', 'score': '0', 'car': 'none'},
                {'name': 'dummy', 'score': '0', 'car': 'none'},
                {'name': 'dummy', 'score': '0', 'car': 'none'},
                {'name': 'dummy', 'score': '0', 'car': 'none'},
                {'name': 'dummy', 'score': '0', 'car': 'none'}
            ]
    
        def display_scores(self):
            self.clear_widgets()
            for i in xrange(len(self.data)):
                if i < 1:
                    row = self.create_header(i)
                else:
                    row = self.create_player_info(i)
                for item in row:
                    self.add_widget(item)
    
        def create_header(self, i):
            first_column = TableHeader(text=self.data[i]['name'])
            second_column = TableHeader(text=self.data[i]['score'])
            third_column = TableHeader(text=self.data[i]['car'])
            return [first_column, second_column, third_column]
    
        def create_player_info(self, i):
            first_column = PlayerRecord(text=self.data[i]['name'])
            second_column = PlayerRecord(text=self.data[i]['score'])
            third_column = PlayerRecord(text=self.data[i]['car'])
            return [first_column, second_column, third_column]
    
    
    class Test(App):
        pass
    
    
    Test().run()
    

* * *

In order to set a number of columns based on number of keys in a row, simply
move `cols` property from kv to py file, and attach number of keys to it:

main.py (fragment):

    
    
    from kivy.properties import NumericProperty
    ...
    class MyGrid(GridLayout):
    
        cols = NumericProperty()
    
        def fetch_data_from_database(self):
            self.data = [{...},...]
            self.cols = len(self.data[0].keys())
    ...
        def create_header(self, i):
            cols = []
            row_keys = self.data[i].keys()
            row_keys.reverse()
            for key in row_keys:
                cols.append(TableHeader(text=self.data[i][key]))
            return cols
    
        def create_player_info(self, i):
            cols = []
            row_keys = self.data[i].keys()
            row_keys.reverse()
            for key in row_keys:
                cols.append(PlayerRecord(text=self.data[i][key]))
            return cols
    
    ...
    

Keys were returned in order _car-score-name_ , so I put `.reverse()` to fix
it.

