Ten Kho
Ten Kho

Reputation: 344

How to add and remove widgets with animation in BoxLayout in Kivy

So basically I'm trying to use Animation with add_widget and remove_widget to make the app look smoother.

The problem with the default BoxLayout is that when you remove_widget, the widget just disappears, and then fills gaps with the rest of the widgets instantly.

For example, when you click the "x" button below the widget enter image description here

enter image description here

The glass of wine disappeared and auto-fit without any smooth animation

I just wonder if is there any way to do this?

Here the code used for the image example:

from kivymd.app import MDApp

from kivymd.uix.relativelayout import MDRelativeLayout

from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.uix.button import Button

from kivy.properties import StringProperty 

from kivy.metrics import dp
from kivy.lang.builder import Builder

KV="""
<ListItems>:
    ScrollView:
        do_scroll_y: False
        
        MDBoxLayout:
            id: order_list
            
            padding: dp(10)
            
            size_hint:None,None
            width: self.minimum_width
            height: dp(150)
    
    Button:
        text: "ADD PUDDING"
        size_hint: None, None
        size: dp(110), dp(60)
        pos_hint:{"center_x":.2, "center_y":.1}
        on_press:
            root.add_item="ReWorkLayout/items/pudding.png"
            
    Button:
        text: "ADD WINE"
        size_hint: None, None
        size: dp(110), dp(60)
        pos_hint:{"center_x":.8, "center_y":.1}
        on_press:
            root.add_item="ReWorkLayout/items/wine.png"
            
<Item>:
    id: item
    source: root.image
    
    size_hint: None,None
    size: dp(130),dp(130)
    
    MDIconButton:
        icon: "close"
        
        user_font_size: dp(35)
        
        pos: item.pos[0] + item.width/2 - self.width/2,item.pos[1] - self.height
        
        on_press:
            app.root.ids.order_list.remove_widget(root)
"""



class ListItems(MDRelativeLayout):
    def __init__(self,**kwargs):
        super().__init__(**kwargs)
    
    @property
    def add_item(self):
        return "nothing here lul."
    
    @add_item.setter
    def add_item(self,image):
        i = Item()
        i.image=image
        self.ids.order_list.add_widget(i)
        
    def remove_item(self,item):
        self.ids.order_list.remove_widget(item)

class Item(Image):
    image=StringProperty()

class TestRun(MDApp):
    def build(self):
        self.kv=Builder.load_string(KV)
        return ListItems()
    
if __name__=="__main__":
    TestRun().run()

Upvotes: 0

Views: 555

Answers (2)

ApuCoder
ApuCoder

Reputation: 2908

You can use class Animation to bring a nice (visually pleasing) effect on removal of some widget.

The idea is to bring down any of widget's size (or sometimes size_hint) property to zero or sufficiently small value (that will be visually negligible) and only then remove the widget. You can also use prop. opacity for even smoother experience.

Below is the implementation of this idea.

First in your kvlang of Item,

        on_press:
            app.root.remove_item(item)
#            app.root.ids.order_list.remove_widget(root)

Then in method remove_item,

    def remove_item(self, item):
        # Create an animation instance with properties (that will be animated)
        # like (widget's) width, opacity etc. with their resp. target values.
        anim = Animation(width = 0, opacity = 0, d = 0.5)
        # Bind the removal callback which will take place
        # as soon as the animation is completed.
        anim.bind(on_complete=lambda *args : self.ids.order_list.remove_widget(item))
        # Now start with the target widget.
        anim.start(item)

Upvotes: 1

John Anderson
John Anderson

Reputation: 39152

As @ApuCoder mentioned, you can do this by using Animation. In your App class you can add methods:

def animate_widget_removal(self, wid):
    # animate shrinking widget width
    anim = Animation(width=0)
    anim.bind(on_complete=self.do_actual_remove)
    anim.start(wid)

def do_actual_remove(self, anim, wid):
    # actually remove the shrunken widget
    self.root.ids.order_list.remove_widget(wid)

And reference the animate_widget_removal() method in your kv:

    on_press:
        app.animate_widget_removal(root)
        # app.root.ids.order_list.remove_widget(root)

And you can animate the addition of widgets by animating the opacity, in the add_item() method:

def add_item(self,image):
    i = Item()
    i.image=image
    i.opacity = 0
    self.ids.order_list.add_widget(i)
    anim = Animation(opacity=1)
    anim.start(i)

Of course, you can animate different or more properties in either case.

Upvotes: 1

Related Questions