amit yakovian
amit yakovian

Reputation: 91

Create an infinite scrollview image in kivy

I'm trying to create a digital notebook app using kivy. I have my basic screen and a ScrollView of the lined sheet I want to use. It seems to be fine, but I now want this sheet to be infinite - meaning you'd be able to keep scrolling down and have more lines of it (like having the image duplicate itself vertically). How can I do that?

I'd really appreciate any help :)

Python code:

import kivy
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from PIL import Image as Image1
from kivy.uix.image import Image

GUI = Builder.load_file('style.kv')
img_size = Image1.open("images/notebook.png").size


class NotebookScreen(GridLayout):

    def __init__(self, **kwargs):
        self.rows = 1
        super(NotebookScreen, self).__init__(**kwargs)

    def get_size_for_notebook(self, **kwargs):
        global img_size
        width, height = Window.size
        return width, (img_size[0] * height / width)


class MainApp(App):

    def build(self):
        return NotebookScreen()


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

kv file:

<NotebookScreen>
    FloatLayout:
        rows: 2
        GridLayout:
            size_hint: 1, .05
            pos_hint: {"top": 1, "left": 1}
            id: tool_bar
            cols: 1
            canvas:
                Color:
                    rgba: 0, 0, 1, 1
                Rectangle:
                    pos: self.pos
                    size: self.size
        GridLayout:
            id: notebook_grid
            size_hint: 1, .95
            pos_hint: {"top": .95, "left": .97}
            cols: 1


            ScrollView:
                do_scroll: (False, True)  # up and down

                Image:
                    id: notebook_image
                    source: 'images/notebook.png'
                    allow_stretch: True
                    keep_ratio: False
                    pos: self.pos
                    size: root.get_size_for_notebook()
                    size_hint: 1, None

Upvotes: 1

Views: 1200

Answers (1)

John Anderson
John Anderson

Reputation: 39082

You can do that by using a BoxLayout to hold the lined sheet Image. Then you can just add more instances of the same Image to the BoxLayout as needed. Here is a version of your code that does that:

from kivy.lang import Builder
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
from PIL import Image as Image1
from kivy.uix.image import Image

#GUI = Builder.load_file('style.kv')
Builder.load_string('''
<NotebookScreen>
    FloatLayout:
        GridLayout:
            size_hint: 1, .05
            pos_hint: {"top": 1, "left": 1}
            id: tool_bar
            cols: 1
            canvas:
                Color:
                    rgba: 0, 0, 1, 1
                Rectangle:
                    pos: self.pos
                    size: self.size
        GridLayout:
            id: notebook_grid
            size_hint: 1, .95
            pos_hint: {"top": .95, "left": 0}
            cols: 1

            MyScrollView:
                do_scroll: (False, True)  # up and down

                BoxLayout:
                    id: notebook_images
                    orientation: 'vertical'
                    size_hint: 1, None
                    height: self.minimum_height
                    MyImage:
<MyImage>:
    source: 'images/notebook.png'
    allow_stretch: True
    keep_ratio: True
    size_hint: None, None
    size: self.get_size_for_notebook()
''')

Window.size = (1000, 200)
img_size =Image1.open("images/notebook.png").size


class MyImage(Image):
    def get_size_for_notebook(self, **kwargs):
        global img_size
        width, height = Window.size
        return width, (img_size[0] * height / width)


class MyScrollView(ScrollView):
    def on_scroll_y(self, instance, scroll_val):
        if scroll_val < 0.05:  # no logic for this number
            box = App.get_running_app().root.ids.notebook_images
            new_image = MyImage()
            box.add_widget(new_image)
            self.scroll_y = new_image.height / box.height  # a more careful calculation may provide smoother operation


class NotebookScreen(GridLayout):
    def __init__(self, **kwargs):
        self.rows = 1
        super(NotebookScreen, self).__init__(**kwargs)


class MainApp(App):

    def build(self):
        return NotebookScreen()


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

The use of Builder.load_string() instead of load_file() is just for my own convenience.

Upvotes: 1

Related Questions