Skip to content

Instantly share code, notes, and snippets.

@sirpercival
Created March 30, 2015 13:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save sirpercival/4acce2375a22b6ea724e to your computer and use it in GitHub Desktop.
Save sirpercival/4acce2375a22b6ea724e to your computer and use it in GitHub Desktop.
DataTable widget for kivy
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.properties import DictProperty, NumericProperty, StringProperty, \
BooleanProperty, ObjectProperty
from operator import itemgetter
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
Builder.load_string("""
<ColHeader>:
bold: True
on_press: self.data_table.sort_by(self.text)
<RowHeader>:
background_down: self.background_normal
<EditableCell>:
multiline: False
on_focus: if not self.focus: self.data_table.data_update(self.id, self.text)
<StaticCell>:
halign: 'left'
<DataTable>:
id: table_grid
cols: self.ncol
""")
class ColHeader(Button):
#For some reason, adding this property as part of a
#dynamic class declaration was failing.
data_table = ObjectProperty(None)
class RowHeader(Button):
data_table = ObjectProperty(None)
initial_type = ObjectProperty(None)
class EditableCell(TextInput):
data_table = ObjectProperty(None)
initial_type = ObjectProperty(None)
class StaticCell(Label):
data_table = ObjectProperty(None)
initial_type = ObjectProperty(None)
class DataTable(GridLayout):
"""This is a compound widget designed to display
a dictionary of data as a nice table. The dictionary
should have the column headers as keys, and then
the associated value is a list of data for that
column.
You may have lists of different lengths, but the columns
will fill from the top down; therefore, include blank
strings as placeholders for any empty cells.
Note that since the column headers are dict keys, you
must have unique column names. Sorry..."""
data = DictProperty({})
ncol = NumericProperty(0)
nrow = NumericProperty(0)
editable = BooleanProperty(False)
header_col = StringProperty('')
def __init__(self, data = {}, editable = False, header_column = '',
header_row = [], **kw):
super(DataTable, self).__init__(**kw)
self.data = data
self.ncol = len(data)
self.editable = editable
self.header_col = header_column
self.header_row = header_row
celltype = EditableCell if self.editable else StaticCell
self.nrow = max([len(data[x]) for x in data])
self.cells = {}
for key in self.header_row:
cell_id = str(key)+'_head'
cell = ColHeader(text = str(key), data_table = self, id = cell_id)
self.cells[cell_id] = cell
self.add_widget(cell)
for i in xrange(self.nrow):
get = itemgetter(i)
for key in self.header_row:
cell_id = str(key)+'_'+str(i)
if i <= len(self.data[key]):
text = get(self.data[key])
else:
text = ''
self.data[key].append('')
if key == self.header_col:
self.cells[cell_id] = RowHeader(text = str(text), data_table = self,
id = cell_id, initial_type = type(text))
else:
self.cells[cell_id] = celltype(text = str(text), data_table = self,
id = cell_id, initial_type = type(text))
self.add_widget(self.cells[cell_id])
def data_update(self, cell_id, value):
"""This will try to convert the value
to the initial type of the data. If that fails,
it'll just be a string. The initial type won't
change, however."""
key, idx = cell_id.split('_')
try:
val = self.cells[cell_id].initial_type(value)
except ValueError:
val = value
self.data[key][int(idx)] = val
def sort_by(self, colname):
column_to_order = enumerate(self.data[colname])
sort_order = map(itemgetter(0),
sorted(column_to_order, key=itemgetter(1)))
for key in self.data:
col = self.data[key]
self.data[key] = [col[x] for x in sort_order]
self.cells[str(key)+'_head'].background_color = (1, 1, 1, 1)
for i in xrange(self.nrow):
self.cells[str(key)+'_'+str(i)].text = str(self.data[key][i])
self.cells[colname+'_head'].background_color = (0, 1, 0, 1)
if __name__ == '__main__':
import random
data = {'Col1':[random.random() for x in xrange(10)],
'Col2':[random.random() for x in xrange(10)],
'Col3':[random.random() for x in xrange(10)],
'Col4':[random.random() for x in xrange(10)],
'Col5':[random.random() for x in xrange(10)]}
from kivy.base import runTouchApp
from kivy.uix.pagelayout import PageLayout
from kivy.factory import Factory
Builder.load_string("""
<Page@AnchorLayout>:
canvas.before:
Color:
rgba: 0, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
""")
pg = PageLayout()
staticpage = Factory.Page(name='static')
staticpage.add_widget(DataTable(name = 'static', data=data, header_column = 'Col1',
header_row = ['Col'+str(x+1) for x in xrange(5)]))
editpage = Factory.Page(name='edit')
editpage.add_widget(DataTable(name = 'edit', data=data, header_column = 'Col5',
header_row = ['Col'+str(5-x) for x in xrange(5)],
editable = True))
pg.add_widget(staticpage)
pg.add_widget(editpage)
runTouchApp(pg)
@applicatlat
Copy link

I tried to run your code it gave unresolved reference 'xrange'

@Garikayi
Copy link

try changing xrange to just range

@Garikayi
Copy link

When I run it, I am getting the following error: Traceback (most recent call last):
File "/home/gari/Documents/api_02/datatable/datatablegist.py", line 141, in
staticpage = Factory.Page(name='static')
File "/home/gari/kivy_venv/lib/python3.8/site-packages/kivy/uix/anchorlayout.py", line 68, in init
super(AnchorLayout, self).init(**kwargs)
File "/home/gari/kivy_venv/lib/python3.8/site-packages/kivy/uix/layout.py", line 76, in init
super(Layout, self).init(**kwargs)
File "/home/gari/kivy_venv/lib/python3.8/site-packages/kivy/uix/widget.py", line 350, in init
super(Widget, self).init(**kwargs)
File "kivy/_event.pyx", line 245, in kivy._event.EventDispatcher.init
TypeError: object.init() takes exactly one argument (the instance to initialize)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment