Kivy中的可滚动动态网格布局

时间:2018-06-01 13:59:26

标签: python-3.x kivy

我拥有什么:
- 动态TextWrapper(GridLayout),包括由SpeciesView.data中的“描述”文本制作的图像和标签
- 一个RecycleView,让我点击不同的文本

问题是,我似乎找不到滚动GridLayout(TextWrapper)的方法。

我认为我没有正确使用size_hint_y,这应该允许ScrollView能够滚动。 The reason found here.
文字总是很奇怪。

那么如何让这个ScrollView工作?

from kivy.app import App
from kivy.uix.button import Label
from kivy.lang import Builder
from kivy.properties import BooleanProperty, StringProperty, ObjectProperty
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
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_species: screen_species

    ScreenSpecies:
        id: screen_species
        name: 'screen_species'


<ScreenSpecies>:
    species_text: species_text

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

    BoxLayout:
        id: species_layout
        padding: dp(10)
        spacing: dp(10)
        orientation: 'horizontal'

        SpeciesView:
            id: species_list_view

        SpeciesText:
            id: species_text
            name_selected: "" if not species_list_view.name_selected else species_list_view.name_selected


<SpeciesView>:
    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


<SpeciesText>:
    ScrollView:
        size_hint_y: None  


<SelectableLabel>:
    canvas.before:
        Color:
            rgba: (.05, 0.5, .9, .8) if self.selected else (.5, .5, .5, 1)
        Rectangle:
            pos: self.pos
            size: self.size


<SpeciesLabel>:      
    text_size: self.size       
    size_hint_y: None   


<SpeciesImage>:
    size_hint_y: None
    allow_stretch: True
    keep_ratio: True
    texture_size: dp(20), dp(40)


<TextWrapper>:
    minimum_height: self.height
    orientation: "vertical"
    spacing: 10
    cols: 1
    size_hint_y: None
""")

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']
            App.get_running_app().root.screen_species.species_text.update(TextWrapper(rv.data[index]["description"]))
        else:
            print("selection removed for {0}".format(rv.data[index]))


class ScreenSpecies(Screen):
    pass


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

        self.data = [
            {"text": "Test1", "description": "Test1.textbf\nBla\n |img=image.jpg| Test1.textaf\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla"},
            {"text": "Test2", "description": "Test2.text"},
            {"text": "Test3", "description": "Test3.text"}
        ]


class SpeciesText(ScrollView):
    def update(self, text_wrapper):
        print("Adding TextWrapper {} to SpeciesText ScrollView".format(text_wrapper))
        self.clear_widgets()
        self.add_widget(text_wrapper)


class SpeciesLabel(Label):
    pass


class SpeciesImage(Image):
    pass


class TextWrapper(GridLayout):
    def __init__(self, text="", **kwargs):
        super(TextWrapper, self).__init__(**kwargs)
        self.content = self.wrap_text(text)

    def wrap_text(self, source):
        text = source.split("|")

        for i in range(0, len(text)):
            if "img=" in text[i]:
                self.add_widget(SpeciesImage(source=text[i][4:]))
            else:
                self.add_widget(SpeciesLabel(text=text[i]))
        return text


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


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


test_app = TestApp()
test_app.run()

一如既往地谢谢!

1 个答案:

答案 0 :(得分:2)

SpeciesText - 使用RstDoc

以下示例说明了Kivy RstDcoument。它支持长文本images,文档可滚动。

片段

kv文件

<SpeciesText>:
    container: container
    orientation: 'vertical'
    size_hint: 1, .9

    RstDocument:
        id: container

示例:SpeciesText - 使用RstDoc

main.py

from kivy.app import App
from kivy.uix.button import Label
from kivy.lang import Builder
from kivy.properties import BooleanProperty, ObjectProperty
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.image import Image
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.boxlayout import BoxLayout

Builder.load_string("""
<ScreenManagement>:
    screen_species: screen_species

    ScreenSpecies:
        id: screen_species
        name: 'screen_species'


<ScreenSpecies>:
    species_text: species_text

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

    BoxLayout:
        id: species_layout
        padding: dp(10)
        spacing: dp(10)
        orientation: 'horizontal'

        SpeciesView:
            id: species_list_view

        SpeciesText:
            id: species_text
            name_selected: "" if not species_list_view.name_selected else species_list_view.name_selected


<SpeciesView>:
    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

<SpeciesText>:
    container: container
    orientation: 'vertical'
    size_hint: 1, .9

    RstDocument:
        id: container

