Python 2.7 Python/Kivy:Kivy中树视图上的'up'和'down'键盘接口
我想在Python 2.7 Python/Kivy:Kivy中树视图上的'up'和'down'键盘接口,python-2.7,kivy,kivy-language,Python 2.7,Kivy,Kivy Language,我想在树状视图上使用向上和向下键盘界面。当我点击名称时,树状视图弹出显示和向上,down键工作正常。但是我在TextInput中键入了一些内容,用于筛选行使用up和down键选择行后,它会给出错误AttributeError:“NoneType”对象没有属性“parent\u node”。如何row使用up和向下键 test.py 试验电压(千伏) #:kivy 1.10.0 : 所选颜色:[1,0,0,1],如果选择了self.is,则为[1,0,1],否则为[1,1,1,1]#红色 按下按钮
树状视图
上使用向上
和向下
键盘界面。当我点击名称
时,树状视图
弹出显示和向上
,down
键工作正常。但是我在TextInput
中键入了一些内容,用于筛选行
使用up
和down
键选择行后,它会给出错误AttributeError:“NoneType”对象没有属性“parent\u node”
。如何row
使用up
和向下
键
test.py
试验电压(千伏)
#:kivy 1.10.0
:
所选颜色:[1,0,0,1],如果选择了self.is,则为[1,0,1],否则为[1,1,1,1]#红色
按下按钮时:
app.root.name.text=self.text
app.root.popup.discouse\u callback()
:
树视图:树视图
标题:“选择”
标题尺寸:17
尺码:800800
自动解除:错误
滚动:滚动
ti:ti
盒子布局
方向:“垂直”
文本输入:
id:ti
text:root.filter\u text
尺寸提示:13
多行:False
on_text:root.filter(self.text)
滚动视图:
id:滚动
尺寸提示:1.9
盒子布局:
尺寸提示:无
id:树视图
网格布局:
科尔斯:2
行\默认\高度:“20dp”
大小提示:.5,0.1
位置提示:{'x':.25'y':1}
按钮:
文字:“Ok”
发布时:
root.discouse_callback()
按钮:
文本:“取消”
发布时:
root.discouse_callback()
:
文本大小:self.size
valign:“中间”
填充x:5
:
多行:False
:
背景颜色:1,1,1,1
尺寸提示:无
高度:self.parent.height*0.150
组屏幕:
姓名:姓名
网格布局:
科尔斯:2
填充:30,30
间距:10,10
行\默认\高度:“40dp”
自定义标签:
文本:“”
自定义标签:
文本:“”
自定义标签:
文本:“名称”
SingleLineTextInput:
id:姓名
焦点:正确
多行:False
on_文本:根目录。显示_组(自身)
绿色按钮:
文字:“Ok”
绿色按钮:
文本:“取消”
按:app.stop()
问题是您的键盘正在关闭,因此当您单击文本输入时,键盘事件将被忽略。当您在Select
TextInput
中点击Enter
时,您可以安排再次请求键盘
,方法是将其限制为单行,并在\u text\u validate:
项上添加一个,如您的kv文件中所示:
TextInput:
id : ti
size_hint_y: .13
multiline: False
on_text_validate: root.validate()
on_text: root.filter(self.text)
在TreeViewGroup
类中,添加validate方法,如下所示:
def validate(self):
self.on_open()
这将调用打开时的方法并再次设置键盘事件。问题是键盘正在关闭,因此当您单击文本输入时,键盘事件将被忽略。当您在Select
TextInput
中点击Enter
时,您可以安排再次请求键盘
,方法是将其限制为单行,并在\u text\u validate:
项上添加一个,如您的kv文件中所示:
TextInput:
id : ti
size_hint_y: .13
multiline: False
on_text_validate: root.validate()
on_text: root.filter(self.text)
在TreeViewGroup
类中,添加validate方法,如下所示:
def validate(self):
self.on_open()
这将调用打开时的方法,并再次设置键盘事件。选择一行-按ENTER键或鼠标单击
使用向上或向下箭头滚动,然后按ENTER键选择特定行。注意:如果要选择其他行,请单击鼠标选择特定行
将输入字符串从名称传递到筛选器
要通过整个单词,例如测试,请将on_focus替换为on_text_validate
干-不要重复你自己
有重复的代码,我已经把它们变成模块,便于维护
def create_treeview_root(self):
...
def create_treeview_branch(self, obj):
...
def dismiss_callback(self):
一小条
打开时弹出事件
删除了uuu init_uuuuu中的绑定,因为打开的是自动绑定的,并且在弹出窗口打开时触发。
例子
main.py
试验电压(千伏)
#:kivy 1.10.0
:
尺寸提示:13
多行:False
:
所选颜色:[1,0,0,1],如果选择了self.is,则为[1,0,1],否则为[1,1,1,1]#红色
按下按钮时:
app.root.name.text=self.text
app.root.popup.discouse\u callback()
:
树视图:树视图
过滤文本输入:过滤文本输入
标题:“选择”
标题尺寸:17
尺码:800800
自动解除:错误
滚动:滚动
盒子布局
方向:“垂直”
自定义文本输入:
id:过滤器\文本\输入
text:root.filter\u text
关于文本:
root.filter\u text=self.text
root.filter(self.text)
滚动视图:
id:滚动
尺寸提示:1.9
盒子布局:
尺寸提示:无
id:树视图
网格布局:
科尔斯:2
行\默认\高度:“20dp”
大小提示:.5,0.1
位置提示:{'x':.25'y':1}
按钮:
文字:“Ok”
发布时:
root.discouse_callback()
按钮:
文本:“取消”
发布时:
root.discouse_callback()
:
文本大小:self.size
valign:“中间”
填充x:5
:
多行:False
:
背景颜色:1,1,1,1
尺寸提示:无
高度:self.parent.height*0.150
组屏幕:
姓名:姓名
网格布局:
科尔斯:2
填充:30,30
间距:10,10
行\默认\高度:“40dp”
自定义标签:
文本:“”
自定义标签:
文本:“”
自定义标签:
文本:“名称”
SingleLineTextInput:
id:姓名
GroupScreen:
...
SingleLineTextInput:
id: name
focus: True
multiline: False
on_text_validate: root.display_groups(self)
class TreeviewGroup(Popup):
...
def __init__(self,obj, **kwargs):
super(TreeviewGroup, self).__init__(**kwargs)
...
self.bind(on_open=self.on_open)
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 = ['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.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()
#: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()