Reputation: 421
As the title suggests, I'm trying to make a data table with a fixed header and a cell size of w400 x h60. If the text inside the label exceeds a width of 400, the label should scroll horizontally. The code posted below is the closest I've come.
Inspecting the tree, ScrollCell (BoxLayout) has dimensions of 100x100 and does not fill respect it's given size or fill the deafult col/row width/height of the RecycleGridLayout.
Here's how it looks:
Any help in fixing this is appreciated.
rtable.py:
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.recycleview import RecycleView
from kivy.properties import BooleanProperty
from kivy.properties import ObjectProperty
from kivy.properties import NumericProperty, StringProperty
import kivy
kivy.require('1.10.0')
class HeaderCell(Label):
pass
class TableHeader(ScrollView):
"""Fixed table header that scrolls x with the data table"""
header = ObjectProperty(None)
def __init__(self, list_dicts=None, *args, **kwargs):
super(TableHeader, self).__init__(*args, **kwargs)
titles = list_dicts[0].keys()
for title in titles:
self.header.add_widget(HeaderCell(text=title))
class ScrollCell(BoxLayout):
text = StringProperty(None)
is_even = BooleanProperty(None)
class TableData(RecycleView):
nrows = NumericProperty(None)
ncols = NumericProperty(None)
rgrid = ObjectProperty(None)
def __init__(self, list_dicts=[], *args, **kwargs):
self.nrows = len(list_dicts)
self.ncols = len(list_dicts[0])
super(TableData, self).__init__(*args, **kwargs)
self.data = []
for i, ord_dict in enumerate(list_dicts):
is_even = i % 2 == 0
row_vals = ord_dict.values()
for text in row_vals:
self.data.append({'text': text, 'is_even': is_even})
class Table(BoxLayout):
def __init__(self, list_dicts=[], *args, **kwargs):
super(Table, self).__init__(*args, **kwargs)
self.orientation = "vertical"
self.header = TableHeader(list_dicts=list_dicts)
self.table_data = TableData(list_dicts=list_dicts)
self.table_data.fbind('scroll_x', self.scroll_with_header)
self.add_widget(self.header)
self.add_widget(self.table_data)
def scroll_with_header(self, obj, value):
self.header.scroll_x = value
if __name__ == '__main__':
from kivy.app import App
from collections import OrderedDict
class RtableApp(App):
def build(self):
data = []
keys = ["Title Col: {}".format(i + 1) for i in range(15)]
for nrow in range(30):
row = OrderedDict.fromkeys(keys)
for i, key in enumerate(keys):
row[key] = "Data col: {}, row: {}".format(i + 1, nrow + 1)
if i % 3 == 0:
row[key] = row[key] + ". Extra long label. " * 3
data.append(row)
return Table(list_dicts=data)
RtableApp().run()
rtable.kv:
<HeaderCell>
size_hint: (None, None)
height: 60
width: 400
text_size: self.size
halign: "left"
valign: "middle"
background_disabled_normal: ''
disabled_color: (1, 1, 1, 1)
canvas.before:
Color:
rgba: 0.165, 0.165, 0.165, 1
Rectangle:
pos: self.pos
size: self.size
<TableHeader>:
header: header
bar_width: 0
do_scroll: False
size_hint: (1, None)
effect_cls: "ScrollEffect"
height: 60
GridLayout:
id: header
rows: 1
size_hint: (None, None)
width: self.minimum_width
height: self.minimum_height
<ScrollCell>:
orientation: 'horizontal'
label: label
size_hint: (None, None)
size: (400, 60)
text: ''
canvas.before:
Color:
rgba: [0.23, 0.23, 0.23, 1] if self.is_even else [0.2, 0.2, 0.2, 1]
Rectangle:
pos: self.pos
size: self.size
ScrollView:
scroll_type: ['bars', 'content']
do_scroll_x: True
do_scroll_y: False
effect_cls: "ScrollEffect"
Label:
id: label
text_size: None, self.height
size_hint: (None, 1)
width: self.texture_size[0]
text: root.text
padding_x: 10
<TableData>:
rgrid: rgrid
bar_width: 25
scroll_type: ['bars']
bar_color: [0.2, 0.7, 0.9, 1]
bar_inactive_color: [0.2, 0.7, 0.9, .5]
do_scroll_x: True
do_scroll_y: True
effect_cls: "ScrollEffect"
viewclass: "ScrollCell"
RecycleGridLayout:
id: rgrid
rows: root.nrows
cols: root.ncols
size_hint: (None, None)
width: self.minimum_width
height: self.minimum_height
col_default_width: 400
row_default_height: 60
Upvotes: 2
Views: 4167
Reputation: 421
I was able to make this work where upon I discovered a flaw in my intended design. If the labels are scrollable and take up the entire area (400x60) of the cell within the RecycleGridLayout then this will break the scroll behavior of the RecycleView by not allowing the table to scroll y and x. This is caused by, presumably, the nested ScrollViews.
There is mention in the docs about being able to have nested ScrollViews but I was not able to figure that out. I also saw in the #kivy irc logs that nested ScrollViews are not recommended. I have decided to go a different route by using a 'tooltip' type widget to display labels when they are wider than the cell.
Upvotes: 3