Search code examples
python-3.xkivykivy-language

How to solve Kivy CardPost stacked to the bottom


I'm learning to create an app with KivyMd. The code below is my working code but but the problem is, the PostCard is kind of stacked to the bottom with first Post fixed and the rest scrolling vertically(scroll-y). Please i need help in getting the posts displaying properly and hence removing the large whitesapce above them.

Also I get an error when I change the Navigation Menu Icon from 'checkbox-blank-circle' to say 'facebook' or 'book'.

Sorry am asking too much in one question.

from kivy.app import App
from kivy.lang import Builder
from kivy.factory import Factory

from kivymd.navigationdrawer import NavigationDrawerIconButton
from kivymd.theming import ThemeManager
from kivymd.card import MDCardPost
from kivymd.toast import toast

main_kv = """
#:import Toolbar kivymd.toolbar.Toolbar
#:import MDNavigationDrawer kivymd.navigationdrawer.MDNavigationDrawer
#:import NavigationDrawerSubheader kivymd.navigationdrawer.NavigationDrawerSubheader
#:import MDLabel kivymd.label.MDLabel


<ContentNavigationDrawer@MDNavigationDrawer>:
    drawer_logo: 'business-1.jpg'

    NavigationDrawerSubheader:
        text: "Menu:"


NavigationLayout:
    id: nav_layout

    ContentNavigationDrawer:
        id: nav_drawer

    BoxLayout:
        orientation: 'vertical'

        Toolbar:
            id: toolbar
            title: 'My First App'
            md_bg_color: app.theme_cls.primary_color
            background_palette: 'Primary'
            background_hue: '500'
            elevation: 10
            left_action_items:
                [['dots-vertical', lambda x: app.root.toggle_nav_drawer()]]

        FloatLayout:
            MDCardPost:
                orientation: 'vertical'
                spacing: dp(5)

                ScrollView:
                    id: scroll
                    size_hint: 1, 1
                    do_scroll_x: False

                    GridLayout:
                        id: grid_card
                        cols: 1
                        spacing: dp(5)
                        padding: dp(5)
                        size_hint_y: None
                        height: self.minimum_height    
"""


class Example(App):
    theme_cls = ThemeManager()
    theme_cls.primary_palette = 'Blue'
    title = "My App"
    main_widget = None
    cards_created = False

    def build(self):
        self.main_widget = Builder.load_string(main_kv)
        return self.main_widget

    def callback(self, instance, value):
        if value==1:
            self.main_widget.ids.toolbar.title="Something 1"
        elif value==2:
            self.main_widget.ids.toolbar.title="Something 2"
        else:
            self.main_widget.ids.toolbar.title="Something 3"

        toast("Pressed Something %d" % value)

    def on_start(self):

        def callback_for_menu_items(text_item):
            toast(text_item)

        def callback(instance, value):
            if value and isinstance(value, int):
                toast('Set like in %d stars' % value)
            elif value and isinstance(value, str):
                toast('Repost with %s ' % value)
            elif value and isinstance(value, list):
                toast(value[1])
            else:
                toast('Delete post %s' % str(instance))

    self.main_widget.ids.nav_drawer.add_widget(
            NavigationDrawerIconButton(
                icon='checkbox-blank-circle', text="Something 1",
                on_release=lambda x, y=1: self.callback(x, y)))
        self.main_widget.ids.nav_drawer.add_widget(
            NavigationDrawerIconButton(
                icon='checkbox-blank-circle', text="Something 2",
                on_release=lambda x, y=2: self.callback(x, y)))
        self.main_widget.ids.nav_drawer.add_widget(
            NavigationDrawerIconButton(
                icon='checkbox-blank-circle', text="Something 3",
                on_release=lambda x, y=3: self.callback(x, y)))

        instance_grid_card = self.main_widget.ids.grid_card
        buttons = ['facebook', 'vk', 'twitter']
        menu_items = [
            {'viewclass': 'MDMenuItem',
             'text': 'Example item %d' % i,
             'callback': callback_for_menu_items}
            for i in range(2)
        ]

        if not self.cards_created:
            self.cards_created = True

            instance_grid_card.add_widget(
                MDCardPost(text_post='Card with text',
                           swipe=True, callback=callback))
            instance_grid_card.add_widget(
                MDCardPost(
                    right_menu=menu_items, swipe=True,
                    text_post='Card with a button to open the menu MDDropDown',
                    callback=callback))
            instance_grid_card.add_widget(
                MDCardPost(
                    likes_stars=True, callback=callback, swipe=True,
                    text_post='Card with asterisks for voting.'))

            instance_grid_card.add_widget(
                MDCardPost(
                    source="./assets/kitten-1049129_1280.jpg",
                    tile_text="Little Baby",
                    tile_font_style="Headline",
                    text_post="This is my favorite cat. He's only six months "
                              "old. He loves milk and steals sausages :) "
                              "And he likes to play in the garden.",
                    with_image=True, swipe=True, callback=callback,
                    buttons=buttons))

