2015-07-10 69 views
1

我不知道爲什麼我收到此錯誤的情況下使用...add_widget()只能與Widget的

我有一個顯示在畫布上的一個矩形此類HungerBar。我希望它在遊戲開始時出現。所以我把它放在這個更新函數中,一旦程序啓動,我就會調用它。這是一段代碼。

class HungerBar(Rectangle): 
    def hunger_dec(self): 
     if (self.size[0] > 0): 
      self.size = Vector(-1, 0) + self.size 
    def hunger_inc(self, increase): 
     if (self.size[0] + increase < 100): 
      self.size = Vector(increase, 0) + self.size 

class ShootingGame(Widget): 
    hungerBar = HungerBar() 

    def drawBar(self): 
     self.hungerBar.size = Vector(1000, 20) 
     self.add_widget(self.hungerBar) 

    def update(self, dt): 
     self.drawBar() 

class GameScreen(Screen): 
    def __init__(self, **kwargs): 
     super(GameScreen, self).__init__(**kwargs) 
     self.game = ShootingGame() 
     self.add_widget(self.game) 
     Clock.schedule_interval(self.game.update, 1.0/60.0) 

最後,GameScreen被添加到應用程序。

這是HungerBar是如何在.kv文件

<HungerBar>: 
    Color: 
     rgb: (0, 1, 1) 
    canvas: 
     Rectangle: 
      pos: self.width * 7, 30 
      size: self.size 

定義。當我運行此代碼,這是錯誤我得到:

Traceback (most recent call last): 
    File "main.py", line 323, in <module> 
    ShootingApp().run() 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/app.py", line 824, in run 
    runTouchApp() 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 487, in runTouchApp 
    EventLoop.window.mainloop() 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/core/window/window_sdl2.py", line 539, in mainloop 
    self._mainloop() 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/core/window/window_sdl2.py", line 300, in _mainloop 
    EventLoop.idle() 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 330, in idle 
    self.dispatch_input() 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 315, in dispatch_input 
    post_dispatch_input(*pop(0)) 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 221, in post_dispatch_input 
    listener.dispatch('on_motion', etype, me) 
    File "kivy/_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:7011) 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/core/window/__init__.py", line 904, in on_motion 
    self.dispatch('on_touch_down', me) 
    File "kivy/_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:7011) 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/core/window/__init__.py", line 920, in on_touch_down 
    if w.dispatch('on_touch_down', touch): 
    File "kivy/_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:7011) 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/uix/screenmanager.py", line 1069, in on_touch_down 
    return super(ScreenManager, self).on_touch_down(touch) 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/uix/widget.py", line 382, in on_touch_down 
    if child.dispatch('on_touch_down', touch): 
    File "kivy/_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:7011) 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/uix/relativelayout.py", line 276, in on_touch_down 
    ret = super(RelativeLayout, self).on_touch_down(touch) 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/uix/widget.py", line 382, in on_touch_down 
    if child.dispatch('on_touch_down', touch): 
    File "kivy/_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:7011) 
    File "main.py", line 94, in on_touch_down 
    self.drawBar() 
    File "main.py", line 173, in drawBar 
    self.add_widget(self.hungerBar) 
    File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/uix/widget.py", line 442, in add_widget 
    'add_widget() can be used only with instances' 
kivy.uix.widget.WidgetException: add_widget() can be used only with instances of the Widget class. 

預先感謝您對我的幫助! :)

這是完整的代碼。對不起,這真的很漫長,很混亂。

import kivy 
__version__ = "1.9.0" 

from kivy.app import App 
from kivy.uix.widget import Widget 
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty, StringProperty 
from kivy.uix.relativelayout import RelativeLayout 
from kivy.animation import Animation 
from kivy.uix.label import Label 
from kivy.vector import Vector 
from kivy.clock import Clock 
from kivy.uix.button import Button 
from kivy.uix.screenmanager import ScreenManager, Screen 
from kivy.graphics import Line, Color, Rectangle 
from functools import partial 
import socket, time, math, random, math 
from random import randint 
from kivy.lang import Builder 

Builder.load_file('shooting.kv') 

#setup graphics 
from kivy.config import Config 
Config.set('graphics','resizable',0) 

#Graphics fix 
from kivy.core.window import Window; 
Window.clearcolor = (1,1,1,1) 

UDP_IP = "10.0.1.5" 
UDP_PORT = 5005 
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

