title: Python/Kivy : `up` and `down` Keyboard interface on Tree View in kivy
body: <p>I want to use <code>up</code> and <code>down</code> Keyboard interface on <code>Tree View</code>.When i click on <code>name</code> then <code>Tree View</code> popup shows And <code>up</code>,<code>down</code> key working good.But i type something into <code>TextInput</code> for filter <code>row</code> after use <code>up</code> and <code>down</code> key for select row then it gives error <code>AttributeError: 'NoneType' object has no attribute 'parent_node'</code> .How to  <code>row</code> select using <code>up</code> and <code>down</code> keys?</p>
  
  <h1>test.py</h1>
  
  <pre><code>from kivy.uix.screenmanager import Screen
  from kivy.app import App
  from kivy.lang import Builder
  from kivy.core.window import Window
  from kivy.uix.popup import Popup
  from kivy.uix.treeview import TreeView, TreeViewLabel, TreeViewNode
  from kivy.uix.label import Label
  from kivy.properties import ObjectProperty, ListProperty, StringProperty
  Window.size = (500, 400)
  
  
  def populate_tree_view(tree_view, parent, node):
      if parent is None:
          tree_node = tree_view.add_node(TreeViewLabel(text=node['node_id'],
                                                       is_open=True))
      else:
          tree_node = tree_view.add_node(TreeViewLabel(text=node['node_id'],
                                                       is_open=True), parent)
  
      for child_node in node['children']:
          populate_tree_view(tree_view, tree_node, child_node)
  
  
  class TreeViewLabel(Label, TreeViewNode):
      pass
  
  
  class TreeViewGroup(Popup):
      tree_view = ObjectProperty(None)
      tv = ObjectProperty(None)
      filter_text = StringProperty('')
      tree = ListProperty([])
  
      def __init__(self, **kwargs):
          super(TreeViewGroup, self).__init__(**kwargs)
          self.create_tree_view_root()
          rows = ['test{}'.format(i) for i in range(1, 20)]
          self.tree = [{'node_id': r, 'children': []} for r in rows]
          self.tv.bind(minimum_height=self.tree_view.setter('height'))
          self.create_tree_view_branch(self.tree)
  
      def create_tree_view_root(self):
          self.tv = TreeView(root_options=dict(text=""),
                             hide_root=False,
                             indent_level=4)
  
      def create_tree_view_branch(self, obj):
          for branch in obj:
              populate_tree_view(self.tv, None, branch)
          self.tree_view.add_widget(self.tv)
  
      def on_open(self, *args):
          self.filter_text = App.get_running_app().root.name.text
          self._request_keyboard()
          self.ti.focus = True
  
      def dismiss_callback(self):
          if self._keyboard is not None:
              self._keyboard.release()
          self.tree_view.clear_widgets()
          self.dismiss()
          App.get_running_app().root.name.focus = True
  
      def _request_keyboard(self):
          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.release()
          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)
          elif key in ('enter', 'numpadenter'):
              App.get_running_app().root.name.text = node.text
              print(node.text)
              self.dismiss_callback()
  
          # Keycode is composed of an integer + a string
          # If we hit escape, release the keyboard
          if keycode[1] == 'escape':
              keyboard.release()
  
          # Return True to accept the key. Otherwise, it will be used by
          # the system.
          return True
  
      def filter(self, value):
          self.tree_view.clear_widgets()
          self.create_tree_view_root()
          filtered_tree = []
          for node in self.tree:
              if value.lower() in node['node_id'].lower():
                  filtered_tree.append(node)
          self.create_tree_view_branch(filtered_tree)
          self._request_keyboard()
          self.ti.focus = True
  
  
  class GroupScreen(Screen):
      name = ObjectProperty(None)
      popup = ObjectProperty(None)
  
      def display_groups(self, instance):
          if len(instance.text) &gt; 0:
              if self.popup is None:
                  self.popup = TreeViewGroup()
              self.popup.open()
  
  
  class Group(App):
      def build(self):
          self.root = Builder.load_file('test.kv')
          return self.root
  
  
  if __name__ == '__main__':
      Group().run()
  </code></pre>
  
  <h1>test.kv</h1>
  
  <pre><code>#:kivy 1.10.0
  
  &lt;TreeViewLabel&gt;:
      color_selected: [1, 0, 0, 1]  if self.is_selected else [.1, .1, .1, 1]  # red
      on_touch_down:
          app.root.name.text = self.text
          app.root.popup.dismiss_callback()
  
  &lt;TreeviewGroup&gt;:
      tree_view: tree_view
      title: "Select"
      title_size: 17
      size: 800, 800
      auto_dismiss: False
      scroll: scroll
      ti:ti
  
      BoxLayout
          orientation: "vertical"
          TextInput:
              id : ti
              text: root.filter_text
              size_hint_y: .13
              multiline: False
              on_text: root.filter(self.text)
  
          ScrollView:
              id: scroll
              size_hint: 1, .9
  
              BoxLayout:
                  size_hint_y: None
                  id: tree_view
  
          GridLayout:
              cols: 2
              row_default_height: '20dp'
              size_hint: .5, 0.1
              pos_hint: {'x': .25, 'y': 1}
              Button:
                  text: 'Ok'
                  on_release:
                      root.dismiss_callback()
  
              Button:
                  text: 'Cancel'
                  on_release:
                      root.dismiss_callback()
  
  &lt;CustomLabel@Label&gt;:
      text_size: self.size
      valign: "middle"
      padding_x: 5
  
  &lt;SingleLineTextInput@TextInput&gt;:
      multiline: False
  
  &lt;GreenButton@Button&gt;:
      background_color: 1, 1, 1, 1
      size_hint_y: None
      height: self.parent.height * 0.150
  
  GroupScreen:
      name: name
  
      GridLayout:
          cols: 2
          padding : 30,30
          spacing: 10, 10
          row_default_height: '40dp'
  
          CustomLabel:
              text: ' '
  
          CustomLabel:
              text: ' '
  
          CustomLabel:
              text: 'Name'
  
          SingleLineTextInput:
              id: name
              focus: True
              multiline: False
              on_text: root.display_groups(self)
  
          GreenButton:
              text: 'Ok'
  
          GreenButton:
              text: 'Cancel'
              on_press: app.stop()
  </code></pre>