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()