class Missile(Widget): 
    angle = NumericProperty(0) 
    source = StringProperty("") 
    def expend_size(self): 
     if (self.size[0] < 200): 
      self.size = Vector(3, 3) + self.size 
    def cont_travel(self, velocity): 
     self.pos = Vector(*velocity) + self.pos 
    def rotate(self, a): 
     self.angle = a 

class Enemy(Widget): 
    velocity_x = NumericProperty(0) 
    velocity_y = NumericProperty(0) 
    spawnT = NumericProperty(0) 
    angle = NumericProperty(0) 
    source = StringProperty("") 
    def move(self): 
     self.x = self.x + self.velocity_x 
     self.y = self.y + self.velocity_y 
    def drawWalking(self, *large): 
     with self.canvas: 
      self.source = './images/walk1.png' 
      def changeWalk(rect, newSource, *largs): 
       rect.source = newSource 
      Clock.schedule_once(partial(changeWalk, self, './images/walk2.png'),0.5) 

class HungerBar(Widget): 
    def hunger_dec(self): 
     if (self.size[0] > 0): 
      self.size = Vector(-1, 0) + self.size 
    def hunger_inc(self, increase): 
     if (self.size[0] + increase < 100): 
      self.size = Vector(increase, 0) + self.size 

class Score(Label): 
    def show_score(self, s): 
     self.text = str(s) 

class TestCircle(Widget): 
    def move(self, dt): 
     self.x = self.x + self.velocity_x 

