How to switch images after animation? - python

I have a couple of questions:
I have animation of spinning ball which should be always on top of screen and screen should always show only half of it. I can do it but only by clicking buttom to call function which take ball to the right place. I need ball to be always in right place not only then i click the button. I tried to use init function but it always give me this error: 'super' object has no attribute 'getattr'. I also tried to use getattr instead of init but it doesn't take ball to the right place.
So I want click a button and ball start spins for a couple of seconds. When it stops I want ball to be another color or even another image of ball. I tried to use on_complete but I don't understand where I should use it.
My main py file:
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivy.animation import Animation
from kivy.properties import NumericProperty
class OpenScreen(Screen):
angle = NumericProperty(0)
#Trying to make __init__ function
#def __init__(self, **kwargs):
#super(OpenScreen, self).__init__(**kwargs)
#y = self.height
#y1 = self.width / 2
#self.ids.test.pos = 0, y - y1
#Function of taking ball in the right place and spinning it
def z(self):
anim = Animation(angle=360, duration=0.5)
anim += Animation(angle=360, duration=0.5)
anim.start(self)
y = self.height
y1 = self.width / 2
self.ids.test.pos = 0, y - y1
self.ids.test.source = 'redball.png'
def on_angle(self, item, angle):
if angle == 360:
item.angle = 0
...screens classes...
GUI = Builder.load_file('game.kv')
class GameApp(App):
def build(self):
return GUI
def change_screen(self, screen_name):
screen_manager = self.root.ids['screen_manager']
screen_manager.current = screen_name
GameApp().run()
My Openscreen.kv file:
#:kivy 1.10.1
<OpenScreen>:
FloatLayout:
canvas:
Rectangle:
size: self.size
pos: self.pos
source: 'bg.png'
Image:
id: test
size_hint_y: None
height: self.width
source: 'blueball.png'
allow_stretch: True
keep_ratio: False
canvas.before:
PushMatrix
Rotate:
angle: root.angle
axis: 0, 0, 1
origin: self.center
canvas.after:
PopMatrix
Button:
text: "spin"
font_size: self.height - 29
valign: 'middle'
halign: 'center'
padding: 2,2
size_hint: .7, .1
pos_hint:{"x":.15, "y":.585}
background_color: 0,0,0,0
on_press:
root.z()
....
How I can do this?

You can start the animation using Clock.schedule_once() as:
def __init__(self, **kwargs):
super(OpenScreen, self).__init__(**kwargs)
Clock.schedule_once(self.z)
You will need to add a dt argument to the z() method and utilize the on_complete event as:
def z(self, dt):
anim = Animation(angle=360, duration=0.5)
anim += Animation(angle=360, duration=0.5)
anim.bind(on_complete=self.switch_source)
anim.start(self)
Then use a switch_source() method as:
def switch_source(self, *args):
y = self.height
y1 = self.width / 2
self.ids.test.pos = 0, y - y1
self.ids.test.source = 'redball.png'

Related

Kivy- Multiple Animations and placement

