Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
LabelSprite rotate_by problem
-
Hello everyone,
Newbie question : I am trying to create an animation of a label moving like a pendulum. I tried using a SpriteLabel rotating around an arbitrary point using Action.rotate_by, but it always rotates around the lower left corner of the screen (0, 0) and not around the top center, is there any way around this ?
Here is the exact code I am using:from scene import * from math import * class MyScene(Scene): def setup(self): self.background_color = 'black' self.anchor_point = (0.5, 0.0) self.timelabel = LabelNode(position=self.size / 2, text='time') self.add_child(self.timelabel) self.timelabel.rotation = -pi / 8.0 self.run_action(Action.repeat(Action.sequence(Action.rotate_by(pi / 4.0, 1, TIMING_EASE_IN_OUT), Action.rotate_by(-pi / 4.0, 1, TIMING_EASE_IN_OUT), 0), 0)) run(MyScene(), PORTRAIT)
Cheers
Andre -
Is this close to the behavior you want?
from scene import * from math import * class MyScene(Scene): def setup(self): self.background_color = 'black' self.time_anchor = Node(position=(self.size.w/2, 0), parent=self) self.timelabel = LabelNode(position=(0, self.size.h/2), text='time') self.time_anchor.add_child(self.timelabel) rotate_action = Action.repeat(Action.sequence(Action.rotate_to(pi / 4.0, 1, TIMING_EASE_IN_OUT), Action.rotate_to(-pi / 4.0, 1, TIMING_EASE_IN_OUT), 0), 0) self.time_anchor.run_action(rotate_action) run(MyScene(), PORTRAIT)
Things I've changed:
- Added
time_label
to a containerNode
(time_anchor
), so it can be rotated around an arbitrary point without rotating the whole scene (your approach of rotating the scene itself becomes problematic when you want to add more things to the scene; also, theanchor_point
attribute is ignored for scenes) - Changed
rotate_by
torotate_to
-- this is just a guess, but I think the resulting effect may be closer to what you're after.
- Added
-
That is exactly what I wanted omz, thanks alot!
Cheers
Andre -
Hi @omz,
is there a way to include a dynamic variable in an Action?
I was thinking of using the variable self.angle in the Action like this:
from itertools import cycle from scene import * from math import * class MyScene(Scene): def setup(self): self.background_color = 'black' self.time_anchor = Node(position=(self.size.w/2, 0), parent=self) self.timelabel = LabelNode(position=(0, self.size.h/2), text='time') self.time_anchor.add_child(self.timelabel) self.angles = cycle([pi/4, -pi/4]) rotate_action = Action.repeat(Action.rotate_to(self.angles.next(), 1, TIMING_EASE_IN_OUT), 0) self.time_anchor.run_action(rotate_action) run(MyScene(), PORTRAIT)
This does not seem to work it stays at the same angle all the time, is there any way around it?
Thanks in advance.Cheers
Andre -
One way to do is to write your own 'Action.repeat".. In the following code, the angles get changed when you touch. May be others could suggest better ways to this.
from scene import * from math import * class MyScene(Scene): def repeat(self): self.rotate_action = Action.sequence( Action.rotate_to(self.angles[0], 1, TIMING_EASE_IN_OUT), Action.rotate_to(self.angles[1], 1, TIMING_EASE_IN_OUT), Action.call(self.repeat), 0) self.time_anchor.run_action(self.rotate_action) def setup(self): self.background_color = 'black' self.time_anchor = Node(position=(self.size.w/2, 0), parent=self) self.timelabel = LabelNode(position=(0, self.size.h/2), text='time') self.time_anchor.add_child(self.timelabel) self.angles = (pi/8, -pi/8) self.rotate_action = Action.sequence( Action.rotate_to(self.angles[0], 1, TIMING_EASE_IN_OUT), Action.rotate_to(self.angles[1], 1, TIMING_EASE_IN_OUT), Action.call(self.repeat), 0) self.time_anchor.run_action(self.rotate_action) self.toggle = True def touch_began(self, touch): if self.toggle: self.angles = (pi/4, -pi/4) else: self.angles = (pi/8, -pi/8) self.toggle = not self.toggle run(MyScene(), PORTRAIT)
-
Try this code, you can easily define the fps (frame per second), you work in degrees and you can define min/max, and you can have more interaction than with run_action:
from scene import * from math import * class MyScene(Scene): def setup(self): self.background_color = 'black' self.timelabel = LabelNode(position=self.size/2, text='time') self.add_child(self.timelabel) self.ang = -45 self.delta = 1 def update(self): self.ang = self.ang + self.delta if abs(self.ang) > 45: self.delta = -self.delta self.ang = self.ang + self.delta self.timelabel.rotation = math.radians(self.ang) r = self.size[1]/2 x = self.size[0]/2+r*sin(math.radians(-self.ang)) y = r*cos(math.radians(self.ang)) self.timelabel.position = (x,y) run(MyScene(), PORTRAIT,frame_interval=1)
-
You can change the first line of the update function to the following code if you want TIMING_EASE_IN_OUT like effect
self.ang = self.ang + self.delta*cos(radians(2*self.ang)) +.05*self.delta
Note that it assumes the maximum angle is 45. If it is different, you need to change 2*self.ang to appropriate expression.
-
Thank you @abcabc,
creating a new action is great, do I need to do aself.time_anchor.remove_all_actions ()
before?
Cheers
Andre -
Thanks @cvp,
I was trying to learn automatic actions so I had not considered your approach, I guess I was being lazy :-)
Cheers
Andre -
No problem, I had never used scene thus I'm always happy to try and thus to learn something. Good luck
-
I think that it is not needed since the previous action will be complete by that time. I assume that "run_action" call spawns a thread and
the Action.call does not wait for the thread to complete and it returns immediately. I hope that run_action call overheads are not much.I do not know the internal implementation details and may be omz can answer this. -
"run_action" may not be spawning a thread and it may just create action related data structures. which will be updated every frame. Anyway other experts ( I am just learning) could help you on this.
-
Well, it's running just fine!
Thanks to all!
Cheers
Andre