class ShootingGame(Widget): 
    missile = Missile() 
    hungerBar = ObjectProperty(None) 
    enemy = Enemy() 
    enemy_list = [] 
    enemy_count = 0 
    enemy_amount = 1 
    # this indicates if the frog is traveling or not 
    travel = False 
    pos_down = Vector(0, 0) 
    pos_up = Vector(0, 0) 
    time_down = 0 
    time_up = 0 
    enemy_on_left = True 
    missile_onscreen = False 
    vel = Vector(0, 0) 
    points = NumericProperty(0) 
    # this is the timer that controls if the bug stops or moves 
    movement_timer = 5 
    # if the bug is traveling or not 
    movement = True 

    def __init__(self, **kwargs): 
     super(ShootingGame, self).__init__(**kwargs) 
     randX = random.choice([0.1, 0.2, 0.8, 0.9]) 
     self.enemy = self.drawEnemy(randX) 
     Clock.schedule_interval(self.enemy.drawWalking, 1) 

     # self.hungerBar = self.drawBar() 

    # The following functions let users to shoot missiles by touching the screen 
    def on_touch_down(self, touch): 
     # only draws a new object if there is none on the screen. Also checks for side of the bug 
     if ((not self.missile_onscreen) and 
      ((self.enemy_on_left and touch.x > (self.parent.width/2)) or 
      ((not self.enemy_on_left) and touch.x < self.parent.width/2))): 
      # generate new line 
      with self.canvas: 
       Color(1, 0, 0) 
       self.line = Line(points=(touch.x, touch.y, touch.x, touch.y), width = 2) 
      # generate a new missile 
      self.missile_onscreen = True 
      self.missile = Missile() 
      self.add_widget(self.missile) 
      self.missile.pos = (touch.x - self.missile.size[0]/2 , touch.y - self.missile.size[0]/2) 
      self.pos_down = Vector(touch.x, touch.y) 
      self.time_down = time.time() 
      self.missile.source = 'atlas://images/sprite.atlas/frog' 
      # rotate sprite 
      # left 
     if (touch.x < self.parent.width/2): 
       self.missile.angle = -45 
       send_server(40002, 0.29, 400, 200) 
      # right 
     else: 
       self.missile.angle = 45 
       send_server(50002, 0.29, 400, 200) 

    def on_touch_move(self, touch): 
     if (self.missile_onscreen): 
      self.time_down = time.time() 
      with self.canvas: 
       self.line.points = [touch.x, touch.y, self.pos_down.x, self.pos_down.y] 

      if (touch.x - self.pos_down[0] != 0): 
       a = math.degrees(math.atan((-(touch.y - self.pos_down[1]))/(-(touch.x - self.pos_down[0])))) 
       # left 
       if (self.pos_down[0] < self.parent.width/2) and (touch.x - self.pos_down[0] < 0): 
        self.missile.angle = a/2 - 45 
       # right 
       elif (self.pos_down[0] > self.parent.width/2) and (touch.x - self.pos_down[0] > 0): 
        self.missile.angle = a/2 + 45 

    def on_touch_up(self, touch): 
     # only gives speed and orientation if there is no object traveling already 
     if (self.missile_onscreen and (self.vel == Vector(0, 0))): 
      self.time_up = time.time() 
      self.pos_up = Vector(touch.x, touch.y) 
      self.missile.source = 'atlas://images/sprite.atlas/frog_jump' 

      # remove line 
      self.canvas.remove(self.line) 
      self.vel = Vector(-(self.pos_up[0] - self.pos_down[0])/10, -(self.pos_up[1] - self.pos_down[1])/10) 
      # frog traveling (with speed threshold) 
      if (abs(self.vel[0]) > 5) and (self.vel[1] != 0): 
       print("x vel: "+str(self.vel[0])) 
       print("y vel: "+str(self.vel[1])) 
       # left 
       if (self.vel[0] > 0 and self.pos_down[0] < (self.parent.width/3)): 
        self.travel = True 
        (intensity, duration) = get_haptic_par(self.missile.size[0], self.vel, self.parent.width - self.pos_up[0]) 
       # right 
        send_server(40001, intensity, duration, 200) 
       elif (self.vel[0] < 0 and self.pos_down[0] > (self.parent.width * 2/3)): 
        self.travel = True 
        (intensity, duration) = get_haptic_par(self.missile.size[0], self.vel, self.pos_up[0]) 
        send_server(50001, intensity, duration, 200) 
       else: 
        self.remove_widget(self.missile) 
        send_server(40003, 0, 0, 200) 
        send_server(50003, 0, 0, 200) 
      else: 
       self.remove_widget(self.missile) 
       self.missile_onscreen = False 
       self.vel = Vector(0, 0) 
       send_server(40003, 0, 0, 200) 
       send_server(50003, 0, 0, 200) 


    def drawBar(self): 
     bar = HungerBar() 
     # self.hungerBar.size = Vector(1000, 20) 
     self.add_widget(bar) 
     return bar 


    # Randomly generating an enemy 
    def drawEnemy(self, x_pos): 
     tmpEnemy = Enemy() 
     tmpEnemy.x = self.width * x_pos 

     # 1280 is the width of the screen 
     if (tmpEnemy.x < 1280/2): 
      self.enemy_on_left = True 
     else: 
      self.enemy_on_left = False 

     randPos = randint(10, 90) 
     # 1200 is the width of the screen 
     tmpEnemy.y = float(randPos) /100 * 800 

     tmpEnemy.velocity_x = 0 
     tmpEnemy.velocity_y = randint(3, 5) 
     tmpEnemy.spawnT = time.time() 

     self.add_widget(tmpEnemy) 
     return tmpEnemy 

    def update(self, dt): 
     # self.drawBar() 
     # Missile travels is users flicks the missile 
     if (self.travel): 
      self.missile.cont_travel(self.vel) 
      if ((self.missile.right < 0) or (self.missile.x > self.parent.width) or 
       (self.missile.top < 0) or (self.missile.y > self.parent.height)): 
       self.travel = False 
       self.missile_onscreen = False 
       self.vel = Vector(0, 0) 

     # controls the timing of the enemy movoing 
     # print(time.time() % self.movement_timer) 
     if ((time.time() % self.movement_timer) < 0.015): 
      self.movement = not (self.movement) 
      self.movement_timer = random.randint(3, 5) 

     if (self.movement): 
      self.enemy.move() 
     # Animate the bug turning around 
     if (self.enemy.top > self.parent.height): 
      angle = -90 
      Animation(center=self.enemy.center, angle=angle, duration = 0.5).start(self.enemy) 
      self.enemy.velocity_y = -(self.enemy.velocity_y) 
     elif (self.enemy.y < 0): 
      angle = 90 
      Animation(center=self.enemy.center, angle=angle, duration = 0.5).start(self.enemy) 
      self.enemy.velocity_y = -(self.enemy.velocity_y) 
     # when frog catches the bug 
     if (self.missile and self.enemy.collide_widget(self.missile) and self.missile_onscreen): 
      #calculate score depending on how long it takes the player to hit the enemy 
      round_score = int(1.0/(time.time() - self.enemy.spawnT)/self.missile.size[0] * 1000) 
      if round_score > 30: 
       round_score = 30 
      elif round_score < 1: 
       round_score = 1 
      if (self.enemy.x < self.parent.width/2): 
       send_server(60001, 0, 0, 200) 
      if (self.enemy.x > self.parent.width/2): 
       send_server(60002, 0, 0, 200) 
      # self.points = self.points + round_score 
      score = Score() 
      score.pos = Vector(self.enemy.x, self.enemy.y + 5) 
      score.show_score("[color=ff3333]" + str(round_score) + "[/color]") 
      self.add_widget(score) 
      Clock.schedule_once(lambda dt: self.remove_widget(score), 1) 
      #removing missile as well - removed bug: frog gets stuck on the edge of the screen 
      self.remove_widget(self.missile) 
      self.missile_onscreen = False 
      self.vel = Vector(0, 0) 
      # self.enemy_list.remove(e) 

