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.
Related
I would like to draw something in canvas from .py by using class 'BotRightCanvas'. The initial widgets are all defined in .kv
Problem is when I finally declare BotRightCanvas in .kv, the size of the canvas widget is not coming proper (still 100x100). I would like the green rectangle to coincide with the cyan one.
Here's .py
from kivy.graphics import Color
from kivy.graphics.vertex_instructions import Line, Rectangle, Ellipse
from kivy.metrics import dp
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
class TestOut(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def getSize(self, id):
cSize = id.size
print('win ' + str(Window.size))
print('canvas ' + str(id.size))
print('cSize ' + str(cSize))
class BotRightCanvas(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
with self.canvas:
Color(0, 1, 0, 1)
Line(circle=(self.width / 2, self.height / 2, 25), width=2)
Line(rectangle=(0, 0, self.width, self.height), width=5)
class PlaygroundApp(App):
title = "blabla"
def build(self):
return TestOut()
if __name__ == "__main__":
PlaygroundApp().run()
and .kv
<TestOut>:
BoxLayout:
orientation: 'vertical'
Button:
text:'A'
height: dp(100)
size_hint: 1, None
BoxLayout:
Button:
text:'B'
width: dp(150)
size_hint: None, 1
RelativeLayout:
id: rel_lo
on_size: root.getSize(rel_lo)
Widget:
canvas:
Line:
rectangle: (0,0, self.width, self.height)
width: 2
canvas.before:
Color:
rgba: 0,1,1,1
BotRightCanvas:
I tried printing the final canvas shape in console, seems it initializes with size of 0x0 but later on gets correct size 650x500.
The console output
canvas [0.0, 0]
cSize [0.0, 0]
win (800, 600)
canvas [650.0, 500.0]
cSize [650.0, 500.0]
Any help? Thanks !!
EDIT: Would like to control canvas items from .py
from kivy.graphics import Color
from kivy.graphics.vertex_instructions import Line, Rectangle, Ellipse
from kivy.metrics import dp
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
from kivy.lang.builder import Builder
from kivy.app import App
class TestOut(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def getSize(self, id):
cSize = id.size
print('win ' + str(Window.size))
print('canvas ' + str(id.size))
print('cSize ' + str(cSize))
class BotRightCanvas(Widget):
pass
Builder.load_string(
'''
<BotRightCanvas>:
canvas:
Color:
rgba:0, 1, 0, 1
Line:
circle:self.width / 2, self.height / 2, 25
width:2
Line:
rectangle:0, 0, self.width, self.height
width:5
<TestOut>:
BoxLayout:
orientation: 'vertical'
Button:
text:'A'
height: dp(100)
size_hint: 1, None
BoxLayout:
Button:
text:'B'
width: dp(150)
size_hint: None, 1
RelativeLayout:
id: rel_lo
on_size: root.getSize(rel_lo)
Widget:
canvas:
Line:
rectangle: (0,0, self.width, self.height)
width: 2
canvas.before:
Color:
rgba: 0,1,1,1
BotRightCanvas:
size:rel_lo.size
'''
)
class PlaygroundApp(App):
title = "blabla"
def build(self):
return TestOut()
for line in open('2.py').readlines():
print(' '+line)
if __name__ == "__main__":
PlaygroundApp().run()
I have the following code for a custom knob widget. I am trying to have 6 of these knobs in a layout. But it doesn't seem to work when I wrap the kv code into a CustomKnob#BoxLayout, I followed this answer. Is there a way to replicate the kv code to have 6 knobs, each with their own touch area ?
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.graphics.context_instructions import PushMatrix, PopMatrix, Rotate
from kivy.lang import Builder
from kivy.properties import NumericProperty
import math
kv = '''
<Dial>
canvas:
PushMatrix
Rotate:
angle: root.angle
origin: self.center
# red circle
Color:
rgb: 1,0,0
Line:
circle: (self.center_x, self.center_y, 112, 0, self.angle)
width: 5
#green circle
Color:
rgb: .1, 1, .1
Line:
width: 2
circle: (self.center_x, self.center_y, min(self.width, self.height)/ 8.5)
PopMatrix
Label:
id: lbl
text: str(round(root.angle/360,2))
center: self.parent.center
'''
Builder.load_string(kv)
class Dial(Widget):
angle = NumericProperty(90)
def __init__(self, **kwargs):
super(Dial, self).__init__(**kwargs)
def on_touch_move(self, touch):
y = (touch.y - self.center[1])
x = (touch.x - self.center[0])
temp_calc = math.degrees(math.atan2(y, x))
if temp_calc >= 0:
calc = temp_calc
else:
calc = 360 + temp_calc
self.angle = calc
print(round(self.angle / 360, 2))
class DialApp(App):
def build(self):
return Dial()
if __name__ == "__main__":
DialApp().run()
You can add a rule for CustomKnob in your kv:
<CustomKnob#BoxLayout>:
orientation: 'vertical'
Dial:
id: d1
Dial:
id: d2
Dial:
id: d3
Dial:
id: d4
Dial:
id: d5
Dial:
id: d6
Then, in your build() method:
class DialApp(App):
def build(self):
return Factory.CustomKnob()
The use of Factory is required since CustomKnob is defined in kv.
Note that you still must work out size and positioning of the Dials.
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")
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'
I want the second line to be original color (white). I know I could write 'color: 1,1,1,1' but I would like to use PushMatrix and PopMatrix but it does not work. Where to place PushMatrix and PopMatrix?
Here my sample code:
import kivy
kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
Builder.load_string("""
<GridLayout>:
cols: 2
Label:
PushMatrix:
color: 1,0,0,1
canvas:
Line:
points: self.x, self.y, self.x + self.width, self.y + self.height
PopMatrix:
Widget:
canvas:
Line:
points: self.x, self.y, self.x + self.width, self.y + self.height
""")
class LabelApp(App):
def build(self):
return GridLayout()
if __name__ == '__main__':
LabelApp().run()
Color isn't a Matrix instruction, so PushMatrix and PopMatrix do not affect it. The simple solution is to simply use a second Color instruction.
Also, your syntax is invalid - PushMatrix and PopMatrix are canvas instructions that can only appear under the canvas section in kv.
Edit: To be clear, Color is another canvas instruction that you need to add, e.g.
Color:
rgba: 1, 0, 0, 1