So i tried to create an apk with buildozer and kivy that connects to my raspberry pi with Paramiko scp. My code works totally fine until i import paramiko, bs4 and requests although i put these in the requirements section in my buildozer.spec file. I can build the app without any errors but wenn i start the app it immediately closes again and every other module imported does not seem to be a problem. I put my code into the kivy-pong example to see if it works with just importing the modules.
Code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import (
NumericProperty, ReferenceListProperty, ObjectProperty
)
from kivy.vector import Vector
from kivy.clock import Clock
from scp import SCPClient
from bs4 import BeautifulSoup
import paramiko
import urllib
import requests
import os
import glob
import time
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 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))
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()
Ok i ran adb logcat while running my app and it turns out that modules like paramiko relies on other modules that i have to install too.
Related
I have a simple code in kivy where the ball bounces off the paddle and what i would like to achieve is make ball grounded after the bouncing of the ball ends. So i would like to simulate gravity. Problem is that collide_widget does not detect collision properly. I expect collide_widget will detect collision when ball.y == paddle.top but it detect collision when ball.y < paddle.top and it is always different depending on starting position of ball.
gravity.py
import kivy
kivy.require('1.11.1')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.vector import Vector
from kivy.clock import Clock
DELTA_TIME = 1.0 / 60.0
class Ball(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self, dt):
self.pos = Vector(*self.velocity) *dt + self.pos
class Paddle(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self, dt):
self.pos = Vector(*self.velocity) *dt + self.pos
class GravityBall(Ball):
def update(self, dt, idle):
if idle == 0:
self.velocity_y -= 20
self.move(dt)
else:
self.velocity_y = 0
self.move(dt)
class GravityGame(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.idle = 0
self.ball = GravityBall()
self.ball.center = (200,300)
self.add_widget(self.ball)
self.paddle = Paddle()
self.paddle.center = (200,100)
self.add_widget(self.paddle)
Clock.schedule_interval(self.update, DELTA_TIME)
def update(self, dt):
self.ball.update(dt, self.idle)
self.paddle.move(dt)
if self.ball.collide_widget(self.paddle) and self.ball.y+5 >= self.paddle.top:
self.ball.velocity_y *= -1
self.idle = 0
elif self.ball.collide_widget(self.paddle) and self.ball.y+5 < self.paddle.top:
self.idle = 1
class GravityApp(App):
def build(self):
return GravityGame()
if __name__ == '__main__':
GravityApp().run()
gravity.kv
#:kivy 1.11.1
<GravityBall>:
size: 30, 30
canvas:
Ellipse:
pos: self.pos
size: self.size
<Paddle>:
size: 100, 30
canvas:
Color:
rgb: 1, 0, 1
Rectangle:
pos: self.pos
size: self.size
The collide_point() method is not likely to capture the exact moment when your ball touches the paddle, so you must an approximation. Here is a modified version of your GravityGame class that does so:
class GravityGame(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.idle = 0
self.ball = GravityBall()
self.ball.center = (200, 300)
self.add_widget(self.ball)
self.paddle = Paddle()
self.paddle.center = (200, 100)
self.add_widget(self.paddle)
self.sched = Clock.schedule_interval(self.update, DELTA_TIME) # save a reference to this event
def update(self, dt):
self.ball.update(dt, self.idle)
self.paddle.move(dt)
# capture approximate collision event
if self.ball.collide_widget(self.paddle):
self.ball.y = self.paddle.top # adjust ball position to top of paddle
self.ball.velocity_y *= -0.75 # collision is not perfectly elastic, so use a value between 0 and -1
# check if ball velocity is nearly stopped (does not move a single pixel in an interval)
if self.ball.velocity_y * DELTA_TIME < 1.0:
self.idle = 1
self.sched.cancel() # stop updating
else:
self.idle = 0
Note a reference to the scheduled update event is saved, so that the updates can be cancelled when the ball stops. The reversing of the velocity on a collision should emulate a non-elastic collision, so I have changed *= -1 to *= -0.75. Finally, if the ball velocity is low enough that the ball does not move in a time interval, then consider the ball to have stopped.
I'm trying to get into game creation using these tools.. Python, Kivy, Buildozer. I was following this good tutorial from Richard Jones, tutorial from Richard Jones
I was using my own graphics then what he used in video as I didn't have access to his files initially. Everything worked in python and buildozer would compile ok with no errors discernable. However when running in android simulator, the app would load and show the kivy logo along with 'loading'. After a few seconds the screen would go black and then it would switch to the android home screen. Not sure here, where to check to see if any errors occurred. I was eventually able to find a post in youtube vid on where to get the files he used in his project, Richard's code. I built his code in buildozer and it ran ok on the android simulator. I then tried to see if it was my graphics... Sure enough, that caused the problem.. My background image and ground image were a different resolution.. His graphics dim were, background 288, 384, mine 500, 592. Ground, his 336, 50 , mine was 700, 76. (all in px). His code did have scaling logic to scale the graphics to fit the screen so I'm not sure why the graphics dimensions would cause the android simulator to have those symptoms. I'll paste his code...
import random
import kivy
kivy.require('1.8.0')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.uix.label import Label
from kivy.core.audio import SoundLoader
from kivy.logger import Logger
from kivy.metrics import Metrics
from kivy.graphics import Color, Rectangle
from kivy_fix import SpriteAtlas
class MultiSound(object):
def __init__(self, file, num):
self.num = num
self.sounds = [SoundLoader.load(file) for n in range(num)]
self.index = 0
def play(self):
self.sounds[self.index].play()
self.index += 1
if self.index == self.num:
self.index = 0
sfx_flap = MultiSound('audio/flap.wav', 3)
sfx_score = SoundLoader.load('audio/score.wav')
sfx_die = SoundLoader.load('audio/die.wav')
class Sprite(Image):
def __init__(self, **kwargs):
super(Sprite, self).__init__(allow_stretch=True, **kwargs)
self.texture.mag_filter = 'nearest'
w, h = self.texture_size
self.size = (params.scale * w, params.scale * h)
class Bird(Sprite):
def __init__(self, pos):
self.images = SpriteAtlas('graphics/bird_anim.atlas')
super(Bird, self).__init__(texture=self.images['wing-up'], pos=pos)
self.velocity_y = 0
self.gravity = -.3 * params.scale
def update(self):
self.velocity_y += self.gravity
self.velocity_y = max(self.velocity_y, -10 * params.scale)
self.y += self.velocity_y
if self.velocity_y < -5 * params.scale:
self.texture = self.images['wing-up']
elif self.velocity_y < 0:
self.texture = self.images['wing-mid']
def on_touch_down(self, *ignore):
self.velocity_y = 5.5 * params.scale
self.texture = self.images['wing-down']
sfx_flap.play()
class Background(Widget):
def __init__(self, source):
super(Background, self).__init__()
self.image = Sprite(source=source)
self.add_widget(self.image)
self.size = self.image.size
self.image_dupe = Sprite(source=source, x=self.width)
self.add_widget(self.image_dupe)
def update(self):
self.image.x -= 1 * params.scale
self.image_dupe.x -= 1 * params.scale
if self.image.right <= 0:
self.image.x = 0
self.image_dupe.x = self.width
class Ground(Sprite):
def update(self):
self.x -= 2 * params.scale
if self.x < -24 * params.scale:
self.x += 24 * params.scale
class Pipe(Widget):
def __init__(self, pos):
super(Pipe, self).__init__(pos=pos)
self.top_image = Sprite(source='graphics/pipe_top.png')
self.top_image.pos = (self.x, self.y + 3.5 * 66 * params.scale)
self.add_widget(self.top_image)
self.bottom_image = Sprite(source='graphics/pipe_bottom.png')
self.bottom_image.pos = (self.x, self.y - self.bottom_image.height)
self.add_widget(self.bottom_image)
self.width = self.top_image.width
self.scored = False
def update(self):
self.x -= 2 * params.scale
self.top_image.x = self.bottom_image.x = self.x
if self.right < 0:
self.parent.remove_widget(self)
class Pipes(Widget):
add_pipe = 0
def update(self, dt):
for child in list(self.children):
child.update()
self.add_pipe -= dt
if self.add_pipe < 0:
y = random.randint(int(self.y + 50 * params.scale),
int(self.height - 50 * params.scale - 3.5 * 66 * params.scale))
self.add_widget(Pipe(pos=(self.width, y)))
self.add_pipe = 1.5
class Blank(Widget):
def __init__(self, pos, size):
super(Blank, self).__init__()
with self.canvas:
Color(0, 0, 0)
Rectangle(pos=pos, size=size)
Color(1, 1, 1)
class Game(Widget):
def __init__(self):
super(Game, self).__init__()
self.background = Background(source='graphics/background.jpg')
self.size = self.background.size
self.add_widget(self.background)
self.ground = Ground(source='graphics/ground.png')
self.pipes = Pipes(pos=(0, self.ground.height), size=self.size)
self.add_widget(self.pipes)
self.add_widget(self.ground)
self.score_label = Label(center_x=self.center_x,
top=self.top - 30 * params.scale, text="0")
self.add_widget(self.score_label)
self.over_label = Label(center=self.center, opacity=0,
text="Game Over")
self.add_widget(self.over_label)
self.bird = Bird(pos=(20 * params.scale, self.height / 2))
self.add_widget(self.bird)
self.add_widget(Blank(*params.blank_rect))
Clock.schedule_interval(self.update, 1.0/60.0)
self.game_over = False
self.score = 0
def update(self, dt):
if self.game_over:
return
self.background.update()
self.bird.update()
self.ground.update()
self.pipes.update(dt)
if self.bird.collide_widget(self.ground):
self.game_over = True
for pipe in self.pipes.children:
if pipe.top_image.collide_widget(self.bird):
self.game_over = True
elif pipe.bottom_image.collide_widget(self.bird):
self.game_over = True
elif not pipe.scored and pipe.right < self.bird.x:
pipe.scored = True
self.score += 1
self.score_label.text = str(self.score)
sfx_score.play()
if self.game_over:
self.over_label.opacity = 1
sfx_die.play()
self.bind(on_touch_down=self._on_touch_down)
def _on_touch_down(self, *ignore):
parent = self.parent
parent.remove_widget(self)
parent.add_widget(Menu())
class Menu(Widget):
def __init__(self):
super(Menu, self).__init__()
self.add_widget(Sprite(source='graphics/background.jpg'))
self.size = self.children[0].size
self.add_widget(Ground(source='graphics/ground.png'))
self.add_widget(Label(center=self.center, text="tap to start"))
self.add_widget(Blank(*params.blank_rect))
def on_touch_down(self, *ignore):
parent = self.parent
parent.remove_widget(self)
parent.add_widget(Game())
class FlappyApp(App):
def build(self):
params.init()
top = Widget()
top.add_widget(Menu())
return top
class params(object):
def init(self):
self.bg_width, self.bg_height = 288, 384
#self.bg_width, self.bg_height = 500, 592
self.width, self.height = Window.size
self.center = Window.center
ws = float(self.width) / self.bg_width
hs = float(self.height) / self.bg_height
self.scale = min(ws, hs)
Logger.info('size=%r; dpi=%r; density=%r; SCALE=%r',
Window.size, Metrics.dpi, Metrics.density, self.scale)
if ws > hs:
gap = self.width - (self.bg_width * hs)
self.blank_rect = ((self.width - gap, 0), (gap, self.height))
else:
gap = self.height - (self.bg_height * ws)
self.blank_rect = ((0, self.height - gap), (self.width, gap))
params = params()
if __name__ == '__main__':
FlappyApp().run()
It would seem this is something with Kivy? In his code he does scaling math to compensate for different screen sizes on android devices so I'm not sure why the different graphics dimensions would cause android to behave in the odd fashion when the graphics dimensions didn't match. Is there a possible place to check on the android simulator to see what maybe causing the problem? The code itself doesn't seem to be a problem since build/compilation seems to be ok for both. The python/kivy app runs fine for both on the desktop... It seems it's just running under kivy after build with buildozer there is a difference and the difference is just with the dimensions of the graphics for the background image and the ground image.
Grateful for any help...
My dev env is using win 10 ,notepad++, wsl - ubuntu - buildozer. No problems on desktop, just diff on android simulator (from android studio chipmunk)
hello My problem is that I dont know how to place a button inside my game without making the gamescreen gone.
whenever I try to add return btn1 inside the code it just shows the button. but not the game. I am sure this is a beginner question but looking it up did not work for me.
BTW yes this is the code from a tutorial which I use privately
my code is:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
import kivy.uix.button
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.button import Button
def callback(instance):
print('The button <%s> is being pressed' % instance.text)
btn1 = Button(text='Hello world 1')
btn1.bind(on_press=callback)
btn2 = Button(text='Hello world 2')
btn2.bind(on_press=callback)
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 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))
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)
if __name__ == '__main__':
PongApp().run()
If you want one widget to be part of another, this widget must be the son of the other widget or its ancestor is the son of that widget, and when you return btn1 in the build() you are indicating that it is the root, that is, the window, it does not show the game, to make a widget's son an option is to use add_widget():
class PongApp(App):
def build(self):
game = PongGame()
game.add_widget(btn1) # <--
btn2.pos = 100, 100
game.add_widget(btn2) # <--
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
I'm trying to make a basic visual exercise in Python and Kivy, but i'm stuck in this part of the code, how to make the ball start on the bottom of the screen?
Here's the .py file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
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)
def serve_ball(self, vel=(0, 4)):
self.ball.center = self.center
self.ball.velocity = vel
def update(self, dt):
self.ball.move()
# bounce ball off bottom or top or left or right
if (self.ball.y < self.y) or (self.ball.top > self.top):
self.ball.velocity_y = -2
self.ball.velocity_x = -4
# bounce ball off
if(self.ball.x < 0):
self.ball.velocity_x = 4
self.ball.velocity_y = 0
if(self.ball.x + self.ball.width > self.right):
self.ball.velocity_x *= -1
self.ball.velocity_y = 4
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()
.kv file:
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongGame>:
ball: pong_ball
PongBall:
id: pong_ball
center: self.parent.center
This code make the widget starts on center of the screen, i'm trying to make start on bottom, without success
Thanks for any tips!
The problem in your case is that the PongBall's initial position is the center of the PongGame:
<PongGame>:
ball: pong_ball
PongBall:
id: pong_ball
center: self.parent.center # <---
What you must do is that when you create the initial position is established. Another error is that you are launching the game before the window is displayed, instead of launching it in build() you must do it in on_start():
*.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
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)
def serve_ball(self, vel=(0, 4)):
self.ball.center = self.initial_pos
self.ball.velocity = vel
def update(self, dt):
self.ball.move()
# bounce ball off bottom or top or left or right
if self.ball.y < self.y or self.ball.top > self.top:
self.ball.velocity_y = -2
self.ball.velocity_x = -4
# bounce ball off
if self.ball.x < 0:
self.ball.velocity_x = 4
self.ball.velocity_y = 0
if self.ball.x + self.ball.width > self.right:
self.ball.velocity_x *= -1
self.ball.velocity_y = 4
class PongApp(App):
def build(self):
game = PongGame()
return game
def on_start(self):
self.root.serve_ball()
Clock.schedule_interval(self.root.update, 1.0 / 60.0)
if __name__ == '__main__':
PongApp().run()
*.kv
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongGame>:
ball: pong_ball
initial_pos: [self.center_x , pong_ball.height/2]
PongBall:
id: pong_ball
In build() an incorrect size of the widgets is obtained since these are updated instants before being launched, it is convenient to use on_start() to obtain the size on which the initial position depends.
Dears,
I'm trying to do a very simple program in Kivy, which will move a Label randomly all over the screen (like the old screensavers).
Would like to use Clock and Random
What am I doing wrong ?
Thks
Code
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.properties import ListProperty
from kivy.core.window import Window
from random import random
Builder.load_string('''
<TransLabel>:
Label :
text: "test"
pos: self.pos
size: self.size
''')
class TransLabel(Label):
velocity = ListProperty([10,15])
def __init__(self, **kwargs):
super(TransLabel,self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1/60.)
def update(self,*args):
self.x += self.velocity[0]
self.y += self.velocity[1]
if self.x <0 or (self.x + self.width) > Window.width:
self.velocity[0] *= -1
if self.y <0 or (self.y + self.height) > Window.height:
self.velocity[1] *= -1
runTouchApp(TransLabel())
The label that contains the text should not have the size of the root, since when executing the update() method the text will be oscillating since the sentences that evaluate the if will always be true.
You must also compare the position of the text with respect to the window, not the position of the root with the window since they have the same size.
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.properties import ListProperty, NumericProperty
from kivy.core.window import Window
from random import randint
Builder.load_string('''
<TransLabel>:
Label :
id: label
text: "test"
''')
class TransLabel(Label):
velocity = ListProperty([10,15])
def __init__(self, **kwargs):
super(TransLabel,self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1./60)
self.x = randint(0, self.width)
self.y = randint(0, self.height)
def update(self,*args):
self.x += self.velocity[0]
self.y += self.velocity[1]
label = self.ids.label
if self.x <0 or (self.x + label.width) > Window.width:
self.velocity[0] *= -1
if self.y <0 or (self.y + label.height) > Window.height:
self.velocity[1] *= -1
label.pos = [self.x, self.y]
runTouchApp(TransLabel())