can anyone help me with with animation placement and the how to add multiple of animations? So i got the animation of the circle that i need and it work just fine, but not only i need to add another one and smaller. But i also need to place them in specific place as shown on the PS sketch (https://i.stack.imgur.com/AmKMS.png). So if anyone could help me please, ill be really thankful :)
That teh code!
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.animation import Animation
from kivy.properties import NumericProperty
Builder.load_string('''
<Loading>:
canvas.before:
PushMatrix
Rotate:
angle: root.angle
axis: 0, 0, 1
origin: 1150, 480
canvas.after:
PopMatrix
canvas:
Color:
rgba: .1, 1, .1, .9
Line:
width: 2.
circle:
(self.center_x, self.center_y, min(1150, 480)
/ 2, 90, 180, 10)
<Loading2>:
canvas.before:
PushMatrix
Rotate:
angle: root.angle
axis: 0, 0, 1
origin: 1150.480
canvas.after:
PopMatrix
canvas:
Color:
rgba: .1, 1, .1, .9
Line:
width: 2.
circle:
(self.center_x, self.center_y, min(1150, 480)
/ 4, 90, 180, 10)`enter code here`
''')
class Loading(FloatLayout):
angle = NumericProperty(0)
def __init__(self, **kwargs):
super(Loading, self).__init__(**kwargs)
anim = Animation(angle = 360, duration=2)
anim += Animation(angle = 360, duration=2)
anim.repeat = True
anim.start(self)
def on_angle(self, item, angle):
if angle == 360:
item.angle = 0
class Loading2(FloatLayout):
angle = NumericProperty(0)
def __init__(self, **kwargs):
super(Loading, self).__init__(**kwargs)
anim = Animation(angle = 360, duration=2)
anim += Animation(angle = 360, duration=2)
anim.repeat = True
anim.start(self)
def on_angle(self, item, angle):
if angle == 360:
item.angle = 0
class TestApp(App):
def build(self):
return Loading()
return Loading2()
TestApp().run()
A few problems with your code:
The build() method (or any method) can only execute one return, so your second return is ignored.
You have too many arguments to your circle.
The origin of your Rotate is likely off the screen. I think you want to rotate about the circle center.
The super() in Loading2 references Loading instead of Loading2.
There are a couple indentation errors (probably just from copy/paste of code).
So here is a modified version of your code that does most of what you want:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.animation import Animation
from kivy.properties import NumericProperty
kv = '''
FloatLayout:
Loading:
size_hint: 0.3, 1
Loading2:
size_hint: 0.7, 1
pos_hint: {'right':1}
<Loading>:
canvas.before:
PushMatrix
Rotate:
angle: root.angle
axis: 0, 0, 1
origin: root.center
canvas.after:
PopMatrix
canvas:
Color:
rgba: .1, 1, .1, .9
Line:
width: 2.
circle:
(root.center_x, root.center_y, 100, 90, 180, 10)
<Loading2>:
canvas.before:
PushMatrix
Rotate:
angle: root.angle
axis: 0, 0, 1
origin: root.center
canvas.after:
PopMatrix
canvas:
Color:
rgba: .1, 1, .1, .9
Line:
width: 2.
circle:
(root.center_x, root.center_y, 200, 90, 180, 10)
'''
class Loading(FloatLayout):
angle = NumericProperty(0)
def __init__(self, **kwargs):
super(Loading, self).__init__(**kwargs)
anim = Animation(angle = 360, duration=2)
anim += Animation(angle = 360, duration=2)
anim.repeat = True
anim.start(self)
def on_angle(self, item, angle):
if angle == 360:
item.angle = 0
class Loading2(FloatLayout):
angle = NumericProperty(0)
def __init__(self, **kwargs):
super(Loading2, self).__init__(**kwargs)
anim = Animation(angle = 360, duration=2)
anim += Animation(angle = 360, duration=2)
anim.repeat = True
anim.start(self)
def on_angle(self, item, angle):
if angle == 360:
item.angle = 0
class TestApp(App):
def build(self):
return Builder.load_string(kv)
TestApp().run()
I have added a root FloatLayout to your kv, so that both the Loading and Loading2 classes are in your GUI.

use collide_point with a rotated widget

I'm trying to rotate a widget and then use the method collide_point to check if I'm touching the widget or not
The problem is that when I rotate the widget, the canvas (image) works fine, but the widget itself doesn't seems to rotate, so the check fails
.py:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.widget import Widget
class Game(Widget):
pass
def on_touch_down(self, touch):
if self.player.collide_point(*touch.pos):
print("collide")
class TestApp(App):
def build(self):
Window.size = 300, 300
self.game = Game()
return self.game
if __name__ == "__main__":
TestApp().run()
.kv:
<Game>:
player: player
Widget:
id: player
angle: 50
size: 40, 200
canvas:
Color:
rgba: 1,0.5,0,1
Rectangle:
pos: self.pos
size: self.size
canvas.before:
PushMatrix
Rotate:
axis: 0, 0, 1
angle: self.angle
origin: self.right, self.top
canvas.after:
PopMatrix
I expect to print collide when I touch in the image, but only print collide when I touch where the image was before the rotation
edited:
I must do someting like this? (it works, but there is not an easy way?)
def on_touch_down(self, touch):
top_center = Vector(self.player.center_x, self.player.top)
touch_pos = Vector(*touch.pos)
top2touch = touch_pos - top_center
top2touch_rotate = top2touch.rotate(-self.player.angle)
correct_touch = top_center + top2touch_rotate
if self.player.collide_point(*correct_touch):
print("collide")

How to assign a canvas to multiple widgets in Kivy

I would like to give all of the widgets on the screen a white canvas. I know how to create the intended canvas in KV but in this case I must use Python code.
In my code, I tried self.cavas.add(...) and in the code below, I use with self.canvas:. Both attempts resulted in a canvas being drawn in the corner of the screen, rather than inside of the widgets.
How do I put a canvas to be inside of every widget using Python code?
Code:
from kivy.app import App
from kivy.core.window import Window
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.lang import Builder
from random import random
from kivy.uix.button import Button
from kivy.animation import Animation
from kivy.clock import Clock
class LittleButtons(Button):
dur = 2
def reup(self, *args):
Animation.cancel_all(self)
Animation(pos_hint = {'center_x': random(), 'center_y': random()}, duration = self.dur).start(self)
def __init__(self, **kwargs):
super(LittleButtons, self).__init__(**kwargs)
self.pos_hint = {'center_x': random(), 'center_y': random()}
self.size_hint = None, None
self.width = random() * (Window.width / 20)
self.height = self.width
self.background_color = [0,0,0,.05]
with self.canvas:
Color(rgba = [1,1,1,.2])
Rectangle(pos = self.pos, size = self.size)
Animation(pos_hint = {'center_x': random(), 'center_y': random()}, duration = self.dur).start(self)
Clock.schedule_interval(self.reup, self.dur)
KV = Builder.load_string("""
#:import Factory kivy.factory.Factory
Screen:
FloatLayout:
on_parent:
(lambda ltext: [self.add_widget(Factory.LittleButtons(text=ltext)) for i in range (150)])('hi!')
Button:
background_color: 0,0,0,0
canvas:
Color:
rgba: 0,1,1,1
Rectangle:
pos: self.pos
size:self.size
""")
class MyApp(App):
def build(self):
return KV
if __name__ == '__main__':
MyApp().run()
The problem is that in the LittleButtons __init__() method, its position is still the default of (0,0), so the position of the Rectangle is set to (0,0). When you use KV, it cleverly binds the Rectangle position to the LittleButtons position when you reference self.pos. Unfortunately, in a .py file, you must provide that binding yourself. So, here is a modification of your LittleButtons that should handle the position changes:
class LittleButtons(Button):
dur = 2
def reup(self, *args):
Animation.cancel_all(self)
Animation(pos_hint = {'center_x': random(), 'center_y': random()}, duration = self.dur).start(self)
def __init__(self, **kwargs):
self.rect = None
super(LittleButtons, self).__init__(**kwargs)
self.pos_hint = {'center_x': random(), 'center_y': random()}
self.size_hint = None, None
self.width = random() * (Window.width / 20)
self.height = self.width
self.background_color = [0,0,0,.05]
with self.canvas:
Color(rgba = [1,1,1,.2])
self.rect = Rectangle(pos = self.pos, size = self.size)
Animation(pos_hint = {'center_x': random(), 'center_y': random()}, duration = self.dur).start(self)
Clock.schedule_interval(self.reup, self.dur)
def on_pos(self, instance, pos):
if self.rect is not None:
self.rect.pos = pos
The changes are the addition of a self.rect attribute and a on_pos method.
You can add something similar if you need to handle changing size.

Typer Error in Kivy while running a simple pong tutorial

I'm currently learning to use kivy with python. I'm following this tutorial of creating a simple pong game
https://kivy.org/docs/tutorials/pong.html
And when I reach the part where I'm attempting to animate the ball. I wrote the code exactly as the tutorial. I use F5 to run the program from IDLE and I get a
return game()
Type Error: 'PongGame' object is not callable
message in the interactive shell and the window of the game itself freezes. Any thoughts on how to fix this ? Thanks in advance
Here is the code I wrote (exactly as the tutorial):-
code for main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
from random import randint
class PongBall(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
class PongGame(Widget):
ball = ObjectProperty(None)
def serve_ball(self):
self.ball.center = self.center
self.ball.velocity = Vector(4, 0).rotate(randint(0, 360))
def update(self, dt):
self.ball.move()
# bounce off top and bottom
if (self.ball.y < 0) or (self.ball.top > self.height):
self.ball.velocity_y *= -1
# bounce off left and right
if (self.ball.x < 0) or (self.ball.right > self.width):
self.ball.velocity_x *= -1
class PongApp(App):
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
if __name__ == '__main__':
PongApp().run()
code for pong.kv
#:kivy 1.0.9
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongGame>:
ball: pong_ball
canvas:
Rectangle:
pos: self.center_x-5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: "0"
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: "0"
PongBall:
id: pong_ball
center: self.parent.center
In your files (Python and kv) you have several identation errors. Compare your code with the original one and you will find the errors.
For more information regarding identation look here: https://www.python.org/dev/peps/pep-0008/#indentation.

kivy pong tutorial: ''NoneType" has no attribute 'center'

Following the kivy pong tutorial
and I've come across the same problem as OP in this post kivy official pong tutorial: 'NoneType' object has no attribute 'center'
I've attempted this solution to no avail.
When I run this is what I get:
Traceback (most recent call last):
File "main.py", line 58, in <module>
PongApp().run()
File "/usr/local/lib/python2.7/site-packages/kivy/app.py", line 802, in run
root = self.build()
File "main.py", line 52, in build
game.serve_ball()
File "main.py", line 34, in serve_ball
self.ball.center = self.center
AttributeError: 'NoneType' object has no attribute 'center'
main.py:
from kivy.app import App
from kivy.uix.widget import Widget
#import properties and vector
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty
from kivy.vector import Vector
# import clock and randomint
from kivy.clock import Clock
from random import randint
#define the ball
class PongBall(Widget):
#velocity of ball on x and y axis
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
#referencelist property so we can use ball.velocity as
#shorthand, e.g w.pos, x.pos, w.x and w.y
velocity = ReferenceListProperty(velocity_x, velocity_y)
#"move" function will move the ball one step.
#this will be called in equal intervals to animate the ball
def move(self):
self.pos = Vector(*self.velocity) + self.pos
class PongGame(Widget):
ball = ObjectProperty(None)
def serve_ball(self):
self.ball.center = self.center
self.ball.velocity = Vector(4,0).rotate(randint(0,360))
def update(self, dt):
self.ball.move()
#bounce ball off top and bottom
if (self.ball.y < 0) or (self.ball.top > self.height):
self.ball.velocity_y *= -1
#bounce ball of left and right
if (self.ball.x < 0) or (self.ball.right > self.widht):
self.ball.velocity_x += -1
#define base Class for kivy app
#function initializes and returns the Root Widget
class PongApp(App):
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 6.0)
return game
#here the class PongApp is initialized and its run() method called. This initializes and starts our Kivy application.
if __name__ == '__main__':
PongApp().run()
pong.kv:
<PongBall>
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
#draw the pong game canvas and lines
<PongGame>:
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: "0"
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: "0"
PongBall:
id: pong_ball
center: self.parent.center
Go into your kv file, find <PongGame> and pass a value to your ball in python file like this
<PongGame>:
ball: pong_ball
This will make a link between ball and PongBall which has id: pong_ball and then you'll be able to access properties such as velocity, center and so on.
If there's no such a link, an AttributeError will jump out and it means that there's no such a thing in somewhere, in your case there's no center(and many others) in basically None value.

Categories

Resources