D.Thomas
D.Thomas

Reputation: 359

I it posible to refer from one widget in the kivi language to properties of an other widget?

I'm starting with kivy playing around in the tutorials I'm at the end of the Pong tutorial and want to add the 'Winner' label based on the score.

In order to do so, I styled a winner label:

<Winner>:
    Label:
        font_size: 200
        #center_x: self.parent.width * .5
        #center_y: self.parent.top - .5
        text: "wINNER!"

I would like to place this label in the middle, based on the dimensions of the Pong game but it ends up somewhere in the lower left.

I also tried adding the widget directly in python (see update method), but then i still don't know how to do the placement dynamically, like how the other labels with the scores are placed in the PongGame in the kivy file.

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.properties import (
    NumericProperty, ReferenceListProperty, ObjectProperty
)
from kivy.vector import Vector
from kivy.clock import Clock


class PongPaddle(Widget):
    score = NumericProperty(0)

    def bounce_ball(self, ball):
        if self.collide_widget(ball):
            vx, vy = ball.velocity
            offset = (ball.center_y - self.center_y) / (self.height / 2)
            bounced = Vector(-1 * vx, vy)
            vel = bounced * 1.1
            ball.velocity = vel.x, vel.y + offset


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 Winner(Widget):
    pass



class PongGame(Widget):
    ball = ObjectProperty(None)
    player1 = ObjectProperty(None)
    player2 = ObjectProperty(None)

    def serve_ball(self, vel=(4, 0)):
        self.ball.center = self.center
        self.ball.velocity = vel

    def update(self, dt):
        self.ball.move()

        # bounce of paddles
        self.player1.bounce_ball(self.ball)
        self.player2.bounce_ball(self.ball)

        # bounce ball off bottom or top
        if (self.ball.y < self.y) or (self.ball.top > self.top):
            self.ball.velocity_y *= -1

        # went of to a side to score point?
        if self.ball.x < self.x:
            self.player2.score += 1
            self.serve_ball(vel=(4, 0))
        if self.ball.x > self.width:
            self.player1.score += 1
            self.serve_ball(vel=(-4, 0))
        if self.player1.score>0:
            Clock.unschedule(self.update)
            self.add_widget(Winner())
            # self.add_widget(text='bla',center_y=self.parent.top-50)
            self.add_widget(Label(text='bla',center_y=self.parent.top-50))
        if self.player2.score>0:
            Clock.unschedule(self.update)
            self.add_widget(Winner())
            self.add_widget(Label(text='bla',center_y=self.parent.top-50))
    def on_touch_move(self, touch):
        if touch.x < self.width / 3:
            self.player1.center_y = touch.y
        if touch.x > self.width - self.width / 3:
            self.player2.center_y = touch.y


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()

kivy

#:kivy 1.0.9

<PongBall>:
    size: 50, 50
    canvas:
        Ellipse:
            pos: self.pos
            size: self.size

<Winner>:
    Label:
        font_size: 200
        center_x: self.parent.width * .5
        center_y: self.parent.top - .5
        text: "wINNER!"

<PongPaddle>:
    size: 20, 200
    canvas:
        Rectangle:
            pos: self.pos
            size: self.size

<PongGame>:
    ball: pong_ball
    player1: player_left
    player2: player_right

    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: str(root.player1.score)

    Label:
        font_size: 70
        center_x: root.width * 3 / 4
        top: root.top - 50
        text: str(root.player2.score)

    Label:
        font_size: 70
        center_x: root.width * .5
        top: root.top - 50
        text: "Winner!"

    PongBall:
        id: pong_ball
        center: self.parent.center

    PongPaddle:
        id: player_left
        x: root.x
        y: root.center_y

    PongPaddle:
        id: player_right
        x: root.width-self.width
        center_y: root.center_y

How to refer to the properties of the parent widget? Thanks!

Upvotes: 1

Views: 538

Answers (1)

ikolim
ikolim

Reputation: 16011

The following enhancements are required in the kv file and Python script to solve the problem.

kv file

  • Use app.root to refer to the root widget.

Kv language » Rule context

There are three keywords specific to the Kv language:

  • app: always refers to the instance of your application.
  • root: refers to the base widget/template in the current rule
  • self: always refer to the current widget

Kivy Language » Value Expressions, on_property Expressions, ids, and Reserved Keywords

self

The keyword self references the “current widget instance”:

Button:
    text: 'My state is %s' % self.state

root

This keyword is available only in rule definitions and represents the root widget of the rule (the first instance of the rule):

<MyWidget>:
    custom: 'Hello world'
    Button:
        text: root.custom

app

This keyword always refers to your app instance. It’s equivalent to a call to kivy.app.App.get_running_app() in Python.

Label:
    text: app.name

Snippets - kv file

<Winner>:
    font_size: 200
    center: app.root.center
    text: "WINNER!"

py file

  • Change the inheritance of the class Winner() from Widget to Label because we don't really need two widgets and it will reduce resource usage e.g. memory, size of app, etc.
  • Add the Winner object.

Snippets - py file

class Winner(Label):
    pass


class PongGame(Widget):
    ...
    def update(self, dt):
        self.ball.move()
        ...
        if self.player1.score > 0:
            self.add_widget(Winner())
        if self.player2.score > 0:
            self.add_widget(Winner())

    def on_touch_move(self, touch):
        ...

Output

Result

Upvotes: 1

Related Questions