python-2.7 - Python/Kivy : Selection Row in RecycleView using `Up` and `Down` key

标签 python-2.7 kivy kivy-language

我正在使用 python-2.7 和 kivy。当我运行 test.py 并单击 Test 菜单时,屏幕显示如附加图像。
1. 如何在屏幕加载时默认突出显示第一行?当按下键盘的键时,应该根据键选择行。
2. 当我单击任何行时,它会在 modify 情况下打开。如何使用 ctrl+e 而不是在 modify 情况下打开选定的行点击?

测试.py

import kivy

kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty,NumericProperty
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView

from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
from kivy.core.window import Window
Window.size = (600, 325)
from kivy.clock import Clock


class EditStatePopup(Popup):
    col_data = ListProperty(["?", "?"])
    index = NumericProperty(0)

    def __init__(self, obj, **kwargs):
        super(EditStatePopup, self).__init__(**kwargs)
        self.index = obj.index
        self.col_data[0] = obj.rv_data[self.index]["Id"]
        self.col_data[1] = obj.rv_data[self.index]["Name"]



class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
                                  RecycleGridLayout):
    ''' Adds selection and focus behaviour to the view. '''


class SelectableButton(RecycleDataViewBehavior, Button):
    ''' Add selection support to the Button '''
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)
    rv_data = ObjectProperty(None)
    start_point = NumericProperty(0)

    def __init__(self, **kwargs):
        super(SelectableButton, self).__init__(**kwargs)
        Clock.schedule_interval(self.update, .0005)



    def update(self, *args):
        self.text = self.rv_data[self.index][self.key]

    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        return super(SelectableButton, self).refresh_view_attrs(rv, index, data)

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SelectableButton, self).on_touch_down(touch):
            return True
        if self.collide_point(*touch.pos) and self.selectable:
            return self.parent.select_with_touch(self.index, touch)

    def apply_selection(self, rv, index, is_selected):
        self.selected = is_selected
        self.rv_data = rv.data



    def on_press(self):
        popup = EditStatePopup(self)
        popup.open()

class MyRV(RecycleView):
    def __init__(self, **kwargs):
        super(MyRV, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down)
        self.selectedItem = -1

    def _keyboard_closed(self):
        pass

    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'down':
            self.clearAll()
            self.nextItem()
            print('down')
        elif keycode[1] == 'up':
            self.clearAll()
            self.prevItem()
            print("up")
        elif keycode[1] == 'e' and len(modifiers) > 0 and modifiers[0] == 'ctrl':
            self.view_adapter.views[self.selectedItem].on_press()

    def clearAll(self):
        if (self.selectedItem > -1):
            for i in range(len(self.view_adapter.views) - 1):
                self.view_adapter.views[self.selectedItem].selected = 0


    def nextItem(self):
        if self.selectedItem < len(self.view_adapter.views) - 1:
            self.selectedItem += 1
        else:
            self.selectedItem = 0
        self.view_adapter.views[self.selectedItem].selected = 1
        print(self.selectedItem)


    def prevItem(self):
        if self.selectedItem > 0:
            self.selectedItem -= 1
        else:
            self.selectedItem = len(self.view_adapter.views) - 1
        self.view_adapter.views[self.selectedItem].selected = 1
        print(self.selectedItem)



class RV(RecycleView):
    data_items = ListProperty([])
    col1 = ListProperty()
    col2 = ListProperty()

    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.get_states()

    def update(self):
        self.col1 = [{'Id': str(x[0]), 'Name': x[1], 'key': 'Id', 'text': str(x[2])} for x in self.data_items]
        self.col2 = [{'Id': str(x[0]), 'Name': x[1], 'key': 'Name', 'text': str(x[2])} for x in self.data_items]


    def get_states(self):
        rows = [(1, 'Test1'), (2, 'Test2'), (3, 'Test3')]

        i = 0
        for row in rows:
            self.data_items.append([row[0], row[1], i])
            i += 1
        print(self.data_items)
        self.update()



class MainMenu(BoxLayout):
    states_cities_or_areas = ObjectProperty()

    def display_states(self):
        self.remove_widgets()
        self.rv = RV()
        self.states_cities_or_areas.add_widget(self.rv)

    def remove_widgets(self):
        self.states_cities_or_areas.clear_widgets()

class TestApp(App):
    title = "test"

    def build(self):
        return MainMenu()



if __name__ == '__main__':
    TestApp().run()

测试.kv

<SelectableButton>:
    canvas.before:
        Color:
            rgba: (10, 10, 10, 10) if self.selected else (0, 0.517, 0.705, 1)
        Rectangle:
            pos: self.pos
            size: self.size


<MyRV@RecycleView>:
    viewclass: 'SelectableButton'
    SelectableRecycleGridLayout:
        cols: 1
        default_size: None, dp(26)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
        multiselect: True
        touch_multiselect: True

