1
votes

I am learning to use Kivy, so I walked through the Pong tutorial and started messing around with the code. So, I removed everything but the bouncing ball and decided to generate multiple balls on demand. The problem I am having is that while I can place balls where I want them when application is already running (for example, adding a ball on touch works fine), but when I add balls in the app build() they don't get placed right. Here is the code I have. The balls placed on touch, correctly start from the center. But the ball added in build() starts from the lower left corner. Why? I wanted to add more moving widgets with different properties, but I cannot seem to figure out how to place them on application start.

#:kivy 1.0.9
<World>:
    canvas:
        Ellipse:
            pos: self.center
            size: 10, 10

<Agent>:
    size: 50, 50
    canvas:
        Ellipse:
            pos: self.pos
            size: self.size
from random import randint

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty, ListProperty
from kivy.vector import Vector
from kivy.clock import Clock


class World(Widget):
    agents = ListProperty()

    def add(self):
        agent = Agent()
        agent.center = self.center
        agent.velocity = Vector(4, 0).rotate(randint(0, 360))
        self.agents.append(agent)
        self.add_widget(agent)

    def on_touch_down(self, touch):
        self.add()

    def update(self, dt):
        for agent in self.agents:
            agent.move()
            if agent.y < 0 or agent.top > self.height:
                agent.velocity_y *= -1
            if agent.x < 0 or agent.right > self.width:
                agent.velocity_x *= -1


class Agent(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 WorldApp(App):
    def build(self):
        world = World()
        # add one ball by default
        world.add()
        Clock.schedule_interval(world.update, 1.0/60.0)
        return world


if __name__ == '__main__':
    WorldApp().run()
1

1 Answers

2
votes

Found the answer. The default widget size is 100, 100. By the time I add the initial ball, the World widget is not rendered and therefore has a default size. But it is possible to pass the windows size in the Widget constructor. So changing the World instantiation to

world = World(size=Window.size)

solved the problem