我不知道为什么会收到此错误...
我有这个类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会添加到App。
这就是在.kv文件中定义HungerBar的方式
<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
答案 0 :(得分:4)
B
此规则在画布块外添加一个Color(VertexInstruction),因此kivy会尝试将其添加为小部件。