<RV>:
    BoxLayout:
        orientation: "vertical"
        viewclass: 'SelectableButton'
        GridLayout:
            size_hint: 1, None
            size_hint_y: None
            height: 25
            cols: 3

            Label:
                size_hint_x: .1
                text: "Id"
            Label:
                size_hint_x: .5
                text: "Name"

        BoxLayout:
            MyRV:
                size_hint_x: .1
                data: root.col1
            MyRV:
                size_hint_x: .5
                data: root.col2



<EditStatePopup>:
    size_hint: None, None
    title_size: 20
    title_font: "Verdana"
    size: 400, 275
    auto_dismiss: False

    BoxLayout:
        orientation: "vertical"
        GridLayout:
            cols: 2
            #backgroun_color: 0, 0.517, 0.705, 1
            spacing: 10, 10
            padding: 20, 20
            Label:
                text: "Id"
                text_size: self.size
            Label:
                text: root.col_data[0]
                text_size: self.size
            Label:
                text: "Name"
                text_size: self.size
                valign: 'middle'
            TextInput:
                focus : True
                text: root.col_data[1]
                text_size: self.size

        GridLayout:
            cols: 2
            padding: 10, 0, 10, 10
            spacing: 10, 10
            row_default_height: '20dp'
            size_hint: .5, .2
            pos_hint: {'x': .25, 'y':.65}

            Button:
                text: 'Ok'


            Button:
                text: 'Cancel'
                size_hint_x: .5
                on_release: root.dismiss()




<MenuButton@Button>:
    text_size: self.size
    valign: "middle"
    padding_x: 5
    size : (80,30)
    size_hint : (None, None)
    background_color: 90 , 90, 90, 90
    background_normal: ''
    color: 0, 0.517, 0.705, 1
    border: (0, 10, 0, 0)


<MainMenu>:
    states_cities_or_areas: states_cities_or_areas


    BoxLayout:
        orientation: 'vertical'
        #spacing : 10

        BoxLayout:
            canvas.before:
                Rectangle:
                    pos: self.pos
                    size: self.size

            size_hint_y: 1

            MenuButton:
                id: btn
                text: 'Test'
                size : (60,30)
                on_release: root.display_states()


        BoxLayout:
            canvas.before:
                Rectangle:
                    pos: self.pos
                    size: self.size

                Color:
                    rgb: (1,1,1)

            Label:
                size_hint_x: 45

        BoxLayout:
            id: states_cities_or_areas
            size_hint_y: 10

        Label:
            size_hint_y: 1

最佳答案

  1. 在 SelectableButton 上使用 2 种不同的颜色
  2. 检查按键并触发 on_press

文本.kv

<SelectableButton>:
    canvas.before:
        Color:
            rgba: (0.5, 0.5, 0.5, 1) if self.selected else (0, 0.517, 0.705, 1)
        Rectangle:
            pos: self.pos
            size: self.size


<MyRV@RecycleView>:
    viewclass: 'SelectableButton'
    SelectableRecycleGridLayout:
        cols: 1
        default_size: None, dp(26)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
        multiselect: True
        touch_multiselect: True

<RV>:
    BoxLayout:
        orientation: "vertical"
        viewclass: 'SelectableButton'
        GridLayout:
            size_hint: 1, None
            size_hint_y: None
            height: 25
            cols: 3

            Label:
                size_hint_x: .1
                text: "Id"
            Label:
                size_hint_x: .5
                text: "Name"

        BoxLayout:
            MyRV:
                size_hint_x: .1
                data: root.col1
            MyRV:
                size_hint_x: .5
                data: root.col2


<EditStatePopup>:
    size_hint: None, None
    title_size: 20
    title_font: "Verdana"
    size: 400, 275
    auto_dismiss: False

    BoxLayout:
        orientation: "vertical"
        GridLayout:
            cols: 2
            #backgroun_color: 0, 0.517, 0.705, 1
            spacing: 10, 10
            padding: 20, 20
            Label:
                text: "Id"
                text_size: self.size
            Label:
                text: root.col_data[0]
                text_size: self.size
            Label:
                text: "Name"
                text_size: self.size
                valign: 'middle'
            TextInput:
                text: root.col_data[1]
                text_size: self.size



<MenuButton@Button>:
    text_size: self.size
    valign: "middle"
    padding_x: 5
    size : (80,30)
    size_hint : (None, None)
    background_color: 90 , 90, 90, 90
    background_normal: ''
    color: 0, 0.517, 0.705, 1
    border: (0, 10, 0, 0)


<MainMenu>:
    states_cities_or_areas: states_cities_or_areas


    BoxLayout:
        orientation: 'vertical'
        #spacing : 10

        BoxLayout:
            canvas.before:
                Rectangle:
                    pos: self.pos
                    size: self.size

            size_hint_y: 1

            MenuButton:
                id: btn
                text: 'Test'
                size : (60,30)
                on_release: root.display_states()


        BoxLayout:
            canvas.before:
                Rectangle:
                    pos: self.pos
                    size: self.size

                Color:
                    rgb: (1,1,1)

            Label:
                size_hint_x: 45

        BoxLayout:
            id: states_cities_or_areas
            size_hint_y: 10

        Label:
            size_hint_y: 1

