Kivy - 添加和删除标签

Posted

技术标签:

【中文标题】Kivy - 添加和删除标签【英文标题】:Kivy - adding and removing labels 【发布时间】:2013-08-01 19:59:33 【问题描述】:

我是 Kivy 的新手,找不到任何关于添加和删除标签的真正答案。我完成了 Pong 教程(您可能已经猜到了),并将其改编为 4 名玩家。现在,我正在尝试添加一个标签来显示文本Winner!,并单击该文本以重新开始游戏。

到那里为止,它正在工作。问题是,我无法在新游戏开始后让标签再次消失。另外,我不明白格式 - 我似乎无法使我的标签更大或在面板中将其移动到更低的位置。

我将发布所有代码,因为希望您无论如何都想玩游戏(一旦它被修复),我很肯定有一种更好、更简洁的方式来添加和删除文本。

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

还有.kv文件:

#: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

【问题讨论】:

【参考方案1】:

Label 正在被删除,问题是你添加了无数个 :) 因为更新方法计划在这里每 1/60 秒调用一次:

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

如果不是您的计算机会死机,您需要在添加小部件之前取消计划:

Clock.unschedule(self.update)

下面的代码有效。我想建议不要使用全局变量(这只是一个非常糟糕的做法)。我也修改了那个。对于这种情况,您甚至不需要类属性,因为参数instance 包含与self.win_label 完全相同的内容。另外,我改变了你的居中方式(self.win_label.center = self.center)。最后,使用[Button][1] 而不是Label 会更容易,因为您可以绑定其他方法,例如on_presson_release。实际上,您不需要为此提供ref。你可以简单地绑定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()

【讨论】:

非常感谢您的解释!今晚我才能尝试代码,但我很兴奋。您能否进一步解释一下“实际上,您不需要为此使用ref。您可以简单地绑定on_touch_down。”你指的是标签还是按钮? 任何 [Widget][kivy.org/docs/… 有 3 个事件:on_touch_downon_touch_moveon_touch_upButtonLevel 都继承自 Widget。你可以这样做self.win_label.bind(on_touch_down=self.click_win_label)。优点是您不必一定要点击Label。您可以单击屏幕上的任意位置,游戏将重新启动。如果你想限制你可以使用collide_points。 还要注意 on_touch_down 只接收一个参数(触摸)而不是两个:instancevalue,所以你必须在方法click_win_label 中反映这一点。除此之外,Button 有事件on_press(和on_release),它们与on_touch_downon_touch_up 类似,只是它们已经控制您专门单击Button 本身(可能使用方法collide_points 内部)。

以上是关于Kivy - 添加和删除标签的主要内容,如果未能解决你的问题,请参考以下文章

使用线程更新 Kivy 标签

Kivy - 如何在标签中添加多个标签或按钮

如何在 kivy python 中的标签、文本输入和其他小部件中添加标题

如何在kivy python中添加标签,TextInput

为啥我需要创建 Line 的新实例,而不是在 Kivy 中简单地更新或添加和删除它

为啥我需要创建 Line 的新实例,而不是在 Kivy 中简单地更新或添加和删除它