如何获取 Python 字典值列表并在 Kivy UI 中显示为 table(使用 ListView 小部件)?

How do I take a Python List of Dictionary Values and display in Kivy UI as a table (Using ListView Widget)?

背景: 我在 Python 2.7.10 on Red Hat Linux 6 上工作。我安装了 Kivy 1.9.2,我正在开发一个应用程序,它将显示来自 Oracle 数据库表的一些数据。我正在使用 cx_Oracle 连接和查询我的 Oracle 数据库。

目前,我可以查询我的数据库和return我正在转换成字典列表的元组列表。

请参阅下面的 "Figure 1",了解我想在 ListView 小部件中显示的值字典。

问题: 我花了一些时间在以下链接中搜索并参考了 Kivy 关于 ListProperty、DictProperty 以及 ListAdapter 和 DictAdapter 的文档: https://kivy.org/docs/api-kivy.properties.html

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

我无法找到解释我正在处理的确切案例的来源:

我有一个字典键列表,我正在 returning 的数据库中每一行的值对。我怎样才能获取这个字典键、值对列表并成功显示为 ListItemLabels,其格式类似于从数据库中 returned 的结果?

错误: 我收到的错误是 ValueError: too many values to unpack,可以在下面的 "Figure 4" 中看到

请让我知道哪些其他信息可能有帮助。谢谢

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

图 1 - 字典值列表

[{'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'}]

图 2 - 我正在使用的 Python 代码

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()

图 3 - 我设置的 .kv kivy 文件显示 ListView 和其他一些小部件

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

图 4 - 在 运行 此代码

后记录错误
[ [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

我建议不要使用列表视图,这对于 kivy 新手来说可能更清楚。这是一个从字典列表中创建 table 的简单示例:

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()

为了根据一行中的键数设置列数,只需将 cols 属性 从 kv 文件移动到 py 文件,并将键数附加到它:

main.py(片段):

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

...

密钥按 car-score-name 的顺序返回,所以我输入 .reverse() 来修复它。