测试应用程序.py

import kivy

kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty,NumericProperty
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView

from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
from kivy.core.window import Window
Window.size = (600, 325)
from kivy.clock import Clock


class EditStatePopup(Popup):
    col_data = ListProperty(["?", "?"])
    index = NumericProperty(0)

    def __init__(self, obj, **kwargs):
        super(EditStatePopup, self).__init__(**kwargs)
        self.index = obj.index
        self.col_data[0] = obj.rv_data[self.index]["Id"]
        self.col_data[1] = obj.rv_data[self.index]["Name"]



class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
                                  RecycleGridLayout):
    ''' Adds selection and focus behaviour to the view. '''


class SelectableButton(RecycleDataViewBehavior, Button):
    ''' Add selection support to the Button '''
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)
    rv_data = ObjectProperty(None)
    start_point = NumericProperty(0)

    def __init__(self, **kwargs):
        super(SelectableButton, self).__init__(**kwargs)
        Clock.schedule_interval(self.update, .0005)



    def update(self, *args):
        self.text = self.rv_data[self.index][self.key]

    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        return super(SelectableButton, self).refresh_view_attrs(rv, index, data)

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SelectableButton, self).on_touch_down(touch):
            return True
        if self.collide_point(*touch.pos) and self.selectable:
            return self.parent.select_with_touch(self.index, touch)

    def apply_selection(self, rv, index, is_selected):
        self.selected = is_selected
        self.rv_data = rv.data



    def on_press(self):
        popup = EditStatePopup(self)
        popup.open()

class MyRV(RecycleView):
    def __init__(self, **kwargs):
        super(MyRV, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down)
        self.selectedItem = -1

    def _keyboard_closed(self):
        pass

    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'down':
            self.clearAll()
            self.nextItem()
            print('down')
        elif keycode[1] == 'up':
            self.clearAll()
            self.prevItem()
            print("up")
        elif keycode[1] == 'e' and len(modifiers) > 0 and modifiers[0] == 'ctrl':
            self.view_adapter.views[self.selectedItem].on_press()

    def clearAll(self):
        if (self.selectedItem > -1):
            for i in range(len(self.view_adapter.views) - 1):
                self.view_adapter.views[self.selectedItem].selected = 0


    def nextItem(self):
        if self.selectedItem < len(self.view_adapter.views) - 1:
            self.selectedItem += 1
        else:
            self.selectedItem = 0
        self.view_adapter.views[self.selectedItem].selected = 1
        print(self.selectedItem)


    def prevItem(self):
        if self.selectedItem > 0:
            self.selectedItem -= 1
        else:
            self.selectedItem = len(self.view_adapter.views) - 1
        self.view_adapter.views[self.selectedItem].selected = 1
        print(self.selectedItem)



class RV(RecycleView):
    data_items = ListProperty([])
    col1 = ListProperty()
    col2 = ListProperty()

    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.get_states()

    def update(self):
        self.col1 = [{'Id': str(x[0]), 'Name': x[1], 'key': 'Id', 'text': str(x[2])} for x in self.data_items]
        self.col2 = [{'Id': str(x[0]), 'Name': x[1], 'key': 'Name', 'text': str(x[2])} for x in self.data_items]


    def get_states(self):
        rows = [(1, 'Test1'), (2, 'Test2'), (3, 'Test3')]

        i = 0
        for row in rows:
            self.data_items.append([row[0], row[1], i])
            i += 1
        print(self.data_items)
        self.update()



class MainMenu(BoxLayout):
    states_cities_or_areas = ObjectProperty()

    def display_states(self):
        self.remove_widgets()
        self.rv = RV()
        self.states_cities_or_areas.add_widget(self.rv)

    def remove_widgets(self):
        self.states_cities_or_areas.clear_widgets()

class TestApp(App):
    title = "test"

    def build(self):
        return MainMenu()



if __name__ == '__main__':
    TestApp().run()

关于python-2.7 - Python/Kivy : Selection Row in RecycleView using `Up` and `Down` key,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49732096/

相关文章:

python - 在 Python 中将元组传递给 __init__

python - 无法在 Ubuntu 14.04 上升级 pip 1.5.4 - InsecurePlatformWarning : A true SSLContext object is not available

android - Haxe 和 Kivy 的优缺点

python - Kivy 小部件隐藏在 Windows 标题栏下

python - 如何在 kivy 中将 pos_hint 与 FloatLayout 一起使用?

python-2.7 - 属性错误 : 'module' object has no attribute 'merge_all_summaries'

python - 将 Python DateTime 字符串转换为整数毫秒

Python Kivy 在导入时打印数字

android - KIVY:如何清除其所有子项的网格布局

python - Kivy Action按钮无法通过python更新图标