Norbert Tiborcz
Norbert Tiborcz

Reputation: 281

Kivy - How to add multiple Buttons to ScrollView inside ModalView?

I want to write a mobile app with a KivyMD like Bottom Sheet Menu. My problem with KivyMD Buttom Sheet is, when I click the button to open it, takes very long time (depends on the menu length) because the list is generated by calling the function every time the button was pressed. So I want to write my own solution for this. In my kv file I manually added 20 buttons to see, it's everything work. But i didn't find the way to do it in python file with loop. Can anyone help me please to add more than 30 buttons to modalview to be scrollable?

Here is my python file:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.core.window import Window


Builder.load_file('mylayout.kv')
Window.size = (350, 700)

class MyLayout(BoxLayout):
    pass

class MainApp(App):

    def build(self):
        return MyLayout()


MainApp().run()

an my kv file:

#:import Factory kivy.factory.Factory

<MyPopup@ModalView>
    auto_dismiss: True
    size_hint: 1, 0.5
    pos_hint: {'x': 0, 'top': 0.5}
    background_color: 0,0,0,0
    background_normal: ''
    canvas.before:
        Color:
            rgba: 48/150,84/150,150/150,1
        Rectangle:
            size: self.size
            pos: self.pos
    ScrollView:
        #do_scroll_x: False
        GridLayout:
            id: container1
            cols: 1
            size_hint: None, None
            size: root.width, 1200
            pos_hint: {'center_x': .5, 'center_y': .5}

            MyButton:
                text: '1'
                on_press:
                    root.dismiss()
                    print(1)
            MyButton:
                text: '2'
                on_press:
                    root.dismiss()
                    print(2)
            MyButton:
                text: '3'
                on_press:
                    root.dismiss()
                    print(3)
            MyButton:
                text: '4'
                on_press:
                    root.dismiss()
                    print(4)
            MyButton:
                text: '5'
                on_press:
                    root.dismiss()
                    print(5)
            MyButton:
                text: '6'
                on_press:
                    root.dismiss()
                    print(6)
            MyButton:
                text: '7'
                on_press:
                    root.dismiss()
                    print(7)
            MyButton:
                text: '8'
                on_press:
                    root.dismiss()
                    print(8)
            MyButton:
                text: '9'
                on_press:
                    root.dismiss()
                    print(9)
            MyButton:
                text: '10'
                on_press:
                    root.dismiss()
                    print(10)
            MyButton:
                text: '11'
                on_press:
                    root.dismiss()
                    print(11)
            MyButton:
                text: '12'
                on_press:
                    root.dismiss()
                    print(12)
            MyButton:
                text: '13'
                on_press:
                    root.dismiss()
                    print(13)
            MyButton:
                text: '14'
                on_press:
                    root.dismiss()
                    print(14)
            MyButton:
                text: '15'
                on_press:
                    root.dismiss()
                    print(15)
            MyButton:
                text: '16'
                on_press:
                    root.dismiss()
                    print(16)
            MyButton:
                text: '17'
                on_press:
                    root.dismiss()
                    print(17)
            MyButton:
                text: '18'
                on_press:
                    root.dismiss()
                    print(18)
            MyButton:
                text: '19'
                on_press:
                    root.dismiss()
                    print(19)
            MyButton:
                text: '20'
                on_press:
                    root.dismiss()
                    print(20)

<MyLayout>
    orientation: 'vertical'
    size: root.width, root.height

    Label:
        size_hint: 1, 0.9
        text: 'main'
        font_size: 24

    Button:
        size_hint: 1, 0.1
        text: 'menu'
        font_size: 24
        on_release: Factory.MyPopup().open()

<MyButton@Button>
    background_color: 0,0,0,0
    background_normal: ''
    canvas.before:
        Color:
            rgba: (48/255,84/255,150/255,1) if self.state == 'normal' else (43/255,108/255,229/255,1)
        Rectangle:
            size: self.size
            pos: self.pos

Upvotes: 0

Views: 980

Answers (1)

John Anderson
John Anderson

Reputation: 38837

In order to build the MyPopup filled with MyButtons, you must either define those classes in the python code or use Factory to create the instances. Here is a modified version of your kv to do this:

<MyPopup@ModalView>
    auto_dismiss: True
    size_hint: 1, 0.5
    pos_hint: {'x': 0, 'top': 0.5}
    background_color: 0,0,0,0
    background_normal: ''
    canvas.before:
        Color:
            rgba: 48/150,84/150,150/150,1
        Rectangle:
            size: self.size
            pos: self.pos
    ScrollView:
        #do_scroll_x: False
        GridLayout:
            id: container1
            cols: 1
            size_hint: None, None
            width: root.width
            height: self.minimum_height  # let the GridLayout set its own height as needeed
            pos_hint: {'center_x': .5, 'center_y': .5}
            
<MyLayout>
    orientation: 'vertical'
    size: root.width, root.height

    Label:
        size_hint: 1, 0.9
        text: 'main'
        font_size: 24

    Button:
        size_hint: 1, 0.1
        text: 'menu'
        font_size: 24
        # on_release: Factory.MyPopup().open()
        on_release: app.open_popup()  # call app method to build MyPopup and fill it

<MyButton@Button>
    background_color: 0,0,0,0
    background_normal: ''
    size_hint_y: None
    height: 20
    canvas.before:
        Color:
            rgba: (48/255,84/255,150/255,1) if self.state == 'normal' else (43/255,108/255,229/255,1)
        Rectangle:
            size: self.size
            pos: self.pos

Note that the GridLayout height is set to self.minimum_height to allow for any number of MyButton children, and the MyButton height is set to a fixed value (so that GridLayout can calculate the minimum height). Also, the import of Factory is no longer needed in the kv.

The modified python code:

from kivy.app import App
from kivy.factory import Factory
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.core.window import Window

Builder.load_file('mylayout.kv')
Window.size = (350, 700)

class MyLayout(BoxLayout):
    pass

class MainApp(App):

    def build(self):
        return MyLayout()

    def open_popup(self, *args):
        # create the popup (must use Factory since MyPopup is defined in kv)
        self.popup = Factory.MyPopup()

        # fill the GridLayout
        grid = self.popup.ids.container1
        for i in range(60):
            grid.add_widget(Factory.MyButton(text=str(i), on_press=self.myButtPress))

        # open popup
        self.popup.open()

    def myButtPress(self, butt):
        print(butt.text)
        self.popup.dismiss()

MainApp().run()

Upvotes: 1

Related Questions