def get_haptic_par(size, vel, canvasWidth): 
    intensity = float(size)/300 * 0.29 
    vis_dur_tot = canvasWidth/(abs(vel[0]) * 60) * 1000 
    hap_dur_tot = 0.69 * vis_dur_tot + 137.02 
    #hap_dur_tot = dur + soa = dur + 0.28 * dur + 60.7 => dur = (hap_dur_tot - 60.7)/1.28 
    return (intensity, (hap_dur_tot - 60.7) /1.28) 

def send_server(*args): 
    port = args[0] 
    intensity = args[1] 
    duration = args[2] 
    frequency = args[3] 

    MESSAGE = "%d;%f;%f;%f" % (port, intensity, duration, frequency) 
    sock.sendto(MESSAGE, (UDP_IP, UDP_PORT)) 

class WelcomeScreen(Screen): 
    pass 

class BasicScreen1(Screen): 
    pass 

class BasicScreen2(Screen): 
    pass 

class BasicScreen3(Screen): 
    pass 

class GameScreen(Screen): 
    def __init__(self, **kwargs): 
     super(GameScreen, self).__init__(**kwargs) 
     self.game = ShootingGame() 
     self.add_widget(self.game) 
     Clock.schedule_interval(self.game.update, 1.0/60.0) 

sm = ScreenManager() 
sm.add_widget(WelcomeScreen(name='welcome')) 
sm.add_widget(BasicScreen1(name='basic1')) 
sm.add_widget(BasicScreen2(name='basic2')) 
basicscreen3 = BasicScreen3(name='basic3') 
sm.add_widget(basicscreen3) 
game_screen = GameScreen(name='game') 
sm.add_widget(game_screen) 

class ShootingApp(App): 
    def printThis(self, x): 
     print(x) 

    def play_haptic(self, intensity, duration): 
     send_server(40001, intensity, duration, 70) 

    def play_ball(self, intensity, duration): 
     ball = TestCircle() 
     # i = 0.0106 * math.pow(10, (intensity - 38.892)/9.7721) 
     # ball.size = Vector(50, 50) 
     ball.pos = Vector(0,405) 
     basicscreen3.add_widget(ball) 
     lengthT = duration + 0.4 * duration + 0.28 * duration + 60.7 
     ball.velocity_x = ball.parent.width/lengthT * 20 
     Clock.schedule_interval(ball.move, 1.0/60.0) 
     if (ball.x > ball.parent.width): 
      basicscreen3.remove_widget(ball) 


    def build(self): 
     return sm 

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

的kivy文件:

#:kivy 1.9.0 
#:import atlas kivy.atlas.Atlas 
#:import NoTransition kivy.uix.screenmanager.NoTransition 
#:import SlideTransition kivy.uix.screenmanager.SlideTransition 


<WelcomeScreen>: 
    Button: 
     text: "Learn about tactile illusions" 
     size_hint: None, None 
     size: 500, 70 
     pos: 100, 200 
     font_size: 30 
     on_release: 
      app.root.transition = SlideTransition() 
      app.root.current = "basic1" 

    Button: 
     text: "Play our game" 
     size_hint: None, None 
     size: 500, 70 
     pos: 100, 100 
     font_size: 30 
     on_release: 
      app.root.transition = SlideTransition() 
      app.root.current = "game" 

<BasicScreen1>: 
    name: "basic1" 
    Label: 
     text: "When two hands are vibrated with a time lapse in the middle, \nyou would feel a continuous motion across the hands." 
     font_size: 30 
     color: .8,.9,0,1 
    Button: 
     text: "Play" 
     size_hint: None, None 
     size: 140, 70 
     pos: 1000, 200 
     font_size: 30 
     on_release: 
      app.play_haptic(0.5, 400) 

    Button: 
     text: "Next" 
     size_hint: None, None 
     size: 140, 70 
     pos: 1000, 120 
     font_size: 30 
     on_release: 
      app.root.transition = SlideTransition() 
      app.root.current = "basic2" 

