Kivy - adding and removing labels

I'm new to Kivy, and can't find any real answers about adding and removing labels. I worked my way through the Pong tutorial (as you might have guessed), and adapted it for 4 players. Now, I'm trying to add a label to display the text Winner!, and to click on that text to restart the game.

Up to there, it's working. Problem is, I can't get the label to disappear again after the new game starts. Also, I don't understand the formatting - I can't seem to make my label bigger or move it lower in the panel.

I'm going to post all the code, since hopefully you'd like to play the game anyway (once it's fixed), and I'm positive there's a better, less janky way to add and remove the text.

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
from random import randint


class PongPaddle(Widget):
    score = NumericProperty(0)
    orientation = ObjectProperty([0, 0])
    can_move = ObjectProperty(0)

    def bounce_ball(self, ball):
        if self.collide_widget(ball):
            vx, vy = ball.velocity
            if self.orientation[0] == 25:                
                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
            else:
                offset = (ball.center_x - self.center_x) / (self.width / 2)
                bounced = Vector(vx, -1 * vy)
                vel = bounced * 1.1
                ball.velocity = vel.x + offset, vel.y


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)
    player1 = ObjectProperty(None)
    player2 = ObjectProperty(None)
    player3 = ObjectProperty(None)
    player4 = ObjectProperty(None)

    def initialize(self):
        SCORE = 1
        self.player1.orientation = [25, 200]
        self.player2.orientation = [25, 200]
        self.player3.orientation = [200, 25]
        self.player4.orientation = [200, 25]
        self.player1.score = SCORE
        self.player2.score = SCORE
        self.player3.score = SCORE
        self.player4.score = SCORE
        self.player1.can_move = 1
        self.player2.can_move = 1
        self.player3.can_move = 1
        self.player4.can_move = 1
        self.serve_ball()

    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)
        self.player3.bounce_ball(self.ball)
        self.player4.bounce_ball(self.ball)

        #bounce ball off bottom or top
        if ((self.ball.y < self.y) and not self.player3.can_move) \
           or ((self.ball.top > self.top) and not self.player4.can_move):
            self.ball.velocity_y *= -1
        if ((self.ball.x < self.x) and not self.player1.can_move) \
           or ((self.ball.right > self.width) and not self.player2.can_move):
            self.ball.velocity_x *= -1

        #went off to a side to score point?
        if self.ball.x < self.x and self.player1.can_move == 1:
            self.player1.score -= 1
            self.serve_ball(vel=(4, randint(1, 4)))
            if self.player1.score <= 0:
                self.player1.can_move = 0
        elif self.ball.x > self.width and self.player2.can_move == 1:
            self.player2.score -= 1
            self.serve_ball(vel=(-4, randint(1, 4)))
            if self.player2.score <= 0:
                self.player2.can_move = 0
        elif self.ball.y > self.height and self.player4.can_move == 1:
            self.player4.score -= 1
            self.serve_ball(vel = (randint(1, 4), -4))
            if self.player4.score <= 0:
                self.player4.can_move = 0
        elif self.ball.y < self.y and self.player3.can_move == 1:
            self.player3.score -= 1
            self.serve_ball(vel = (randint(1, 4), 4))
            if self.player3.score <= 0:
                self.player3.can_move = 0

        if self.player1.can_move + self.player2.can_move + \
           self.player3.can_move + self.player4.can_move == 1:
            self.ball.velocity = (0, 0)
            global win_label
            win_label = Label(size_hint=(None, None),
                              text='[ref=winner]Winner![/ref]',
                              markup=True, text_size=(70, None))
            #win_label.texture_update()
            win_label.pos = (self.width / 2, self.height / 2 - 70)
