Python kivy : Nested scrollviews

615 views Asked by At

I have a nested horizontal scroll view inside a vertical scroll view, I followed many links, found nothing, I need a clear scrollview so that if I scroll nested horrizontal scrollview it will scroll without disturbing the parent vertical scrollview, and if I scroll the nested horrizontal scrollview vertically then only the parent vertical scroll view will scroll! any suggestions ? thanks in advance! (I want the scroll view to be same as any other modern app like facebook story horizontal scrollview,, etc..)

2

There are 2 answers

9
Fadi Abu Raid On

This is how you can achieve what you are looking for.

Use KivyMD to get material design for your app. https://kivymd.readthedocs.io/en/latest/index.html

Then try this

scrollview.py

from kivymd.app import MDApp
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window

class MyScrollView(ScrollView):
    def on_touch_down(self, touch):
        # get touch position in coordinates of the main_box (parent of inner_scroll)
        x,y = self.ids.main_box.to_widget(*self.to_window(*touch.pos))

        # if converted position is within the inner_scroll, send touch to the inner_scroll
        if self.ids.inner_scroll.collide_point(x,y):
            touch.pos = (x,y)   # change touch position to coordinates in main_box
            return self.ids.inner_scroll.on_touch_down(touch)   # call on_touch of inner_scroll
        else:
            return super(MyScrollView, self).on_touch_down(touch)

class ScrolltwoApp(MDApp):
    
    def build(self):
        app=MDApp.get_running_app()
        Clock.schedule_once(self.add_members)
        return app.root
    
    def add_members(self,dt):
        app=MDApp.get_running_app()
        for i in range(15):
            app.root.ids.main_box.add_widget(Button(text='button'+str(i), size_hint=(1.0, None), height=50,background_color=[0,0,1,1]))
            app.root.ids.horizontal_grid.add_widget(Button(text='button' + str(i), size_hint=(None, None),height=130,width=100,background_color=[0,0,1,1]))
            app.root.ids.horizontal_grid2.add_widget(Button(text='button' + str(i), size_hint=(None, None),height=130,width=100,background_color=[0,0,1,1]))

if __name__ == '__main__':
    Window.show_cursor = True
    Window.size = (360, 680)
    ScrolltwoApp().run()

scrolltwo.kv

#: import Window kivy.core.window.Window
MyScrollView:
    bar_width: 5
    scroll_type:['bars', 'content']
    do_scroll: (False, True)
    size_hint_y: None
    height: Window.height
    GridLayout:
        id: main_box
        size_hint_y: None
        cols: 1
        height: self.minimum_height
        ScrollView:
            id: inner_scroll
            bar_width: 5
            scroll_type: ['bars', 'content']
            do_scroll: (True, False)
            size_hint_y: None
            effect_cls: "ScrollEffect"
            height: Window.height/4.5
            GridLayout:
                id: horizontal_grid
                rows: 1
                padding: [10, 10]
                size: self.minimum_size
                size_hint: None, None
        ScrollView:
            id: inner_scroll2
            bar_width: 5
            scroll_type: ['bars', 'content']
            do_scroll: (True, False)
            size_hint_y: None
            effect_cls: "ScrollEffect"
            height: Window.height/4.5
            GridLayout:
                id: horizontal_grid2
                rows: 1
                padding: [10, 10]
                size: self.minimum_size
                size_hint: None, None

Output

enter image description here

0
Pendechosen On

I understand you and had the same problem trying to scroll horizontal scrollview vertically, which it should scroll the vertical parent scrollview instead.

I peeked into the source code of scrollview and found out the child scrollview stops propogating move event to parent when it receives touchmove.

Here's a demo of the solution (some hacks) that allows you to scroll horizontal scrollview vertically that affects parent scrollview.

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder
from kivy.uix.scrollview import ScrollView
from kivy.properties import StringProperty

KV = """
<SomeText>
    id: some_text
    orientation: 'horizontal'
    ScrollText:
        Label:
            text: some_text.text
            max_lines: 1
            halign: 'left'
            padding_x: 20
            font_size: some_text.height
            width: self.texture_size[0]
            size_hint_x: None


<ScrollText>
    bar_color: (1,1,1,1)
    bar_width: 1

<VerticalScroll>
    some_text_list: some_text_list
    GridLayout:
        id: some_text_list
        cols: 1
        size_hint_y: None
        row_default_height: 80
        row_force_default: True
        height: self.minimum_height
"""

class SomeText(BoxLayout):
    text = StringProperty()
    

class ScrollText(ScrollView):
    # pass y-move event to parent instead of stopping
    def on_scroll_move(self, touch):
        super().on_scroll_move(touch)
        touch.ud['sv.handled']['y'] = False 

class VerticalScroll(ScrollView):
    def __init__(self, texts, **kwargs):
        super().__init__(**kwargs)
        for text in texts:
            self.some_text_list.add_widget(SomeText(text=text))

    # prevent stuck on overscroll
    def on_scroll_stop(self, touch, check_children=True):
        super().on_scroll_stop(touch, check_children=False)

if __name__ == "__main__":
    class TestApp(App):
        def build(self):
            Builder.load_string(KV)
            long_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
            texts = [long_text for _ in range(20)]
            return VerticalScroll(texts)
    TestApp().run()