首页 > 解决方案 > Kivy 中的依赖循环视图

问题描述

我的问题:我想要两个 RecycleView,第二个(CultureSubView)取决于第一个(CultureView)中单击的内容。
每当单击 CultureView 中的某些内容时,它可以调用 CultureSubView 的“更新”功能,但我不知道该怎么做。

因为这必须在 SelectableLabel (这可能需要一个额外的 SelectableLabel 因为 CultureView 不需要额外的功能)
或在 kivy 语言的 CultureView 中完成。

我还尝试找到任何可以在 CultureView 中触发“on_press”的东西,然后更新 CultureSubView,但我也没有找到任何选项。这个想法在代码中被注释掉了。

from kivy.app import App
from kivy.properties import StringProperty, BooleanProperty
from kivy.uix.behaviors import FocusBehavior
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView


Builder.load_string('''
<ScreenCulture>:
    BoxLayout:
        orientation: 'vertical'

        Label:
            pos_hint: {"x": .45, "top": 1}
            text: "Testosterone"
            size_hint: .1, .1

        GridLayout:
            id: culture_layout
            rows: 1
            cols: 3
            padding: dp(10)
            spacing: dp(10)
            orientation: 'horizontal'

            CultureView:
                id: culture_list_view
                # on_press? culture_sub_view.update()

            CultureSubView:
                id: culture_sub_view
                name_selected: 
                    "Planet" if not culture_list_view.name_selected else culture_list_view.name_selected

            CultureLabel:
                id: culture_text
                name_selected: "NVM" if not culture_sub_view.name_selected else culture_sub_view.name_selected
                text_selected: "This is totally a test text" if not culture_sub_view.text_selected else culture_sub_view.text_selected


<CultureView>:
    viewclass: 'SelectableLabel'
    name_selected: ''

    SelectableRecycleBoxLayout:
        orientation: 'vertical' 
        default_size: None, dp(32)
        default_size_hint: .6, None
        size_hint: 1, .9
        multiselect: False
        touch_multiselect: False


<CultureSubView>:
    viewclass: 'SelectableLabel'
    text_selected: ''
    name_selected: ''

    SelectableRecycleBoxLayout:
        orientation: 'vertical' 
        default_size: None, dp(32)
        default_size_hint: .6, None
        size_hint: 1, .9
        multiselect: False
        touch_multiselect: False


<CultureLabel>:
    size_hint_y: .85
    Label:
        halign: 'left'
        valign: 'middle'
        size_hint_y: None
        height: self.texture_size[1]
        text_size: self.width, None
        text: root.text_selected


<SelectableLabel>:
    # Draw a background to indicate selection
    canvas.before:
        Color:
            rgba: (.05, 0.5, .9, .8) if self.selected else (.5, .5, .5, 1)
        Rectangle:
            pos: self.pos
            size: self.size
''')


class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
                                 RecycleBoxLayout):
    pass


class SelectableLabel(RecycleDataViewBehavior, Label):
    ''' Add selection support to the Label '''
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)

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

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SelectableLabel, 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):
        ''' Respond to the selection of items in the view. '''
        self.selected = is_selected
        if is_selected:
            print("selection changed to {0}".format(rv.data[index]))
            rv.name_selected = rv.data[index]['text']
        else:
            print("selection removed for {0}".format(rv.data[index]))


class CultureView(RecycleView):
    def __init__(self, **kwargs):
        super(CultureView, self).__init__(**kwargs)

        self.data = [
            {"text": "Test1", "description": "testu"},
            {"text": "Test2", "description": "testo"}
        ]


class CultureSubView(RecycleView):
    planet_selected = StringProperty('')

    def __init__(self, **kwargs):
        super(CultureSubView, self).__init__(**kwargs)
        self.selection_data = {
            '': [],
            'Test1': [
                {"text": "test1.1", "description": "test1"},
                {"text": "test1.2", "description": "text1"}
            ],
            'Test2': [
                {"text": "test2.1", "description": "text2"},
                {"text": "test2.2", "description": "test2"}
            ]
        }
        self.data = self.selection_data[self.planet_selected]

    def update(self):
        print("Updating")
        self.data = self.selection_data[self.planet_selected]
        self.refresh_from_data()