##            win_label.size =  win_label.texture_size[0] + 20, \
##                             win_label.texture_size[1] + 20
            win_label.bind(on_ref_press=self.click_win_label)
            win_label.texture_update()
            self.add_widget(win_label)

    def click_win_label(self, instance, value):
        self.initialize()
        self.remove_widget(win_label)


    def on_touch_move(self, touch):
        if touch.x < self.width / 3 and touch.y > self.height / 6 \
            and touch.y < 5 * self.height / 6 and self.player1.can_move:
            self.player1.center_y = touch.y
        if touch.x > self.width - self.width / 3 and touch.y > self.height / 6 \
            and touch.y < 5 * self.height / 6 and self.player2.can_move:
            self.player2.center_y = touch.y
        if touch.y < self.height / 3 and touch.x > self.width / 6 \
            and touch.x < 5 * self.width / 6 and self.player3.can_move:
            self.player3.center_x = touch.x
        if touch.y > 2* self.height / 3 and touch.x > self.width / 6 \
            and touch.x < 5 * self.width / 6 and self.player4.can_move:
            self.player4.center_x = touch.x


class PongApp(App):
    def build(self):
        game = PongGame()
        game.initialize()
        game.serve_ball()
        Clock.schedule_interval(game.update, 1.0 / 60.0)
        return game


if __name__ == '__main__':
    PongApp().run()

And the .kv file:

#:kivy 1.0.9

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

<PongPaddle>:
    size: root.orientation[0], root.orientation[1]
    canvas:
        Rectangle:
            pos:self.pos
            size:self.size

<PongGame>:
    ball: pong_ball
    player1: player_left
    player2: player_right
    player3: player_top
    player4: player_bottom


    Label:
        font_size: 50  
        center_x: root.width / 6
        top: root.top - root.height / 2 + 50
        text: str(root.player1.score)

    Label:
        font_size: 50  
        center_x: root.width * 5 / 6
        top: root.top - root.height / 2 + 50
        text: str(root.player2.score)

    Label:
        font_size: 50
        center_x: root.width / 2
        top: root.height / 6
        text: str(root.player3.score)

    Label:
        font_size: 50
        center_x: root.width / 2
        top: 5 * root.height / 6
        text: str(root.player4.score)

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

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

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

    PongPaddle:
        id: player_top
        y: root.y
        center_x: root.center_x

    PongPaddle:
        id: player_bottom
        y: root.height - self.height
        center_x: root.center_x

Upvotes: 3

Views: 3573

Answers (1)

toto_tico
toto_tico

Reputation: 19047

The Label is being removed, the problem is that you are adding infinite number of them :) because the update method is scheduled to be called each 1/60 seconds here:

Clock.schedule_interval(game.update, 1.0 / 60.0)

You need to un-schedule that before adding the widget if not your computer is going to freeze:

Clock.unschedule(self.update)

The code below works. I would like to suggest not to use global variable (It is just a very bad practice). I modified that too. You don't even need a class attribute for this case since the parameter instance contains exactly the same of self.win_label. Also, I change the way you are centering (self.win_label.center = self.center). Finally, it would be easier to use a [Button][1] instead of a Label because you can bind other methods like on_press or on_release. Actually, you don't need to a ref for this. You can simply bind on_touch_down.

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
from random import randint