<SelectableLabel>:
    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']
            App.get_running_app().root.screen_species.species_text.update(rv.data[index]["description"])
        else:
            print("selection removed for {0}".format(rv.data[index]))


class ScreenSpecies(Screen):
    species_text = ObjectProperty(None)


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

        self.data = [
            {"text": "Test1", "description": "./rstDocs/bearwithus.rst"},
            {"text": "Test2", "description": "./rstDocs/kvDoc.rst"},
            {"text": "Test3", "description": "./rstDocs/sphinxDoc.rst"}
        ]


class SpeciesText(BoxLayout):
    container = ObjectProperty(None)

    def update(self, source):
        print("Adding TextWrapper {} to SpeciesText ScrollView".format(source))
        self.container.source = source


class SpeciesLabel(Label):
    pass


class SpeciesImage(Image):
    pass


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


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


test_app = TestApp()
test_app.run()

输出:SpeciesText - 使用RstDoc

Img03 - RstDoc Img04 - RstDoc Img05 - RstDoc

SpeciesText - 使用ScrollView + GridLayout + Label + Image

使用ObjectProperty连接到GridLayout。有关详细信息,请参阅示例。

片段

kv文件

<SpeciesText>:
    container: container

    bar_width: 10
    bar_color: 0, 1, 0, 1   # green
    bar_inactive_color: 1, 0, 0, 1   # red
    effect_cls: "ScrollEffect"
    scroll_type: ['bars']
    size_hint: (1, None)

    # TextWrapper
    GridLayout:
        id: container
        cols: 1
        spacing: 10
        size_hint_y: None
        height: self.minimum_height

示例:SpeciesText - 使用ScrollView + GridLayout + Label + Image

main.py

from kivy.app import App
from kivy.uix.button import Label
from kivy.lang import Builder
from kivy.properties import BooleanProperty, StringProperty, ObjectProperty
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
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_species: screen_species

    ScreenSpecies:
        id: screen_species
        name: 'screen_species'


<ScreenSpecies>:
    species_text: species_text

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

    BoxLayout:
        id: species_layout
        padding: dp(10)
        spacing: dp(10)
        orientation: 'horizontal'

        SpeciesView:
            id: species_list_view

        SpeciesText:
            id: species_text
            name_selected: "" if not species_list_view.name_selected else species_list_view.name_selected


<SpeciesView>:
    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

<SpeciesText>:
    container: container

    bar_width: 10
    bar_color: 0, 1, 0, 1   # green
    bar_inactive_color: 1, 0, 0, 1   # red
    effect_cls: "ScrollEffect"
    scroll_type: ['bars']
    size_hint: (1, None)

    GridLayout:
        id: container
        cols: 1
        spacing: 10
        size_hint_y: None
        height: self.minimum_height

<SelectableLabel>:
    canvas.before:
        Color:
            rgba: (.05, 0.5, .9, .8) if self.selected else (.5, .5, .5, 1)
        Rectangle:
            pos: self.pos
            size: self.size


<SpeciesLabel>:      
    size: self.texture_size       
    size_hint_y: None   


<SpeciesImage>:
    size_hint_y: None
    allow_stretch: True
    keep_ratio: True
    texture_size: dp(20), dp(40)

""")


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']
            App.get_running_app().root.screen_species.species_text.wrap_text(rv.data[index]["description"])
        else:
            print("selection removed for {0}".format(rv.data[index]))


class ScreenSpecies(Screen):
    species_text = ObjectProperty(None)


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

        self.data = [
            {"text": "Test1", "description": "Test1.textbf\nBla\n |img=kivy_logo.jpg| Test1.textaf\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla"},
            {"text": "Test2", "description": "Test2.text"},
            {"text": "Test3", "description": "Test3.text"}
        ]


class SpeciesText(ScrollView):
    container = ObjectProperty(None)

    def wrap_text(self, source):
        print("Adding TextWrapper {} to SpeciesText ScrollView".format(source))
        self.container.clear_widgets()
        text = source.split("|")

        for txt in text:
            if "img=" in txt:
                self.container.add_widget(SpeciesImage(source=txt[4:]))
            else:
                self.container.add_widget(SpeciesLabel(text=txt))


class SpeciesLabel(Label):
    pass


class SpeciesImage(Image):
    pass


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


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


test_app = TestApp()
test_app.run()

输出:SpeciesText - 使用ScrollView + GridLayout + Label + Image

Img01 Img02