Python/Kivy : 如何在 TreeView 中搜索 child 节点
Python/Kivy : How to search child node in TreeView
我正在使用 python-2.7
和 kivy-1.9.0
。当我 运行 test.py
然后显示 name
TextInput 。当我输入 name
然后 TreeView
open.I 为 Filter
文本添加了 TextInput
。但它只适用于 Parent
node.When 我输入 R
然后它不显示 Reserves & Surplus
在 Results.It 不适用于 child node
.
1.Can 有人告诉我如何在 child 节点中搜索?
test.py
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.uix.textinput import TextInput
from kivy.properties import ObjectProperty, ListProperty, StringProperty, NumericProperty
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 CustomTextInput(TextInput):
def do_cursor_movement(self, action, control=False, alt=False):
if not self._lines:
return
if action in ('cursor_up', 'cursor_down'):
App.get_running_app().root.popup._request_keyboard()
return super(CustomTextInput, self).do_cursor_movement(action, control=control, alt=alt)
class TreeViewLabel(Label, TreeViewNode):
pass
class TreeViewGroup(Popup):
tree_view = ObjectProperty(None)
filter_text_input = ObjectProperty(None)
tv = ObjectProperty(None)
filter_text = StringProperty('')
tree = ListProperty([])
obj = ObjectProperty(None)
keycodes = {
# specials keys
'backspace': 8, 'tab': 9, 'enter': 13, 'rshift': 303, 'shift': 304,
'alt': 308, 'rctrl': 306, 'lctrl': 305,
'super': 309, 'alt-gr': 307, 'compose': 311, 'pipe': 310,
'capslock': 301, 'escape': 27, 'spacebar': 32, 'pageup': 280,
'pagedown': 281, 'end': 279, 'home': 278, 'left': 276, 'up':
273, 'right': 275, 'down': 274, 'insert': 277, 'delete': 127,
'numlock': 300, 'print': 144, 'screenlock': 145, 'pause': 19,
# F1-15
'f1': 282, 'f2': 283, 'f3': 284, 'f4': 285, 'f5': 286, 'f6': 287,
'f7': 288, 'f8': 289, 'f9': 290, 'f10': 291, 'f11': 292, 'f12': 293,
'f13': 294, 'f14': 295, 'f15': 296,
}
def __init__(self, **kwargs):
super(TreeViewGroup, self).__init__(**kwargs)
self._keyboard = None
self.create_tree_view_root()
rows = [{'node_id': 'Capital Account',
'children': [{'node_id': 'Reserves & Surplus',
'children': [{'node_id': '1.1.1',
'children': [{'node_id': '1.1.1.1',
'children': []}]},
{'node_id': '1.1.2',
'children': []},
{'node_id': '1.1.3',
'children': []}]},
{'node_id': '1.2',
'children': []}]},
{'node_id': 'Current Assests',
'children': []}]
self.tree = [{'node_id': r['node_id'], 'children': 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.obj = self.filter_text_input
self.filter_text_input.focus = True
self.filter_text = App.get_running_app().root.name.text
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) \
and (len(self.tv.root.nodes) > 0):
self.tv.select_node(self.tv.root.nodes[0])
else:
self.filter_text_input.focus = True
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()
if self.string_to_keycode(key) == -1:
self.filter_text += key
self.obj.focus = True
# Return True to accept the key. Otherwise, it will be used by
# the system.
return True
def string_to_keycode(self, value):
'''Convert a string to a keycode number according to the
:attr:`TreeViewGroup.keycodes`. If the value is not found in the
keycodes, it will return -1.
'''
return TreeViewGroup.keycodes.get(value, -1)
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)
class GroupScreen(Screen):
name = ObjectProperty(None)
popup = ObjectProperty(None)
def display_groups(self, instance):
if len(instance.text) > 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()
test.kv
#:kivy 1.10.0
<CustomTextInput>:
size_hint_y: .13
multiline: False
<TreeViewLabel>:
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()
<TreeviewGroup>:
tree_view: tree_view
filter_text_input: filter_text_input
title: "Select"
title_size: 17
size: 800, 800
auto_dismiss: False
scroll: scroll
BoxLayout
orientation: "vertical"
CustomTextInput:
id: filter_text_input
text: root.filter_text
on_text:
root.filter_text = self.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()
<CustomLabel@Label>:
text_size: self.size
valign: "middle"
padding_x: 5
<SingleLineTextInput@TextInput>:
multiline: False
<GreenButton@Button>:
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()
我从未使用过 kivy,但节点搜索是在 filter
方法中完成的,它不会搜索子节点。因此,将其替换为以下内容:
def filter(self, value):
self.tree_view.clear_widgets()
self.create_tree_view_root()
filtered_tree = []
v = value.lower() # the lowercase value
def filter_children(parent): # a recursive inner function
ch = parent['children']
if not ch:
return
for node in ch:
if v in node['node_id'].lower():
filtered_tree.append(node)
filter_children(node)
for node in self.tree:
if v in node['node_id'].lower():
filtered_tree.append(node)
filter_children(node) # look into children
self.create_tree_view_branch(filtered_tree)
如果粘贴此代码,请注意添加额外的缩进 4。
我正在使用 python-2.7
和 kivy-1.9.0
。当我 运行 test.py
然后显示 name
TextInput 。当我输入 name
然后 TreeView
open.I 为 Filter
文本添加了 TextInput
。但它只适用于 Parent
node.When 我输入 R
然后它不显示 Reserves & Surplus
在 Results.It 不适用于 child node
.
1.Can 有人告诉我如何在 child 节点中搜索?
test.py
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.uix.textinput import TextInput
from kivy.properties import ObjectProperty, ListProperty, StringProperty, NumericProperty
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 CustomTextInput(TextInput):
def do_cursor_movement(self, action, control=False, alt=False):
if not self._lines:
return
if action in ('cursor_up', 'cursor_down'):
App.get_running_app().root.popup._request_keyboard()
return super(CustomTextInput, self).do_cursor_movement(action, control=control, alt=alt)
class TreeViewLabel(Label, TreeViewNode):
pass
class TreeViewGroup(Popup):
tree_view = ObjectProperty(None)
filter_text_input = ObjectProperty(None)
tv = ObjectProperty(None)
filter_text = StringProperty('')
tree = ListProperty([])
obj = ObjectProperty(None)
keycodes = {
# specials keys
'backspace': 8, 'tab': 9, 'enter': 13, 'rshift': 303, 'shift': 304,
'alt': 308, 'rctrl': 306, 'lctrl': 305,
'super': 309, 'alt-gr': 307, 'compose': 311, 'pipe': 310,
'capslock': 301, 'escape': 27, 'spacebar': 32, 'pageup': 280,
'pagedown': 281, 'end': 279, 'home': 278, 'left': 276, 'up':
273, 'right': 275, 'down': 274, 'insert': 277, 'delete': 127,
'numlock': 300, 'print': 144, 'screenlock': 145, 'pause': 19,
# F1-15
'f1': 282, 'f2': 283, 'f3': 284, 'f4': 285, 'f5': 286, 'f6': 287,
'f7': 288, 'f8': 289, 'f9': 290, 'f10': 291, 'f11': 292, 'f12': 293,
'f13': 294, 'f14': 295, 'f15': 296,
}
def __init__(self, **kwargs):
super(TreeViewGroup, self).__init__(**kwargs)
self._keyboard = None
self.create_tree_view_root()
rows = [{'node_id': 'Capital Account',
'children': [{'node_id': 'Reserves & Surplus',
'children': [{'node_id': '1.1.1',
'children': [{'node_id': '1.1.1.1',
'children': []}]},
{'node_id': '1.1.2',
'children': []},
{'node_id': '1.1.3',
'children': []}]},
{'node_id': '1.2',
'children': []}]},
{'node_id': 'Current Assests',
'children': []}]
self.tree = [{'node_id': r['node_id'], 'children': 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.obj = self.filter_text_input
self.filter_text_input.focus = True
self.filter_text = App.get_running_app().root.name.text
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) \
and (len(self.tv.root.nodes) > 0):
self.tv.select_node(self.tv.root.nodes[0])
else:
self.filter_text_input.focus = True
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()
if self.string_to_keycode(key) == -1:
self.filter_text += key
self.obj.focus = True
# Return True to accept the key. Otherwise, it will be used by
# the system.
return True
def string_to_keycode(self, value):
'''Convert a string to a keycode number according to the
:attr:`TreeViewGroup.keycodes`. If the value is not found in the
keycodes, it will return -1.
'''
return TreeViewGroup.keycodes.get(value, -1)
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)
class GroupScreen(Screen):
name = ObjectProperty(None)
popup = ObjectProperty(None)
def display_groups(self, instance):
if len(instance.text) > 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()
test.kv
#:kivy 1.10.0
<CustomTextInput>:
size_hint_y: .13
multiline: False
<TreeViewLabel>:
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()
<TreeviewGroup>:
tree_view: tree_view
filter_text_input: filter_text_input
title: "Select"
title_size: 17
size: 800, 800
auto_dismiss: False
scroll: scroll
BoxLayout
orientation: "vertical"
CustomTextInput:
id: filter_text_input
text: root.filter_text
on_text:
root.filter_text = self.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()
<CustomLabel@Label>:
text_size: self.size
valign: "middle"
padding_x: 5
<SingleLineTextInput@TextInput>:
multiline: False
<GreenButton@Button>:
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()
我从未使用过 kivy,但节点搜索是在 filter
方法中完成的,它不会搜索子节点。因此,将其替换为以下内容:
def filter(self, value):
self.tree_view.clear_widgets()
self.create_tree_view_root()
filtered_tree = []
v = value.lower() # the lowercase value
def filter_children(parent): # a recursive inner function
ch = parent['children']
if not ch:
return
for node in ch:
if v in node['node_id'].lower():
filtered_tree.append(node)
filter_children(node)
for node in self.tree:
if v in node['node_id'].lower():
filtered_tree.append(node)
filter_children(node) # look into children
self.create_tree_view_branch(filtered_tree)
如果粘贴此代码,请注意添加额外的缩进 4。