class PongPaddle(Widget):
    score = NumericProperty(0)
    orientation = ObjectProperty([0, 0])
    can_move = ObjectProperty(0)

    def bounce_ball(self, ball):
        if self.collide_widget(ball):
            vx, vy = ball.velocity
            if self.orientation[0] == 25:                
                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
            else:
                offset = (ball.center_x - self.center_x) / (self.width / 2)
                bounced = Vector(vx, -1 * vy)
                vel = bounced * 1.1
                ball.velocity = vel.x + offset, vel.y


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)
    player1 = ObjectProperty(None)
    player2 = ObjectProperty(None)
    player3 = ObjectProperty(None)
    player4 = ObjectProperty(None)

    def initialize(self):
        SCORE = 1
        self.player1.orientation = [25, 200]
        self.player2.orientation = [25, 200]
        self.player3.orientation = [200, 25]
        self.player4.orientation = [200, 25]
        self.player1.score = SCORE
        self.player2.score = SCORE
        self.player3.score = SCORE
        self.player4.score = SCORE
        self.player1.can_move = 1
        self.player2.can_move = 1
        self.player3.can_move = 1
        self.player4.can_move = 1
        self.serve_ball()

    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)
        self.player3.bounce_ball(self.ball)
        self.player4.bounce_ball(self.ball)

        #bounce ball off bottom or top
        if ((self.ball.y < self.y) and not self.player3.can_move) \
           or ((self.ball.top > self.top) and not self.player4.can_move):
            self.ball.velocity_y *= -1
        if ((self.ball.x < self.x) and not self.player1.can_move) \
           or ((self.ball.right > self.width) and not self.player2.can_move):
            self.ball.velocity_x *= -1

        #went off to a side to score point?
        if self.ball.x < self.x and self.player1.can_move == 1:
            self.player1.score -= 1
            self.serve_ball(vel=(4, randint(1, 4)))
            if self.player1.score <= 0:
                self.player1.can_move = 0
        elif self.ball.x > self.width and self.player2.can_move == 1:
            self.player2.score -= 1
            self.serve_ball(vel=(-4, randint(1, 4)))
            if self.player2.score <= 0:
                self.player2.can_move = 0
        elif self.ball.y > self.height and self.player4.can_move == 1:
            self.player4.score -= 1
            self.serve_ball(vel = (randint(1, 4), -4))
            if self.player4.score <= 0:
                self.player4.can_move = 0
        elif self.ball.y < self.y and self.player3.can_move == 1:
            self.player3.score -= 1
            self.serve_ball(vel = (randint(1, 4), 4))
            if self.player3.score <= 0:
                self.player3.can_move = 0

        if self.player1.can_move + self.player2.can_move + \
           self.player3.can_move + self.player4.can_move == 1:
            self.ball.velocity = (0, 0)
            Clock.unschedule(self.update)

            self.win_label = Label(size_hint=(None, None),
                              text='[ref=winner]Winner![/ref]',
                              markup=True, font_size=70, color=[1,0,0,1])
            #win_label.texture_update()
            #self.win_label.pos = (self.width / 2, self.height / 2 - 70)
            self.win_label.center = self.center
##            win_label.size =  win_label.texture_size[0] + 20, \
##                             win_label.texture_size[1] + 20
            self.win_label.bind(on_ref_press=self.click_win_label)
            self.win_label.texture_update()
            self.add_widget(self.win_label)


    def click_win_label(self, instance, value):
        self.remove_widget(self.win_label)
        #self.remove_widget(instance) # this should also work:
        self.initialize()
        Clock.schedule_interval(self.update, 1.0 / 60.0)



    def on_touch_move(self, touch):
        if touch.x < self.width / 3 and touch.y > self.height / 6 \
            and touch.y < 5 * self.height / 6 and self.player1.can_move:
            self.player1.center_y = touch.y
        if touch.x > self.width - self.width / 3 and touch.y > self.height / 6 \
            and touch.y < 5 * self.height / 6 and self.player2.can_move:
            self.player2.center_y = touch.y
        if touch.y < self.height / 3 and touch.x > self.width / 6 \
            and touch.x < 5 * self.width / 6 and self.player3.can_move:
            self.player3.center_x = touch.x
        if touch.y > 2* self.height / 3 and touch.x > self.width / 6 \
            and touch.x < 5 * self.width / 6 and self.player4.can_move:
            self.player4.center_x = touch.x


class PongApp(App):
    def build(self):
        game = PongGame()
        game.initialize()
        #game.serve_ball()
        Clock.schedule_interval(game.update, 1.0 / 60.0)
        return game

if __name__ == '__main__':
    PongApp().run()

Upvotes: 1

Related Questions