Question

Clutter is not doing the full animation.

This is my current code:

from gi.repository import Clutter, Gtk
import sys

def onClick(actor, event):
    actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [280])  # clutter does not seem to be running this line
    actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [20])

def main():
    Clutter.init(sys.argv)

    # Colors
    red = Clutter.Color().new(255, 0, 0, 255)
    black = Clutter.Color().new(0, 0, 0, 255)

    # Create Stage
    stage = Clutter.Stage()
    stage.set_title("Basic Usage")
    stage.set_size(400, 200)
    stage.set_color(black)

    # Rectangle Actor
    actor = Clutter.Rectangle()
    actor.set_size(100, 50)
    actor.set_position(150, 100)
    actor.set_color(red)
    actor.set_reactive(True)
    actor.connect("button-press-event", onClick)

    # Add Actor to the Stage
    stage.add_actor(actor)
    stage.connect("destroy", lambda w:  Clutter.main_quit())
    stage.show_all()

    Clutter.main()

if __name__ == '__main__':
    main()

Behold this illustration of my problem:

enter image description here

For those of you who don't like gifs, here is my problem described in words: I want the actor to move from the middle to the right, then all the way to the left. Instead it just moves from the middle straight to the left.

What is causing this, and how can I fix it?

Was it helpful?

Solution

like the documentation for ClutterActor.animate() says:

Calling this function on an actor that is already being animated will cause the current animation to change with the new final values, the new easing mode and the new duration https://developer.gnome.org/clutter/stable/clutter-Implicit-Animations.html#clutter-actor-animate

which means that the following code:

actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [280])
actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [20])

is exactly equivalent to:

actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [20])

which is what you're seeing.

if you want to chain up two animations you have to connect to the completed signal of ClutterAnimation, using the connect_after function, so that Clutter can create a new animation:

def moveLeft (animation, actor):
    actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [20])

actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [280]).connect_after('completed', moveLeft)

I'd like to point out that animatev() and ClutterAnimation are deprecated; they can be replaced by using an explicit Clutter.KeyframeTransition or an implicit transition, for instance:

from gi.repository import Clutter

Clutter.init(None)

stage = Clutter.Stage()
stage.connect('destroy', lambda x: Clutter.main_quit())

actor = Clutter.Actor()
actor.set_background_color(Clutter.Color.get_static(Clutter.StaticColor.RED))
actor.set_reactive(True)
actor.set_size(32, 32)
stage.add_child(actor)
actor.set_position(82, 82)

def moveLeft(actor):
    actor.set_x(20)

def moveRight(actor):

    actor.set_easing_duration(1000)
    actor.set_easing_mode(Clutter.AnimationMode.LINEAR)
    actor.set_x(280)
    actor.connect('transition-stopped::x', lambda a, n, t: moveLeft(actor))

actor.connect('button-press-event', lambda a, e: moveRight(actor))

stage.show()
Clutter.main()

it can be arbitrarily more complex than this; you also need to remember to disconnect the transition-stopped::x signal handler, and restore the easing state to avoid creating implicit animations every time you change the actor's state, but I'll leave that as an exercise to the reader.

OTHER TIPS

Try following code:

def onClick(actor, event):
    animation1 = actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [280])
    animation1.connect_after(
        'completed',
        lambda animation: actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [20])
    )

When you do these to lines immediately after each other

def onClick(actor, event):
    actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [280])
    actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [20])

Clutter does them both without waiting for the other to finish. This means that the first command has almost no time to move the agent at all before the second command takes over.

Here is an example of using the "completed" signal:

def onClick(actor, event):
    animate(actor)

def animate(actor):
    firstAnimation = actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [280]) 

    firstAnimation.connect_after("completed", moveLeft)

def moveLeft():
    self.actor.animatev(Clutter.AnimationMode.LINEAR, 1000, ["x"], [20])

Here is the documentation on clutter animations
Here is the documentation on the "completed" signal
Here is some working example code

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top