i am trying to start an animation when the app is first loaded. I.E. immediately after the loading screen has closed. I have tired the "on_enter" event but it doesn't seem to work, any help would be much appreciated.
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.animation import Animation
from kivy.properties import ListProperty
from kivy.core.window import Window
from random import random
from kivy.graphics import Color, Rectangle
Builder.load_string('''
<Root>:
AnimRect:
pos: 500, 300
<AnimRect>:
on_enter: self.start_animation
canvas:
Color:
rgba: 0, 1, 0, 1
Rectangle:
pos: self.pos
size: self.size
''')
class Root(Widget):
pass
class AnimRect(Widget):
def anim_to_random_pos(self):
Animation.cancel_all(self)
random_x = random() * (Window.width - self.width)
random_y = random() * (Window.height - self.height)
anim = Animation(x=random_x, y=random_y,
duration=4,
t='out_elastic')
anim.start(self)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.anim_to_random_pos()
def start_animation(self, touch):
if self.collide_point(*touch.pos):
self.anim_to_random_pos()
runTouchApp(Root())
The on_enter method is defined in the Screen, not in the Widget. You should put that rectangle on a screen (the Root widget should be a screen here), and once the screen's on_enter event fires, start the rectangle animation.
Also, you are calling it incorrectly; function call should contain brackets, i.e. on_enter: self.start_animation()
Does it look like something you wanted to have?
I just deleted the "on_enter" line in your kv, and corrected your indentation.
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.animation import Animation
from kivy.properties import ListProperty
from kivy.core.window import Window
from random import random
from kivy.graphics import Color, Rectangle
Builder.load_string('''
<Root>:
AnimRect:
pos: 500, 300
<AnimRect>:
canvas:
Color:
rgba: 0, 1, 0, 1
Rectangle:
pos: self.pos
size: self.size
''')
class Root(Widget):
pass
class AnimRect(Widget):
def anim_to_random_pos(self):
Animation.cancel_all(self)
random_x = random() * (Window.width - self.width)
random_y = random() * (Window.height - self.height)
anim = Animation(x=random_x, y=random_y,
duration=4,
t='out_elastic')
anim.start(self)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.anim_to_random_pos()
def start_animation(self, touch):
if self.collide_point(*touch.pos):
self.anim_to_random_pos()
runTouchApp(Root())
Related
I'm doing a tutorial about making a game, and still in the background period, making moving textures. But I just wonder why the wrapped problem fixed by %Window.width in the line self.cloud_texture.uvpos = {(self.cloud_texture.uvpos[0] + time_passed)%Window.width ,self.cloud_texture.uvpos[1]}
I see no problem when I just do self.cloud_texture.uvpos[0] + time_passed only, but the result told me the difference when it's changing the self.cloud_texture.uvpos[1] too, and starting to get some glitch when resizing window after few seconds.
Can someone give me an explanation so I can apply it to my other apps?
.py file
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.properties import ObjectProperty
from kivy.core.window import Window
class Background(Widget):
cloud_texture = ObjectProperty(None)
def __init__(self, **kwargs):
super().__init__(**kwargs)
#create texture
self.cloud_texture = Image(source="assets/cloud.png").texture
self.cloud_texture.wrap = "repeat"
self.cloud_texture.uvsize = {Window.width / self.cloud_texture.width, -1}
def scroll_textures(self,time_passed):
self.cloud_texture.uvpos = {(self.cloud_texture.uvpos[0] + time_passed)%Window.width ,self.cloud_texture.uvpos[1]}
texture = self.property("cloud_texture")
texture.dispatch(self)
from kivy.clock import Clock
class MainApp(App):
def on_start(self):
Clock.schedule_interval(self.root.ids.background.scroll_textures, 1/60)
pass
if __name__ == "__main__":
MainApp().run()
.kv file
FloatLayout:
Background:
id: background
canvas.before:
Rectangle:
size: self.size
pos: self.pos
source: "assets/sky.png"
Rectangle:
size: self.width, 138
pos: self.pos[0], self.pos[1] + self.height - 138
texture: self.cloud_texture
You can find the images I'm using here
I would like to make a puzzle where someone has to follow a maze and collect letters which have to be typed in later as an answer. (Not collected by the program but just remembering which letters are passed and typing it in later while exiting the maze.)
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.config import Config
Config.set('graphics', 'width', '806')
Config.set('graphics', 'height', '706')
class Touch(Widget):
def __init__(self, **kwargs):
super(Touch, self).__init__(**kwargs)
def on_touch_down(self, touch):
pass
def on_touch_down(self, touch):
pass
def on_touch_down(self, touch):
pass
class MyGrid(GridLayout):
pass
class DrawingWindow(App):
def build(self):
return MyGrid()
if __name__ == "__main__":
DrawingWindow().run()
<MyGrid>:
GridLayout:
cols:1
size:1000,706
canvas:
Rectangle:
size: 808,706
source: 'Puzzle.png'
I have found out how to draw a circle where your mouse is (not added to the code as I think a different method is probably going to be used), but how to make everything around a certain radius black so that it only reveals a small circle within this radius. Maybe using opacity within a radius is a sollution. If someone could push me in the right direction, that would be really helpful.
It does not have to stay revealed, rather not actually. Hopefully that makes it somewhat easier as well.
I hope I've stated clearly what the problem is. Thank you in advance for the time and effort.
You can use Stencil Instructions to accomplish that. Here is a simple example:
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.properties import NumericProperty, ListProperty
from kivy.uix.widget import Widget
kv = '''
RelativeLayout:
ImageWithHole:
id: hole
canvas:
StencilPush
Ellipse: # draw the hole
pos: self.hole_pos[0] - self.radius, self.hole_pos[1] - self.radius
size: self.radius*2, self.radius*2
StencilUse
Color:
rgba: 1,1,1,1
Rectangle:
source: 'Puzzle.png' # draw the maze
pos: 0,0
size: self.size
StencilUnUse
Ellipse: # erase the hole (must be identical to original draw above)
pos: self.hole_pos[0] - self.radius, self.hole_pos[1] - self.radius
size: self.radius*2, self.radius*2
StencilPop
'''
class ImageWithHole(Widget):
radius = NumericProperty(50)
hole_pos = ListProperty([400, 300])
class TestApp(App):
def build(self):
Window.bind(mouse_pos=self.on_motion)
return Builder.load_string(kv)
def on_motion(self, src, mouse_pos):
hole = self.root.ids.hole
hole.hole_pos = mouse_pos
TestApp().run()
I'm trying to create pulsing background in my app. Following this question Pulsing Background Color in Kivy
i've tried to make it work also, but for some reason it doesn't work. Here's my main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.core.window import Window
from kivymd.app import MDApp
from kivy.uix.image import Image
from kivy.animation import Animation
from kivy.clock import Clock
Window.size = (360,600)
class MyGridLayout(Screen):
def blink(self):
x = Clock.schedule_once(self.start_pulsing,2)
return x
def start_pulsing(self,*args):
anim = Animation(animated_color=(1,0,0,1))
anim.start(self)
anim.repeat = True
class mainApp(App):
def build(self):
return MyGridLayout()
if __name__ == '__main__':
mainApp().run()
And my main.kv file
<MyGridLayout>:
FloatLayout:
size: root.width, root.height
animated_color:(1,1,1,1)
canvas.before:
Color:
rgba :self.animated_color
Rectangle:
pos: self.pos
size: self.size
Any help will be much appreciated
A couple problems with your code:
The animated_color property in your kv is a property of the FloatLayout, but your python code references it as a property of MyGridLayout.
The anim.repeat = True will cause the Animation to be repeated, but after the first animation, the animated_color is already (1,0,0,1), so the repeated Animation will have no effect.
I don't see any code calling the blink() method to actually start the pulsing.
Here is a modified version of your code that reflects the above changes:
from kivy.app import App
from kivy.properties import ColorProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.core.window import Window
from kivy.animation import Animation
from kivy.clock import Clock
Window.size = (360,600)
class MyGridLayout(Screen):
animated_color = ColorProperty() # the color property to be animated
pulse_interval = 4 # the interval for pulsing
def blink(self):
x = Clock.schedule_once(self.start_pulsing,2)
return x
def start_pulsing(self,*args):
d = self.pulse_interval /2
anim = Animation(animated_color=(1,0,0,1), duration=d) + \
Animation(animated_color=(1,1,1,1), duration=d)
anim.repeat = True
anim.start(self)
class mainApp(App):
def build(self):
root = MyGridLayout()
root.blink() # star the pulsing
return root
if __name__ == '__main__':
mainApp().run()
And the modified main.kv:
<MyGridLayout>:
FloatLayout:
size: root.width, root.height
# animated_color:(1,1,1,1) # property needs to be attribute of MyGridLayout
canvas.before:
Color:
rgba :root.animated_color # reference the animated_color property of MyGridLayout
Rectangle:
pos: self.pos
size: self.size
I am trying to initialize the widget class by placing a button at the root.center. Strangely, root.size during initialization is only (100,100). When it's rendered, the actual size is (600,800). So my button is placed in the left bottom instead of in the center.
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.uix.widget import Widget
w = Builder.load_string('''
<MyWidget>:
canvas.before:
Color:
rgba: 1, 0.3, 0.3, 1
Rectangle:
size: self.size
pos: self.pos
Button:
id: score_button
center: root.score_button_center
text:'Best Scores'
''')
class MyWidget(Widget):
score_center_y = NumericProperty(-100)
score_center_x = NumericProperty(-100)
score_button_center = ReferenceListProperty(score_center_x, score_center_y)
hide_center = (-100, -100)
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.score_button_center = self.width, self.height
class MyApp(App):
def build(self):
g = MyWidget()
return g
if __name__ == '__main__':
MyApp().run()
Just change:
center: root.score_button_center
to:
center: root.center
That will take advantage of the kivy feature that sets up bindings to adjust the Button position whenever the root widget changes size or position.
I'm attempting to drag some images arranged inside a GridLayout (which is indeed inside a ScrollView) to outer Layout.
The image to be dragged has its on_touch_down event defined, when image is clicked parent is changed from WidgetMenu to MainLayout so it can be dragged between those widgets. The current problem is when I touch the image, DragBehavior is lost.
The full code:
import kivy
kivy.require("1.9.1")
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.scrollview import ScrollView
from kivy.uix.widget import Widget
from kivy.uix.behaviors import DragBehavior
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.base import runTouchApp
from kivy.lang import Builder
Builder.load_string('''
<WidgetMenu>:
canvas.before:
Color:
rgb: 0.9,0.5,0.3
RoundedRectangle:
pos:self.pos
size: self.size
radius: [20,]
orientation: "vertical"
padding:30
ScrollView:
GridLayout:
cols:1
size_hint_y:None
row_default_height:root.height*.15
height:self.minimum_height
DragImage:
DragImage:
DragImage:
<DragImage>:
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 100000000
drag_distance: 0
size_hint:None,None
size:234,34
canvas:
Color:
rgb:1,0,1
Rectangle:
pos: self.pos
size: self.size
<MainLayout>:
canvas:
Color:
rgb:1,1,1
Rectangle:
size: self.size
pos: self.pos
WidgetMenu:
size_hint: 0.35,0.9
''')
class MainLayout(FloatLayout):
pass
class WidgetMenu(BoxLayout):
pass
class DragImage(DragBehavior,FloatLayout):
def on_touch_down(self,touch):
workspace = self.parent.parent.parent.parent
grid = self.parent
menu = self.parent.parent.parent
if "MainLayout" in str(workspace):
grid.remove_widget(self)
workspace.remove_widget(menu)
self.pos = Window.mouse_pos
workspace.add_widget(self)
return True
class ScrollApp(App):
def build(self):
return MainLayout()
ScrollApp().run()
Please help.
Two problems with your code are that you are not calling the super method in your on_touch_down, and placing the DragImage in the top level MainLayout changes the pos of the DragImage, thus changing its DragRectangle. That change in DragRectangle leaves the touch.pos outside of it, so DragBehavior thinks the touch is not on the DragImage.
I have fixed both those problems by calling the super method and changing the touch.pos before passing it to the super method. I also added some code to keep the DragImage in the same position relative to the mouse when it is clicked. Also added a call to self.collide_point() in order to ignore clicks not on the DragImage.
class DragImage(DragBehavior,FloatLayout):
def on_touch_down(self,touch):
if not self.collide_point(*touch.pos):
return False
workspace = self.parent.parent.parent.parent
grid = self.parent
menu = self.parent.parent.parent
if "MainLayout" in str(workspace):
grid.remove_widget(self)
workspace.remove_widget(menu)
# the following code assumes that workspace is the entire Window
self.x = Window.mouse_pos[0] - (touch.pos[0] - self.x)
self.y = Window.mouse_pos[1] - (touch.pos[1] - self.y)
workspace.add_widget(self)
touch.pos = Window.mouse_pos
return super(DragImage, self).on_touch_down(touch)