class CultureLabel(ScrollView):
    text_selected = StringProperty('')
    name_selected = StringProperty('')


class ScreenCulture(Screen):
    pass


screen_manager = ScreenManager()
screen_manager.add_widget(ScreenCulture(name="screen_culture"))

class TestApp(App):
    def build(self):
        return screen_manager


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

一如既往,感谢您的帮助:)

标签: python-3.xkivy

解决方案


由于该项目是SelectableLabel,因此使用on_touch_down事件来调用方法CultureSubView.update()

调用方法CultureSubView.update()有两种选择,一种来自 kv 文件,另一种来自 Python 脚本。两个选项都使用了 Kivy ObjectProperty。添加ObjectProperty以连接culture_sub_viewScreenManagerScreenCulture。详情请参阅示例。

选项 1 -通过 kv 文件调用CultureSubView.update()

在这个选项中,on_touch_down事件被调用了两次,因为我们有一个类 SelectableLabel,它在两个地方使用,即 CultureView 和 CultureSubView。

片段

.kv 文件

Builder.load_string('''
<ScreenManagement>:
    screen_culture: screen_culture

    ScreenCulture:
        id: screen_culture
        name: 'screen_culture'

<ScreenCulture>:
    culture_sub_view: culture_sub_view

...

<SelectableLabel>:
    # Draw a background to indicate selection
    canvas.before:
        Color:
            rgba: (.05, 0.5, .9, .8) if self.selected else (.5, .5, .5, 1)
        Rectangle:
            pos: self.pos
            size: self.size
    on_touch_down:
        print('on_touch_down:')
        if self.selectable: \
            app.root.screen_culture.culture_sub_view.update(self.text)

Python 脚本

class SelectableLabel(RecycleDataViewBehavior, Label):
    ...

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SelectableLabel, 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)
...

class ScreenCulture(Screen):
    culture_sub_view = ObjectProperty(None)


class ScreenManagement(ScreenManager):
    screen_culture = ObjectProperty(None)


class TestApp(App):
    def build(self):
        return ScreenManagement()

选项 2 -通过 Python 脚本调用CultureSubView.update()

在这个选项中,我们使用App.get_running_app().root.screen_culture.culture_sub_view.update()调用该方法并将self.text传递给它。

片段

.kv 文件

Builder.load_string('''
<ScreenManagement>:
    screen_culture: screen_culture

    ScreenCulture:
        id: screen_culture
        name: 'screen_culture'

<ScreenCulture>:
    culture_sub_view: culture_sub_view

Python 脚本

class SelectableLabel(RecycleDataViewBehavior, Label):
    ...

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SelectableLabel, self).on_touch_down(touch):
            return True
        if self.collide_point(*touch.pos) and self.selectable:
            App.get_running_app().root.screen_culture.culture_sub_view.update(self.text)
            return self.parent.select_with_touch(self.index, touch)
...

class ScreenCulture(Screen):
    culture_sub_view = ObjectProperty(None)


class ScreenManagement(ScreenManager):
    screen_culture = ObjectProperty(None)


class TestApp(App):
    def build(self):
        return ScreenManagement()

例子

选项 1 -通过 kv 文件调用CultureSubView.update()

from kivy.app import App
from kivy.properties import StringProperty, BooleanProperty, ObjectProperty
from kivy.uix.behaviors import FocusBehavior
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView

