Mark Kortink
Mark Kortink

Reputation: 2120

Animate images on slides in a Kivy carousel

I have a Kivy carousel and each slide contains a float layout with an image and some labels on it. When I move to the next slide I want the image to animate. I am not using KV language for specific reasons, I am doing everything in python script.

enter image description here

I can get animation working so long as I don't try to position the widget I want to animate within the float layout. As soon as I position the widget it will not animate anymore.

Clearly positioning the widget locks it in place and it can't move anymore so can't be animated. How do I get the effect that I want?

Here is some working code that illustrates the problem.

import kivy     
from kivy.app import App  
from kivy.uix.carousel import Carousel 
from kivy.uix.image import AsyncImage  
from kivy.animation import Animation
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout

def animate():
    animation = Animation(pos=(20, 0), t='out_bounce')
    animation += Animation(pos=(-40, 0), t='out_bounce')
    animation += Animation(pos=(0, 0), t='out_bounce')
    return animation

class MyCarousel(Carousel):
    # This class is a carousel that runs script
    # when a slide gets focus (except first load).
    def on_index(self, *args):
        print('the slide is', self.index)
        # 1 is image, 0 is label
        animate().start(self.current_slide.children[1])
        Carousel.on_index(self, *args)

class CarouselApp(App): 
    def build(self): 
        # Set carousel widget as root 
        root = MyCarousel()  

        # Adding slides 
        for i in range(3): 
            flo = FloatLayout() # to test nesting animation
            src = "https://via.placeholder.com/480x270.png&text=slide-%d" %i             
            image = AsyncImage(source = src, allow_stretch = True) 
            hello = Label(text='Hello', font_size=50)

            # THESE KILL ANIMATION -----------------
            # image.pos_hint = {'x': 0.25, 'y': 0.25}
            # hello.pos_hint = {'bottom': 1, 'left': 1}
            # --------------------------------------

            image.size_hint = (0.5, 0.5)
            hello.size_hint = (0.25, 0.25)
            flo.add_widget(image)
            flo.add_widget(hello)
            root.add_widget(flo) 

        return root 
  
# run the App 
if __name__ == '__main__': 
    #breakpoint()
    app = CarouselApp()
    app.run() 

If you run this script it will animate the image. Changing the self.current_slide.children[k] subscript will animate the label. But as soon as you uncomment the pos_hint parameters the animation won't work anymore.

Upvotes: 0

Views: 587

Answers (1)

John Anderson
John Anderson

Reputation: 38962

To animate pos_hint, you must specify that property when you create the animation. For example:

def animate():
    animation = Animation(pos_hint={'x':.5, 'y':0}, t='out_bounce')
    animation += Animation(pos_hint={'x':-.2, 'y':0}, t='out_bounce')
    animation += Animation(pos_hint={'x':0, 'y':0}, t='out_bounce')
    return animation

Now you can use the pos_hint to specify the starting position, as your commented out code does:

        image.pos_hint = {'x': 0.25, 'y': 0.25}
        hello.pos_hint = {'bottom': 1, 'left': 1}

Three things to note:

  1. The pos_hint keys bottom and left are not valid. Legal keys are ‘x’, ‘right’, ‘center_x’, ‘y’, ‘top’ and ‘center_y’.
  2. You must animate the keys that you initially use to position the widget. If you animate the center_x hint, but center_x is not in the initial pos_hint, then it will have no effect.
  3. The pos_hint takes precedence over pos, so animating pos of a widget that is originally positioned using pos_hint, will have no effect.

Upvotes: 1

Related Questions