<BasicScreen2>: 
    name: "basic2" 
    Label: 
     text: "You can change the speed of the motion across hands. \nBut regardless of speed, the motion always feels continuous." 
     font_size: 30 
     color: .8,.9,0,1 
     pos: 0, 150 

    Slider: 
     id: s1 
     value: 100 
     range: (100, 800) 
     step: 1 
     pos: 0, 0 
     padding: 250 

    Button: 
     text: "Play" 
     size_hint: None, None 
     size: 140, 70 
     pos: 1000, 200 
     font_size: 30 
     on_release: 
      app.play_haptic(0.5, s1.value) 

    Button: 
     text: "Next" 
     size_hint: None, None 
     size: 140, 70 
     pos: 1000, 120 
     font_size: 30 
     on_release: 
      app.root.transition = SlideTransition() 
      app.root.current = "basic3" 

<BasicScreen3>: 
    name: "basic3" 
    Label: 
     text: "We can generate visual content that optimally matches with the tactile illusion. \nThe two motions work together to generate coherent multimodal experience." 
     font_size: 30 
     pos: 0, 200 
     color: .8,.9,0.1,1 

    Label: 
     text: "Speed (fast <-> slow)" 
     font_size: 30 
     pos: 0, -25 
     color: 0, 0, 0, 1 

    Slider: 
     id: s2 
     min: 100 
     max: 800 
     pos: 0, -75 
     padding: 250 


    Button: 
     text: "Play" 
     size_hint: None, None 
     size: 250, 70 
     pos: 1000, 200 
     font_size: 30 
     on_release: 
      app.play_ball(0.5, s2.value) 
      app.play_haptic(0.5, s2.value) 

    Button: 
     text: "Play the game" 
     size_hint: None, None 
     size: 250, 70 
     pos: 1000, 120 
     font_size: 30 
     on_release: 
      app.root.transition = SlideTransition() 
      app.root.current = "game" 

<TestCircle>: 
    canvas: 
     Color: 
      rgb: (0, 1, 1) 
     Ellipse: 
      size: 100, 100 
      pos: self.pos 

<GameScreen>: 
    name: "game" 
    Button: 
     text: "Back" 
     size_hint: None, None 
     size: 70, 70 
     pos: 0, 0 
     font_size: 30 
     on_release: 
      app.root.transition = SlideTransition(direction="right") 
      root.game.points = 0 
      app.root.current = "welcome" 

<Missile>: 
    size: 50, 50 
    canvas.before: 
     PushMatrix 
     Rotate: 
      angle: self.angle 
      origin: self.center 
    canvas.after: 
     PopMatrix 
    canvas: 
     Color: 
      rgb: (1, 1, 1) 
     Rectangle: 
      source: self.source 
      pos: self.pos 
      size: self.size 

<Enemy>: 
    size: 70, 70 
    canvas.before: 
     PushMatrix 
     Rotate: 
      angle: self.angle 
      origin: self.center 
    canvas.after: 
     PopMatrix 
    canvas: 
     Color: 
      rgb: (1, 1, 1) 
     Rectangle: 
      source: self.source 
      pos: self.pos 
      size: self.size 

<HungerBar>: 
    size: 100, 5 
    Color: 
     rgb: (0, 1, 1) 
    canvas: 
     Rectangle: 
      pos: self.width * 7, 30 

<Score>: 
    font_size: 30 
    pos: self.pos 
    text: self.text 
    markup: True 
+1

好吧,因爲,HungerBar應該繼承窗體Widget,Rectangle是一個Vertex_instruction,這就是爲什麼它會導致錯誤,嘗試從窗口小部件繼承並指定尺寸屬性在kv lang中得到想要的結果 – fins

+0

我改變了繼承:class HungerBar (Widget)。還指定了size屬性的大小:100,5。該錯誤仍然存​​在 – susanz

+0

這樣的異常僅在不是isinstance(self.hungerBar,Widget)時纔會引發,我不明白它會如何引發相同的錯誤,也許你可以pastebin整個代碼? – fins

回答

4
<HungerBar>: 
    size: 100, 5 
    Color: 
     rgb: (0, 1, 1) 
    canvas: 
     Rectangle: 
      pos: self.width * 7, 30 

此規則增加了一個顏色(這是一個VertexInstruction)畫布塊外面,所以kivy嘗試將其添加爲插件。