Search code examples
pythonkivy

How to automatically scroll to a position in a ScrollView (Kivy)


I need to have a ScrollView with a GridLayout inside of it, and then add a varying number of labels to it. I was able to build this with the code below, and the Kivy scroll works beautifully.

But I also need the ScrollView to automatically scroll to any label in the grid. For instance, if the user selects a certain label somewhere else in the application, the ScrollView should automatically scroll to the corresponding label.

I've tried several methods to do this, including changing scroll_y and using scroll_to() as in the documentation, but changing scroll_y does not update the ScrollView until the user manually scrolls with cursor over the ScrollView, and the scroll_to() method does not scroll at all for me (see code below). Even just auto-scrolling a certain percentage of the ScrollView would work for me.

Does anyone have any suggestions on dealing with this issue? I looked at a lot of similar questions, but couldn't find an answer. I really appreciate any help.

Here is my python code:


from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.graphics import Color, Rectangle
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.scrollview import ScrollView

class LabelScroll(ScrollView):
    
    def scroll_to_label(self):
        label = LabelGrid.labels_list[75]
        self.scroll_to(self, label)

class LabelGrid(GridLayout):

    number = 0
    labels_list = [] # list to hold labels

    def add_label(self):
        # this function creates a label and adds to the ScrollView 100 times
        if self.number < 100:
            label_text = 'text' + str(self.number) # creates unique text
            lbl = Label(text=label_text) # adds text to label
            self.labels_list.append(lbl) # adds label to list for reference later
            self.add_widget(lbl)
            self.number = self.number + 1
            self.add_label() # code to start this function again

class Frame(FloatLayout):
    pass

class ScrollApp(App):
    def build(self):
        return Frame()

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

And here is my kv language file:

#: kivy 2.2.0

<Frame>
    Widget:
        # this widget is just a canvas to show where
        # the ScrollView is located
        canvas:
            Color:
                rgba: (0, .5, .5, 1)
            Rectangle:
                size: (root.width * .2, root.height)
                pos: (root.width * .8, 0)

    Button:
        # this button triggers the function
        # to add the labels to the ScrollView
        pos_hint: {'x': .3, 'y': .6}
        size_hint: (.2, .2)
        text: 'Add labels to ScrollView'
        on_press: lbl_grd.add_label()

    Button:
        # this button triggers the function
        # to scroll to label 75
        pos_hint: {'x': .3, 'y': .2}
        size_hint: (.2, .2)
        text: 'Scroll to label 75\n(Hit the other button first)'
        on_press: scrlvw.scroll_to_label()      

    LabelScroll:
        # this is the ScrollView
        id: scrlvw
        pos_hint: {'x': .8}
        size_hint_x: .2
        do_scroll_x: False
        do_scroll_y: True
        bar_width: root.width * .007
        effect_cls: 'ScrollEffect'
        scroll_type: ['bars', 'content']

        LabelGrid:
            id: lbl_grd
            size_hint_y: None
            cols: 1
            height: self.minimum_height
            row_default_height: root.height * .05

Solution

  • Just a minor issue. You don't need a self argument to an instance method. Try changing:

    self.scroll_to(self, label)
    

    to:

    self.scroll_to(label)
    

    in your scroll_to_label() method.