ScrollView not scrolling when using RelativeLayout as the layout

Question:

I want to be able to build a midi sheet as shown here:
example

I’ve used a relative layout to place the buttons at certain positions (not finalised, still in testing). However, I can’t get the scrollview to scroll horizontally or vertically for that matter. Could someone help me out? Or if you have any suggestions for a better implementation, that would be helpful. Where I create this relative layout is in the screen called MidiSheet:

class MidiSheet(Screen):

    def __init__(self, **kwargs):
        super(MidiSheet, self).__init__(**kwargs)

        screen = ScrollView(size=(Window.width, Window.height), do_scroll_x=True, do_scroll_y=False)

        notes = get_notes_in_song("../data/" + self.name)

        layout = RelativeLayout(size=(Window.width, Window.height))
        layout.add_widget(BackButton(self))

        initial_y_pos = Window.height/2
        initial_x_pos = Window.width*0.1
        for note in notes:
            note_name = note.split(":")[1]

            layout.add_widget(MidiButton(note_name, initial_x_pos, initial_y_pos))

            initial_x_pos += 100

        screen.add_widget(layout)

        self.add_widget(screen)

Here’s my full .py

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition

import os


def get_notes_in_song(song):

    with open(song) as f:
        lines = f.readlines()

    return lines


class SongButton(Button):

    def __init__(self, file_name, **kwargs):
        super(SongButton, self).__init__(**kwargs)

        self.background_color = [0, 0, 0, 0]
        self.size_hint = (0.5, None)
        self.height = 40

        self.file_name = file_name

    def on_press(self):
        App.get_running_app().root.add_widget(MidiSheet(name=self.file_name))
        App.get_running_app().root.transition.direction = "left"
        App.get_running_app().root.current = self.file_name


class BackButton(Button):
    def __init__(self, screen_instance, **kwargs):
        super(BackButton, self).__init__(**kwargs)

        self.background_color = [1, 1, 1, 1]
        self.size_hint = (0.1, 0.1)
        self.text = "Back"

        self.screen_instance = screen_instance

    def on_press(self):
        App.get_running_app().root.remove_widget(self.screen_instance)
        App.get_running_app().root.transition.direction = "right"
        App.get_running_app().root.current = "home"


class MidiButton(Button):
    def __init__(self, name, pos_x, pos_y, **kwargs):
        super(MidiButton, self).__init__(**kwargs)

        self.background_color = [1, 1, 1, 1]
        self.size_hint = (0.1, 0.1)
        self.pos = (pos_x, pos_y)
        self.text = name
        self.padding = (10, 10)

    def on_press(self):
        self.background_color = [0, 1, 0, 1] if self.background_color == [1, 1, 1, 1] else [1, 1, 1, 1]


class Home(Screen):
    def __init__(self, **kwargs):
        super(Home, self).__init__(**kwargs)

        screen = ScrollView(size_hint=(1, None), size=(Window.width, Window.height))

        layout = GridLayout(cols=1, spacing=10, size_hint_y=None)

        songs = [x for x in os.listdir("../data/") if ".txt" in x]

        for song in songs:
            name = song.replace("_", " ").replace(".txt", "")

            layout.add_widget(SongButton(song, text=name))

        screen.add_widget(layout)

        self.add_widget(screen)


class MidiSheet(Screen):

    def __init__(self, **kwargs):
        super(MidiSheet, self).__init__(**kwargs)

        screen = ScrollView(size=(Window.width, Window.height), do_scroll_x=True, do_scroll_y=False)

        notes = get_notes_in_song("../data/" + self.name)

        layout = RelativeLayout(size=(Window.width, Window.height))
        layout.add_widget(BackButton(self))

        initial_y_pos = Window.height/2
        initial_x_pos = Window.width*0.1
        for note in notes:
            note_name = note.split(":")[1]

            layout.add_widget(MidiButton(note_name, initial_x_pos, initial_y_pos))

            initial_x_pos += 100

        screen.add_widget(layout)

        self.add_widget(screen)


class Localiser(App):

    def build(self):
        sm = ScreenManager(transition=SlideTransition())
        sm.add_widget(Home(name="home"))

        return sm

Asked By: Diyon335

||

Answers:

You need to set the height of the GridLayout. Since you don’t know in advance how many Buttons you will be adding to the GridLayut, you can use the minimum_height property of the GridLayout:

    layout = GridLayout(cols=1, spacing=10, size_hint_y=None)
    layout.bind(minimum_height=layout.setter('height'))
Answered By: John Anderson

So actually my problem was that the size of my RelativeLayout wasn’t bigger than my ScrollView’s size. Only if the RelativeLayout is bigger, will there be something to scroll. So I had to disable the size_hint of my layout, and using the pixel size, I was able to make my layout size, bigger than my ScrollView size

screen = ScrollView(size_hint=(None, None), size=(Window.width, Window.height), do_scroll_x=True, do_scroll_y=False)

class MidiLayout(RelativeLayout):

    def __init__(self, midi_buttons, **kwargs):
        super(MidiLayout, self).__init__(**kwargs)

        self.size_hint = (None, None)

        self.window_width = Window.width

        for note in midi_buttons:
            self.window_width += note.size[0]

            self.add_widget(note)

        self.size = (self.window_width + 100, Window.height)

Answered By: Diyon335
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.