Builder.load_string('''
<ScreenManagement>:
    screen_culture: screen_culture

    ScreenCulture:
        id: screen_culture
        name: 'screen_culture'

<ScreenCulture>:
    culture_sub_view: culture_sub_view

    BoxLayout:
        orientation: 'vertical'

        Label:
            pos_hint: {"x": .45, "top": 1}
            text: "Testosterone"
            size_hint: .1, .1

        GridLayout:
            id: culture_layout
            rows: 1
            cols: 3
            padding: dp(10)
            spacing: dp(10)
            orientation: 'horizontal'

            CultureView:
                id: culture_list_view

            CultureSubView:
                id: culture_sub_view
                name_selected: 
                    "Planet" if not culture_list_view.name_selected else culture_list_view.name_selected

            CultureLabel:
                id: culture_text
                name_selected: 
                    "NVM" if not culture_sub_view.name_selected else culture_sub_view.name_selected

                text_selected: 
                    "This is totally a test text" if not culture_sub_view.text_selected else culture_sub_view.text_selected


<CultureView>:
    viewclass: 'SelectableLabel'
    name_selected: ''

    SelectableRecycleBoxLayout:
        orientation: 'vertical' 
        default_size: None, dp(32)
        default_size_hint: .6, None
        size_hint: 1, .9
        multiselect: False
        touch_multiselect: False


<CultureSubView>:
    viewclass: 'SelectableLabel'
    text_selected: ''
    name_selected: ''

    SelectableRecycleBoxLayout:
        orientation: 'vertical' 
        default_size: None, dp(32)
        default_size_hint: .6, None
        size_hint: 1, .9
        multiselect: False
        touch_multiselect: False


<CultureLabel>:
    size_hint_y: .85
    Label:
        halign: 'left'
        valign: 'middle'
        size_hint_y: None
        height: self.texture_size[1]
        text_size: self.width, None
        text: root.text_selected


<SelectableLabel>:
    # Draw a background to indicate selection
    canvas.before:
        Color:
            rgba: (.05, 0.5, .9, .8) if self.selected else (.5, .5, .5, 1)
        Rectangle:
            pos: self.pos
            size: self.size
    on_touch_down:
        print('on_touch_down:')
        if self.selectable: \
            app.root.screen_culture.culture_sub_view.update(self.text)
''')


class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
                                 RecycleBoxLayout):
    pass


class SelectableLabel(RecycleDataViewBehavior, Label):
    ''' Add selection support to the Label '''
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)

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

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SelectableLabel, 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):
        ''' Respond to the selection of items in the view. '''
        self.selected = is_selected
        if is_selected:
            print("selection changed to {0}".format(rv.data[index]))
            rv.name_selected = rv.data[index]['text']
        else:
            print("selection removed for {0}".format(rv.data[index]))


class CultureView(RecycleView):
    def __init__(self, **kwargs):
        super(CultureView, self).__init__(**kwargs)

        self.data = [
            {"text": "Test1", "description": "testu"},
            {"text": "Test2", "description": "testo"}
        ]


class CultureSubView(RecycleView):
    planet_selected = StringProperty('')

    def __init__(self, **kwargs):
        super(CultureSubView, self).__init__(**kwargs)
        self.selection_data = {
            '': [],
            'Test1': [
                {"text": "test1.1", "description": "test1"},
                {"text": "test1.2", "description": "text1"}
            ],
            'Test2': [
                {"text": "test2.1", "description": "text2"},
                {"text": "test2.2", "description": "test2"}
            ]
        }
        self.data = self.selection_data[self.planet_selected]

    def update(self, name_selected):
        print("Updating")
        self.planet_selected = name_selected
        self.data = self.selection_data[self.planet_selected]
        self.refresh_from_data()


class CultureLabel(ScrollView):
    text_selected = StringProperty('')
    name_selected = StringProperty('')


class ScreenCulture(Screen):
    culture_sub_view = ObjectProperty(None)


class ScreenManagement(ScreenManager):
    screen_culture = ObjectProperty(None)


class TestApp(App):
    def build(self):
        return ScreenManagement()


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

选项 2 -通过 Python 脚本调用CultureSubView.update()

from kivy.app import App
from kivy.properties import StringProperty, BooleanProperty, ObjectProperty
from kivy.uix.behaviors import FocusBehavior
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView


