I am using _on_keyboard_down
event in Tree View
.label to be selected with the up
and down
keys.I added scroll in Tree View
List.Can someone tell me how to scroll with up
and down
when needed scroll?
class TreeviewGroup(Popup):
treeview = ObjectProperty(None)
tv = ObjectProperty(None)
h = NumericProperty(0)
popup = ObjectProperty()
def __init__(self, **kwargs):
super(TreeviewGroup, self).__init__(**kwargs)
self.tv = TreeView(root_options=dict(text=""),
hide_root=False,
indent_level=4)
rows = [('test1'),('test2'),('test3'),('test4'),('test5'),('test6')]
tree = []
for r in rows:
tree.append({'node_id': r, 'children': []})
for branch in tree:
populate_tree_view(self.tv, None, branch)
#self.remove_widgets()
self.treeview.add_widget(self.tv)
Clock.schedule_once(self.update, 1)
self.bind(on_open=self.on_open)
def on_open(self, *args):
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down=self._on_keyboard_down)
if self.tv.selected_node is None:
self.tv.select_node(self.tv.root.nodes[0])
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
node = self.tv.selected_node
_, key = keycode
if key in ('down', 'up'):
parent = node.parent_node
ix = parent.nodes.index(node)
nx = ix+1 if 'down' else ix-1
next_node = parent.nodes[nx % len(parent.nodes)]
self.tv.select_node(next_node)
return True
elif key == 'enter':
App.get_running_app().root.name.text = node.text
keyboard.release()
self.dismiss()
def remove_widgets(self):
for child in [child for child in self.treeview.children]:
self.treeview.remove_widget(child)
def update(self, *args):
self.h = len([child for child in self.tv.children]) * 24
<TreeviewGroup>:
treeview: treeview
title: "Select"
title_size: 17
title_font: "Verdana"
size: 800, 800
auto_dismiss: False
BoxLayout
orientation: "vertical"
ScrollView:
size_hint: 1, .9
BoxLayout:
size_hint_y: None
id: treeview
height: root.h
GridLayout:
cols : 2
row_default_height: '20dp'
size_hint: .5, 0.2
pos_hint: {'x': .25, 'y': 1}
Button:
text: 'Ok'
on_release: root.dismiss()
Button:
text: 'Cancel'
on_release: root.dismiss()
<TreeViewLabel>:
text_size: self.size
height: 30
You have to use the scroll_to()
method and pass the node, you must also set the height of the BoxLayout
(self.treeview) to the size of the content, in this case to the TreeView
(self.tv).
So you must remove the height: root.h
in the .kv, and as you create the TreeView
in the .py we will make the connection there, in addition to removing that update method that is not necessary and that interrupts the operation of the ScrollView
.
I do not understand why it is the value 24
in:
self.h = len([child for child in self.tv.children]) * 24
It would not be more appropriate 30
which is the height of the TreeViewLabel
, in addition to using Clock
for this type of tasks is not appropriate, the use of bind()
:
self.tv.bind(minimum_height=self.treeview.setter('height'))
is the solution.
You must also change:
nx = ix+1 if 'down' else ix-1
to
nx = ix+1 if key == 'down' else ix-1
otherwise you can never climb.
Making all those changes the solution is:
*.py
class TreeviewGroup(Popup):
treeview = ObjectProperty(None)
tv = ObjectProperty(None)
h = NumericProperty(0)
popup = ObjectProperty()
def __init__(self, **kwargs):
super(TreeviewGroup, self).__init__(**kwargs)
self.tv = TreeView(root_options=dict(text=""),
hide_root=False,
indent_level=4)
rows = [('test{}').format(i) for i in range(1, 20)]#[('test1'),('test2'),('test3'),('test4'),('test5'),('test6')]
tree = [{'node_id': r, 'children': []} for r in rows]
self.tv.bind(minimum_height=self.treeview.setter('height'))
for branch in tree:
populate_tree_view(self.tv, None, branch)
#self.remove_widgets()
self.treeview.add_widget(self.tv)
self.bind(on_open=self.on_open)
def on_open(self, *args):
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down=self._on_keyboard_down)
if self.tv.selected_node is None:
self.tv.select_node(self.tv.root.nodes[0])
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
node = self.tv.selected_node
_, key = keycode
if key in ('down', 'up'):
parent = node.parent_node
ix = parent.nodes.index(node)
nx = ix+1 if key == 'down' else ix-1
next_node = parent.nodes[nx % len(parent.nodes)]
self.tv.select_node(next_node)
self.scroll.scroll_to(next_node)
return True
elif key == 'enter':
App.get_running_app().root.name.text = node.text
keyboard.release()
self.dismiss()
def remove_widgets(self):
for child in [child for child in self.treeview.children]:
self.treeview.remove_widget(child)
*.kv
<TreeviewGroup>:
treeview: treeview
title: "Select"
title_size: 17
size: 800, 800
auto_dismiss: False
scroll: scroll
BoxLayout
orientation: "vertical"
ScrollView:
id: scroll
size_hint: 1, .9
BoxLayout:
size_hint_y: None
id: treeview
GridLayout:
cols : 2
row_default_height: '20dp'
size_hint: .5, 0.2
pos_hint: {'x': .25, 'y': 1}
Button:
text: 'Ok'
on_release: root.dismiss()
Button:
text: 'Cancel'
on_release: root.dismiss()
Recommendation, use appropriate names, for example the name of the BoxLayout seems to indicate the name of the TreeView, and this causes confusion making its code less readable.