Example().run()

This is the picture of it.

This picture represents what I have now


Solution

  • You can try to use BoxLayout widget instead of FloatLayout widget.

    BoxLayout:
    
        orientation: 'vertical'
    
        Toolbar:
            id: toolbar
            title: 'My First App'
            md_bg_color: app.theme_cls.primary_color
            background_palette: 'Primary'
            background_hue: '500'
            elevation: 10
            left_action_items:
                [['dots-vertical', lambda x: app.root.toggle_nav_drawer()]]
    
        # This one replaced the FloatLayout
        BoxLayout:
            orientation: 'vertical'
    
            MDCardPost:
                orientation: 'vertical'
                spacing: dp(5)
    
    
            ScrollView:
                id: scroll
                size_hint: 1, 1
                do_scroll_x: False
    
                GridLayout:
                    id: grid_card
                    cols: 1
                    spacing: dp(5)
                    padding: dp(5)
                    size_hint_y: None
                    height: self.minimum_height    
    

    Here are some output:

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    Regarding about the icons that gave an error, you can try to instantiate first the NavigationDrawerIconButton widget then give it an icon.

        firstItem = NavigationDrawerIconButton(
            text="Something 1",
            on_release=lambda x, y=1: self.callback(x, y)
            )
    
        firstItem.icon = 'facebook'
    
        self.main_widget.ids.nav_drawer.add_widget(firstItem)
    
        secondItem = NavigationDrawerIconButton(
            text="Something 2",
            on_release=lambda x, y=2: self.callback(x, y)
            )
    
        secondItem.icon = 'book'
    
        self.main_widget.ids.nav_drawer.add_widget(secondItem)
    
        thirdItem = NavigationDrawerIconButton(
            text="Something 3",
            on_release=lambda x, y=3: self.callback(x, y)
            )
    
        thirdItem.icon = 'face'
    
        self.main_widget.ids.nav_drawer.add_widget(thirdItem)
    

    Here's the output:

    enter image description here

    To make the first post card scrollable, you can remove these lines from the kv.

            #Remove these lines 
            MDCardPost:
                orientation: 'vertical'
                spacing: dp(5)
    

    Then we can add the first post card in the ScrollView like this:

    if not self.cards_created:
        self.cards_created = True
    
        # This is the first post card
        instance_grid_card.add_widget(
                MDCardPost()
        )
    
        instance_grid_card.add_widget(
                MDCardPost(text_post='Card with text',
                           swipe=True, callback=callback))
        instance_grid_card.add_widget(
                MDCardPost(
                    right_menu=menu_items, swipe=True,
                    text_post='Card with a button to open the menu MDDropDown',
                    callback=callback))
        instance_grid_card.add_widget(
                MDCardPost(
                    likes_stars=True, callback=callback, swipe=True,
                    text_post='Card with asterisks for voting.'))