Builder.load_string('''
<ScreenManagement>:
    screen_culture: screen_culture

    ScreenCulture:
        id: screen_culture
        name: 'screen_culture'

<ScreenCulture>:
    culture_sub_view: culture_sub_view

    BoxLayout:
        orientation: 'vertical'

        Label:
            pos_hint: {"x": .45, "top": 1}
            text: "Testosterone"
            size_hint: .1, .1

        GridLayout:
            id: culture_layout
            rows: 1
            cols: 3
            padding: dp(10)
            spacing: dp(10)
            orientation: 'horizontal'

            CultureView:
                id: culture_list_view

            CultureSubView:
                id: culture_sub_view
                name_selected: 
                    "Planet" if not culture_list_view.name_selected else culture_list_view.name_selected

            CultureLabel:
                id: culture_text
                name_selected: 
                    "NVM" if not culture_sub_view.name_selected else culture_sub_view.name_selected

                text_selected: 
                    "This is totally a test text" if not culture_sub_view.text_selected else culture_sub_view.text_selected


<CultureView>:
    viewclass: 'SelectableLabel'
    name_selected: ''

    SelectableRecycleBoxLayout:
        orientation: 'vertical' 
        default_size: None, dp(32)
        default_size_hint: .6, None
        size_hint: 1, .9
        multiselect: False
        touch_multiselect: False


<CultureSubView>:
    viewclass: 'SelectableLabel'
    text_selected: ''
    name_selected: ''

    SelectableRecycleBoxLayout:
        orientation: 'vertical' 
        default_size: None, dp(32)
        default_size_hint: .6, None
        size_hint: 1, .9
        multiselect: False
        touch_multiselect: False


<CultureLabel>:
    size_hint_y: .85
    Label:
        halign: 'left'
        valign: 'middle'
        size_hint_y: None
        height: self.texture_size[1]
        text_size: self.width, None
        text: root.text_selected


<SelectableLabel>:
    # Draw a background to indicate selection
    canvas.before:
        Color:
            rgba: (.05, 0.5, .9, .8) if self.selected else (.5, .5, .5, 1)
        Rectangle:
            pos: self.pos
            size: self.size
''')


class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
                                 RecycleBoxLayout):
    pass


class SelectableLabel(RecycleDataViewBehavior, Label):
    ''' Add selection support to the Label '''
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)

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

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SelectableLabel, self).on_touch_down(touch):
            return True
        if self.collide_point(*touch.pos) and self.selectable:
            App.get_running_app().root.screen_culture.culture_sub_view.update(self.text)
            return self.parent.select_with_touch(self.index, touch)

    def apply_selection(self, rv, index, is_selected):
        ''' Respond to the selection of items in the view. '''
        self.selected = is_selected
        if is_selected:
            print("selection changed to {0}".format(rv.data[index]))
            rv.name_selected = rv.data[index]['text']
        else:
            print("selection removed for {0}".format(rv.data[index]))


class CultureView(RecycleView):
    def __init__(self, **kwargs):
        super(CultureView, self).__init__(**kwargs)

        self.data = [
            {"text": "Test1", "description": "testu"},
            {"text": "Test2", "description": "testo"}
        ]


class CultureSubView(RecycleView):
    planet_selected = StringProperty('')

    def __init__(self, **kwargs):
        super(CultureSubView, self).__init__(**kwargs)
        self.selection_data = {
            '': [],
            'Test1': [
                {"text": "test1.1", "description": "test1"},
                {"text": "test1.2", "description": "text1"}
            ],
            'Test2': [
                {"text": "test2.1", "description": "text2"},
                {"text": "test2.2", "description": "test2"}
            ]
        }
        self.data = self.selection_data[self.planet_selected]

    def update(self, name_selected):
        print("Updating")
        self.planet_selected = name_selected
        self.data = self.selection_data[self.planet_selected]
        self.refresh_from_data()


class CultureLabel(ScrollView):
    text_selected = StringProperty('')
    name_selected = StringProperty('')


class ScreenCulture(Screen):
    culture_sub_view = ObjectProperty(None)


class ScreenManagement(ScreenManager):
    screen_culture = ObjectProperty(None)


class TestApp(App):
    def build(self):
        return ScreenManagement()


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

输出:选项 1 -通过 kv 文件调用CultureSubView.update()

Img01 - 点击 Test1 Img02 - 点击 Test2

输出:选项 2 -通过 Python 脚本调用CultureSubView.update()

Img03 - 点击 Test1 Img04 - 点击 Test2


推荐阅读