Having trouble with implementing a timer in Kivy - python

I'm trying to create a kivy application.
The Application works as following:
I send the amount of seconds I want my timer to last via MQTT over the topic 'tafelxuren' (x is a number between 1 and 8).
After sending the amounts of seconds I want it to last, I send a second MQTT command to the topic 'tafelxstart' (x is a number between 1 and 8) with the command 'start'.
After sending the start command, my timer starts running.
Under my timer I have 3 buttons, pause, resume and stop.
While starting and pausing the timer works using MQTT commands, I cannot seem to get my resume button to function properly, if there's 26 seconds left after clicking on the pause button, I want my timer to resume from that point. Instead, it starts again from the beginning
My main.py:
import os
if os.name == 'posix':
os.environ['KIVY_GL_BACKEND'] = 'gl'
import kivy, time,threading
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ObjectProperty
from kivy.clock import Clock
from kivy.config import Config
import paho.mqtt.client as mqtt
Config.set('graphics', 'fullscreen', 'auto')
import paho.mqtt.publish as publish
import paho.mqtt.subscribe as subscribe
import config
# lege MQTT-client instantie
client = None
# De topics die gebruikt worden
ingesteldetijd = [config.timeTopics['tafel1'],config.timeTopics['tafel2'],config.timeTopics['tafel3'],config.timeTopics['tafel4'],config.timeTopics['tafel5'],config.timeTopics['tafel6'],config.timeTopics['tafel7'],config.timeTopics['tafel8']]
ingesteldetijdTopics = [config.tim1, config.tim2, config.tim3, config.tim4, config.tim5, config.tim6, config.tim7, config.tim8]
starttopics = [config.startTopics['tafel1'],config.startTopics['tafel2'],config.startTopics['tafel3'],config.startTopics['tafel4'],config.startTopics['tafel5'],config.startTopics['tafel6'],config.startTopics['tafel7'],config.startTopics['tafel8']]
def on_connect(client,userdata,flags,rc):
'''
This function gets triggered when MQTT is connected succesfully
'''
if(rc == 0):
print("[INFO ] [MQTT ] MQTT connected to broker "+config.settings['broker']+".")
client.subscribe('#')
##################################### ingestelde tijd subscriptions #####################################
for x in range(0,8):
client.subscribe(ingesteldetijd[x])
print('[INFO ] [MQTT ] Subscribed to '+ingesteldetijd[x])
##################################### ingestelde tijd subscriptions #####################################
##################################### start topic subscriptions #####################################
for x in range(0,8):
client.subscribe(starttopics[x])
print('[INFO ] [MQTT ] Subscribed to '+starttopics[x])
##################################### start topic subscriptions #####################################
else:
print("MQTT connection to broker "+config.settings['broker']+"failed.")
def on_message(client,userdata,msg):
'''
If there's a message received on one of the topics, the messages gets handled here.
'''
################################## tijd instellen topic ##################################
if msg.topic == 'tafel1uren':
config.tim1 = int(msg.payload.decode('utf-8'))
if msg.topic == 'tafel2uren':
config.tim2 = int(msg.payload.decode('utf-8'))
if msg.topic == 'tafel3uren':
config.tim3 = int(msg.payload.decode('utf-8'))
if msg.topic == 'tafel4uren':
config.tim4 = int(msg.payload.decode('utf-8'))
if msg.topic == 'tafel5uren':
config.tim5 = int(msg.payload.decode('utf-8'))
if msg.topic == 'tafel6uren':
config.tim6 = int(msg.payload.decode('utf-8'))
if msg.topic == 'tafel7uren':
config.tim7 = int(msg.payload.decode('utf-8'))
if msg.topic == 'tafel8uren':
config.tim8 = int(msg.payload.decode('utf-8'))
################################## tijd instellen topic ##################################
if msg.topic == config.startTopics['tafel1']:
if msg.payload.decode('utf-8') == 'start':
config.tb1start = True
if msg.payload.decode('utf-8') == 'stop':
config.tb1start = False
if msg.topic == config.startTopics['tafel2']:
if msg.payload.decode('utf-8') == 'start':
config.tb2start = True
if msg.payload.decode('utf-8') == 'stop':
config.tb2start = False
if msg.topic == config.startTopics['tafel3']:
if msg.payload.decode('utf-8') == 'start':
config.tb3start = True
if msg.payload.decode('utf-8') == 'stop':
config.tb3start = False
if msg.topic == config.startTopics['tafel4']:
if msg.payload.decode('utf-8') == 'start':
config.tb4start = True
if msg.payload.decode('utf-8') == 'stop':
config.tb4start = False
if msg.topic == config.startTopics['tafel5']:
if msg.payload.decode('utf-8') == 'start':
config.tb5start = True
if msg.payload.decode('utf-8') == 'stop':
config.tb5start = False
if msg.topic == config.startTopics['tafel6']:
if msg.payload.decode('utf-8') == 'start':
config.tb6start = True
if msg.payload.decode('utf-8') == 'stop':
config.tb6start = False
if msg.topic == config.startTopics['tafel7']:
if msg.payload.decode('utf-8') == 'start':
config.tb7start = True
if msg.payload.decode('utf-8') == 'stop':
config.tb7start = False
if msg.topic == config.startTopics['tafel8']:
if msg.payload.decode('utf-8') == 'start':
config.tb8start = True
if msg.payload.decode('utf-8') == 'stop':
config.tb8start = False
class CrudeTimerGrid(GridLayout):
_python_access = ObjectProperty(None)
time = NumericProperty(0)
def __init__(self,**kwargs):
super(CrudeTimerGrid,self).__init__(**kwargs)
self.runningTimer = 0
Clock.schedule_interval(self.load_times,1)
Clock.schedule_interval(self.start,1)
def load_times(self, *_):
tafelobjecten = self.parent.parent.ids
for x in range(0,8):
if list(tafelobjecten.values())[x] == self:
if str(list(tafelobjecten.keys())[x]) == 'tafel1':
self.time = config.tim1
if str(list(tafelobjecten.keys())[x]) == 'tafel2':
self.time = config.tim2
if str(list(tafelobjecten.keys())[x]) == 'tafel3':
self.time = config.tim3
if str(list(tafelobjecten.keys())[x]) == 'tafel4':
self.time = config.tim4
if str(list(tafelobjecten.keys())[x]) == 'tafel5':
self.time = config.tim5
if str(list(tafelobjecten.keys())[x]) == 'tafel6':
self.time = config.tim6
if str(list(tafelobjecten.keys())[x]) == 'tafel7':
self.time = config.tim7
if str(list(tafelobjecten.keys())[x]) == 'tafel8':
self.time = config.tim8
def start(self, *_):
tafelobjecten = self.parent.parent.ids
self.runningTimer = self.time
for x in range(0,8):
if list(tafelobjecten.values())[x] == self:
if str(list(tafelobjecten.keys())[x]) == 'tafel1':
#self.time = config.tim1
if config.tb1start == True:
if(self.runningTimer > 0):
Clock.schedule_interval(self.tick,1)
elif str(list(tafelobjecten.keys())[x]) == 'tafel2':
#self.time = config.tim2
if config.tb2start == True:
if(self.runningTimer > 0):
Clock.schedule_interval(self.tick,1)
elif str(list(tafelobjecten.keys())[x]) == 'tafel3':
#self.time = config.tim3
if config.tb3start == True:
if(self.runningTimer > 0):
Clock.schedule_interval(self.tick,1)
elif str(list(tafelobjecten.keys())[x]) == 'tafel4':
#self.time = config.tim4
if config.tb4start == True:
if(self.runningTimer > 0):
Clock.schedule_interval(self.tick,1)
elif str(list(tafelobjecten.keys())[x]) == 'tafel5':
#self.time = config.tim5
if config.tb5start == True:
if(self.runningTimer > 0):
Clock.schedule_interval(self.tick,1)
elif str(list(tafelobjecten.keys())[x]) == 'tafel6':
#self.time = config.tim6
if config.tb6start == True:
if(self.runningTimer > 0):
Clock.schedule_interval(self.tick,1)
elif str(list(tafelobjecten.keys())[x]) == 'tafel7':
#self.time = config.tim7
if config.tb7start == True:
if(self.runningTimer > 0):
Clock.schedule_interval(self.tick,1)
elif str(list(tafelobjecten.keys())[x]) == 'tafel8':
#self.time = config.tim8
if config.tb8start == True:
if(self.runningTimer > 0):
Clock.schedule_interval(self.tick,1)
def pause(self):
tafelobjecten = self.parent.parent.ids
for x in range(0,8):
if list(tafelobjecten.values())[x] == self:
if str(list(tafelobjecten.keys())[x]) == 'tafel1':
config.tb1start = False
Clock.unschedule(self.tick)
if str(list(tafelobjecten.keys())[x]) == 'tafel2':
config.tb2start = False
Clock.unschedule(self.tick)
if str(list(tafelobjecten.keys())[x]) == 'tafel3':
config.tb3start = False
Clock.unschedule(self.tick)
if str(list(tafelobjecten.keys())[x]) == 'tafel4':
config.tb4start = False
Clock.unschedule(self.tick)
if str(list(tafelobjecten.keys())[x]) == 'tafel5':
config.tb5start = False
Clock.unschedule(self.tick)
if str(list(tafelobjecten.keys())[x]) == 'tafel6':
config.tb6start = False
Clock.unschedule(self.tick)
if str(list(tafelobjecten.keys())[x]) == 'tafel7':
config.tb7start = False
Clock.unschedule(self.tick)
if str(list(tafelobjecten.keys())[x]) == 'tafel8':
config.tb8start = False
Clock.unschedule(self.tick)
def resume(self, *_):
tafelobjecten = self.parent.parent.ids
for x in range(0,8):
if list(tafelobjecten.values())[x] == self:
if str(list(tafelobjecten.keys())[x]) == 'tafel1':
config.tb1start = True
Clock.schedule_interval(self.tick,1)
if str(list(tafelobjecten.keys())[x]) == 'tafel2':
config.tb2start = True
Clock.schedule_interval(self.tick,1)
if str(list(tafelobjecten.keys())[x]) == 'tafel3':
config.tb3start = True
Clock.schedule_interval(self.tick,1)
if str(list(tafelobjecten.keys())[x]) == 'tafel4':
config.tb4start = True
Clock.schedule_interval(self.tick,1)
if str(list(tafelobjecten.keys())[x]) == 'tafel5':
config.tb5start = True
Clock.schedule_interval(self.tick,1)
if str(list(tafelobjecten.keys())[x]) == 'tafel6':
config.tb6start = True
Clock.schedule_interval(self.tick,1)
if str(list(tafelobjecten.keys())[x]) == 'tafel7':
config.tb7start = True
Clock.schedule_interval(self.tick,1)
if str(list(tafelobjecten.keys())[x]) == 'tafel8':
config.tb8start = True
Clock.schedule_interval(self.tick,1) #pass
def stop(self, *_):
#TODO: implement stop button
pass
def tick(self, *_):
tafelobjecten = self.parent.parent.ids
if self.runningTimer > 0:
self.runningTimer -= 1
# publish de juiste tafel topic met de waarde van de restrerende tijd
for x in range(0,8):
if list(tafelobjecten.values())[x] == self:
client.publish(topic = str(list(tafelobjecten.keys())[x]), payload = str(self.runningTimer))
self.ids.Changelabel.text = str(time.strftime('%H:%M:%S',time.gmtime(self.runningTimer)))
else:
pass
class Main(GridLayout):
pass
class CrudeTimerApp(App):
pass
if __name__ == '__main__':
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.username_pw_set(config.settings['username'], config.settings['password'])
client.connect(config.settings['broker'])
t = threading.Thread(target=client.loop_start())
t.daemon = True
t.start()
CrudeTimerApp().run()
My .kv file:
#:kivy 1.10.1
################################### Widget template ##########################################
<CrudeTimerGrid>:
_python_access: Changelabel
id: timer
rows: 4
BoxLayout:
size_hint_y: 0.15
orientation: 'horizontal'
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
Label:
font_size: 20
text: 'Restrerende tijd:'
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
Label:
font_size: 20
id: Changelabel
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
BoxLayout:
size_hint_y: 0.15
orientation: 'horizontal'
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
Label:
font_size: 20
text: 'Pauzetijd:'
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
Label:
font_size: 20
text: '00'
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
BoxLayout:
size_hint_y: 0.2
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
Button:
text: "Pauze"
on_press: timer.pause()
Button:
text: "Hervatten"
on_press: timer.resume()
Button:
text: "Stoppen"
#on_press: timer.reset()
Label:
text: ''
################################### Widget template ##########################################
<Main#Widget>:
rows: 2 # 2 rijen
cols: 4 # 4 colums
padding: 10
spacing: 10
################################### Tafel 1 ##########################################
BoxLayout:
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
orientation: 'vertical'
Label:
size_hint_y: 0.15
font_size: 25
canvas.before:
Color:
rgba: .5, .5, .5, 1
Rectangle:
pos: self.pos
size: self.size
text: 'Tafel 1'
CrudeTimerGrid:
id: tafel1
################################### Tafel 1 ##########################################
################################### Tafel 2 ##########################################
BoxLayout:
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
orientation: 'vertical'
Label:
size_hint_y: 0.15
font_size: 25
canvas.before:
Color:
rgba: .5, .5, .5, 1
Rectangle:
pos: self.pos
size: self.size
text: 'Tafel 2'
CrudeTimerGrid:
id: tafel2
################################### Tafel 2 ##########################################
################################### Tafel 3 ##########################################
BoxLayout:
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
orientation: 'vertical'
Label:
size_hint_y: 0.15
font_size: 25
canvas.before:
Color:
rgba: .5, .5, .5, 1
Rectangle:
pos: self.pos
size: self.size
text: 'Tafel 3'
CrudeTimerGrid:
id: tafel3
################################### Tafel 3 ##########################################
################################### Tafel 4 ##########################################
BoxLayout:
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
orientation: 'vertical'
Label:
size_hint_y: 0.15
font_size: 25
canvas.before:
Color:
rgba: .5, .5, .5, 1
Rectangle:
pos: self.pos
size: self.size
text: 'Tafel 4'
CrudeTimerGrid:
id: tafel4
################################### Tafel 4 ##########################################
################################### Tafel 5 ##########################################
BoxLayout:
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
orientation: 'vertical'
Label:
size_hint_y: 0.15
font_size: 25
canvas.before:
Color:
rgba: .5, .5, .5, 1
Rectangle:
pos: self.pos
size: self.size
text: 'Tafel 5'
CrudeTimerGrid:
id: tafel5
################################### Tafel 5 ##########################################
################################### Tafel 6 ##########################################
BoxLayout:
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
orientation: 'vertical'
Label:
size_hint_y: 0.15
font_size: 25
canvas.before:
Color:
rgba: .5, .5, .5, 1
Rectangle:
pos: self.pos
size: self.size
text: 'Tafel 6'
CrudeTimerGrid:
id: tafel6
################################### Tafel 6 ##########################################
################################### Tafel 7 ##########################################
BoxLayout:
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
orientation: 'vertical'
Label:
size_hint_y: 0.15
font_size: 25
canvas.before:
Color:
rgba: .5, .5, .5, 1
Rectangle:
pos: self.pos
size: self.size
text: 'Tafel 7'
CrudeTimerGrid:
id: tafel7
################################### Tafel 7 ##########################################
################################### Tafel 8 ##########################################
BoxLayout:
canvas.before:
Color:
rgba: .5, .5, .5, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
orientation: 'vertical'
Label:
size_hint_y: 0.15
font_size: 25
canvas.before:
Color:
rgba: .5, .5, .5, 1
Rectangle:
pos: self.pos
size: self.size
text: 'Tafel 8'
CrudeTimerGrid:
id: tafel8
################################### Tafel 8 ##########################################
Main:
my config.py:
# MQTT broker settings.
settings = dict(
broker = '172.16.24.128',
username = 'pi',
password = 'Piaservice123'
)
# start topics.
startTopics = dict(
tafel1 = 'tafel1start',
tafel2 = 'tafel2start',
tafel3 = 'tafel3start',
tafel4 = 'tafel4start',
tafel5 = 'tafel5start',
tafel6 = 'tafel6start',
tafel7 = 'tafel7start',
tafel8 = 'tafel8start'
)
# time in seconds topics.
timeTopics = dict(
tafel1 = 'tafel1uren',
tafel2 = 'tafel2uren',
tafel3 = 'tafel3uren',
tafel4 = 'tafel4uren',
tafel5 = 'tafel5uren',
tafel6 = 'tafel6uren',
tafel7 = 'tafel7uren',
tafel8 = 'tafel8uren'
)
# Currenttime topics.
currentTime = dict(
tafel1 = 'tafel1',
tafel2 = 'tafel2',
tafel3 = 'tafel3',
tafel4 = 'tafel4',
tafel5 = 'tafel5',
tafel6 = 'tafel6',
tafel7 = 'tafel7',
tafel8 = 'tafel8'
)
# Global timer vars
tim1 = 0
tim2 = 0
tim3 = 0
tim4 = 0
tim5 = 0
tim6 = 0
tim7 = 0
tim8 = 0
# startbooleans
tb1start = False
tb2start = False
tb3start = False
tb4start = False
tb5start = False
tb6start = False
tb7start = False
tb8start = False
The class where all the magic happens is the class CrudeTimerGrid().
How can I make it so my timer won't reset everytime I click on my Resume button?
Edit: managed to fix my problem!
I made an event(self.event = Clock.schedule_interval(self.tick,1)) and have 2 buttons to pause and resume.
I use the pause button (Clock.unschedule(self.event)) to unschedule the event and the resume button (Clock.schedule_once(self.event)) to reschedule my timer.

You won't be able to unschedule a Kivy Clock event by using Clock.unschedule(self.tick). Please refer to the example below.
Kivy Clock ยป Unscheduling
Schedule
Replace
Clock.schedule_interval(self.tick, 1)
with
self.event = Clock.schedule_interval(self.tick, 1)
Unschedule
so that you can unschedule using
either
self.event.cancel()
or
Clock.unschedule(self.event)

Related

kivy move image only when image is pressed

I'm making a game in kivy where when you press on the soccer ball it moves. I want the ball to move only when I press exactly on the ball, if I press anywhere else it shouldn't move. Currently, the ball moves if I press anywhere close to the ball which Is not what I want. Is there anything I can do so that it only moves when I press exactly on the ball? Below is my code!
main.py
class Ball(Image):
velocity = NumericProperty(0)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.source = "icons/ball.png"
self.velocity = 275
return super().on_touch_down(touch)
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
self.source = "icons/ball.png"
return super().on_touch_up(touch)
class MainApp(App):
GRAVITY = 300
def move_ball(self, time_passed):
ball = self.root.ids.game_screen.ids.ball
ball.y = ball.y + ball.velocity * time_passed
ball.velocity = ball.velocity - self.GRAVITY * time_passed
def start_game(self):
Clock.schedule_interval(self.move_ball, 1/60.)
kivy code
#:import utils kivy.utils
<GameScreen>:
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#39B3F2")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .1
Image:
source: "icons/sun.png"
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .2
Image:
source: "icons/clouds.png"
GridLayout:
rows: 1
pos_hint: {"bottom": 1, "left": 1}
size_hint: 1, .5
Image:
source: "icons/Field4.png"
allow_stretch: True
keep_ratio: False
pos: self.pos
Button:
size_hint: None, None
font_size: dp(20)
font_name: 'SackersGothicStd-Medium.otf'
text: "Start Game"
color: "gold"
pos_hint: { "center_x": 0.5, "center_y": 0.3}
size: 150, 55
size_hint: None, None
background_normal: ''
background_color: (57/255.0, 179/255.0, 242/255.0, .10)
on_release:
self.disabled = True
self.opacity = 0
root.play_sound()
app.start_game()
Ball:
source: "icons/ball.png"
size_hint: None, None
size: 500, 500
pos_hint: {"center_x": 0.5}
id: ball
I also tried keep_ratio: True
allow_stretch: True
but it still didn't work
ball is a circle (or rather disk) which has some radius - if you check distance between ball center and mouse position and it is smaller then radius then you clicked inside this circle or disk.
More precisely using the Pythagorean formula a**2 + b**2 == c**2
or rather disk's definition : x**2 + y**2 <= r**2
(ball.centerx - touch.pos.x)**2 + (ball.centery - touch.pos.y)**2 <= radius**2
Kivy has class Vector to make it simpler - but I can't test it.
Vector(ball.center).distance(touch.pos) <= radius
And you should use it instead of collide_point()
class Ball(Image):
velocity = NumericProperty(0)
radius = 50 # you have to get it manually from image
def on_touch_down(self, touch):
if Vector(self.center).distance(touch.pos) <= radius:
self.source = "icons/ball.png"
self.velocity = 275
return super().on_touch_down(touch)
def on_touch_up(self, touch):
if Vector(self.center).distance(touch.pos) <= radius:
self.source = "icons/ball.png"
return super().on_touch_up(touch)
I'm not sure but maybe it even keeps ball position as Vector() and you could use directly
ball.center.distance(touch.pos) <= radius

Why doesn't shapes drawn on canvas appear on a single column kivy?

I'm trying to have an infinite amount of shapes drawn on a canvas so I decided to use a scrollview to do this. However, when I add a scroll view none of the shapes appear on the canvas.
py:
# draws the shape
def draw_streak(self, obj):
name = obj.id
with open("streak.json", "r") as file:
read = json.load(file)
for key in read.keys():
if key == name:
with open("streak.json", "r+") as f:
data = json.load(f)
get_score = data.get(key, {}).get('score')
can = self.root.get_screen("three")
new_pos = can.pos
for x in range(-1, get_score): # had to use -1 to get correct amount of shapes
with can.ids.my_box.canvas:
Color(0, 1, 0, .75, mode='rgba')
rect = Rectangle(pos=new_pos, size=(30,30))
new_pos[0] += rect.size[1]
new_pos[0] += rect.size[0]
kv:
<ScreenThree>
id: screen_three
name: "three"
on_leave: my_box.canvas.clear()
on_leave: selected_streak.canvas.clear()
on_leave: del_space.canvas.clear()
GridLayout:
cols: 1
rows: 2
GridLayout:
cols: 3
rows: 1
AnchorLayout:
anchor_x: "left"
anchor_y: "top"
Button:
text: "Back"
size: 50, 25
size_hint: None, None
font_size: 18
on_release: app.root.current = "two"
AnchorLayout:
id: selected_streak
anchor_x: "center"
anchor_y: "top"
AnchorLayout:
id: del_space
anchor_x: "right"
anchor_y: "top"
ScrollView:
do_scroll_x: False
do_scroll_y: True
GridLayout:
cols: 1
id: my_box
size_hint_y: None
height: self.minimum_height
row_force_default: True
row_default_height: 50
Also, would changing the x size hint make it so that shapes that don't fit will be moved down the screen?
EDIT
Draw streak is called when pressing a button at a certain time.
...
elif delay > time.time() > self.honey: # on time (green)
child.background_normal = ''
child.background_color = [0, 1, 0, .95]
child.unbind(on_press=self.early_click)
child.bind(on_press=self.add_score)
child.bind(on_press=self.display_streak)
child.bind(on_press=self.draw_streak)
child.unbind(on_press=self.late_click)
The key of a json value score has the same name as the buttons id.
EDIT
Screenshots
Question 3
is there a way for my rectangles to move down once they exit the
screen, or once they reach a certain number?
Solution
Draw the rectangles from left to right, and top to bottom (lr-tb) orientation.
Snippets
root = App.get_running_app().root
can = root.get_screen('three')
new_pos = can.pos
new_pos[1] = root.height - 60 # below the Back button
for x in range(-1, get_score): # had to use -1 to get correct amount of shapes
with can.canvas:
Color(0, 1, 0, .75, mode='rgba')
rect = Rectangle(pos=new_pos, size=(30,30))
new_pos[0] += rect.size[0] * 2 # x co-ordinate
if new_pos[0] >= (root.width - rect.size[0]):
new_pos[0] = 0 # x co-ordinate
new_pos[1] -= rect.size[0] * 2 # y co-ordinate
Output - Demo
Question 2
wanted the shape to be drawn on page three, not the button.
Solution
Replace with can.ids.my_box.canvas: with with can.canvas:
Snippets
can = App.get_running_app().root.get_screen('three')
new_pos = can.pos
for x in range(-1, get_score): # had to use -1 to get correct amount of shapes
with can.canvas:
Color(0, 1, 0, .75, mode='rgba')
rect = Rectangle(pos=new_pos, size=(30,30))
new_pos[0] += rect.size[1]
new_pos[0] += rect.size[0]
Output
Draw shapes on button in ScrollView
Implement a method get_cell() to get an instance of the button inside my_box
Snippets
def get_cell(self, key):
obj = App.get_running_app().root.get_screen('three')
for row in reversed(obj.ids.my_box.children):
if row.children: # children is not empty
for child in reversed(row.children):
if isinstance(child, Button):
if child.id == key:
return child
elif isinstance(row, Button):
if row.id == key:
return row
return None
def draw_streak(self, obj):
...
button = self.get_cell(key) # get button instance
if button: # Not None
new_pos = button.pos # get button's position
for x in range(-1, get_score):
with button.canvas:
Color(0, 1, 0, .75, mode='rgba')
rect = Rectangle(pos=new_pos, size=(30,30))
new_pos[1] += rect.size[1] # y co-ordinate
new_pos[0] += rect.size[0] # x co-ordinate
Output

How can i fix my bouncing ball glitching through the bottom of my screen?

If your run it once the ball is about to stop it starts skipping up and down a little bit and disappears through the floor.
So i was following the kivy tutorial for the pong game and half way through i thought why not make the ball have gravity...i kind of figured that out though I don't know wether it's good code or not but that's not my problem. I already compared it to some other code basically doing the same and didn't find any differences. Could somebody tell me what i have done wrong? (Sorry i had to paste all my code in here but i don't know where the problem is...)
import kivy
kivy.require("1.10.1")
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 random import randint
class PongBall(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def update_speed(self):
self.velocity[1] = self.velocity[1] - 15/60
self.pos = Vector(*self.velocity) + self.pos
class PongGame(Widget):
ball = ObjectProperty(None)
def serve_ball(self):
self.ball.center = self.center
self.ball.velocity = Vector(0, 0)
def update(self, dt):
self.ball.update_speed()
if (self.ball.y < 0) or (self.ball.top > self.height):
self.ball.velocity_y *= -1
if (self.ball.x < 0) or (self.ball.right > self.width):
self.serve_ball()
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()
This is my kv file:
#:kivy 1.10.1
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongGame>:
ball: pong_ball
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.height * 9 / 10
text: "0"
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.height * 9 / 10
text: str(pong_ball.velocity[1])
PongBall:
id: pong_ball
center: self.parent.center
I want the ball to slow down until it lays on the ground and doesn't move.
this is just a common problem with floats ...
just in your update speed function do something like
if velocity <= 1e-6: # some really small number
velocity = 0.0
its also probably a good idea to fix your y position in your move function
if (self.ball.y < 0) or (self.ball.top > self.height):
self.ball.y = 0.1
self.ball.velocity_y *= -1

conditional expression in kivy lang

I'm trying to chance the opacity of a button through "on-press" function. For example, on the kv file code below, I want to chance the opacity of the button (bt1) by pressing the Button A.
So, by pressing the Button A it should check if the opacity of (bt1) is equal to 0, if its true, change it to 1, if its false it should change the opacity of (bt2) from 0 to 1. Any idea how to do it? Thanks in advance.
FloatLayout:
size_hint: None, None
Button:
id: bt1
pos: 200, 300
opacity: 0
on_press: self.opacity = 0
Button:
id: bt2
pos: 300, 300
opacity: 0
on_press: self.opacity = 0
Button:
id: bt3
pos: 400, 300
opacity: 0
on_press: self.opacity = 0
Button:
pos: 0, 0
text: 'A'
on_press:
bt3.opacity = 1 if bt2.opacity == 1 else 0
bt2.opacity = 1 if bt1.opacity == 1 else 0
bt1.opacity = 1 if bt1.opacity == 0 else 1
Button:
pos: 100, 0
text: 'B'
on_press:
bt3.opacity = 1 if bt2.opacity == 1 else 0
bt2.opacity = 1 if bt1.opacity == 1 else 0
bt1.opacity = 1 if bt1.opacity == 0 else 1
Button:
pos: 200, 0
text: 'C'
on_press:
bt3.opacity = 1 if bt2.opacity == 1 else 0
bt2.opacity = 1 if bt1.opacity == 1 else 0
bt1.opacity = 1 if bt1.opacity == 0 else 1
The solution is to use if...elif....
Solution - Change Button's Text
When button A is pressed, check if bt1's text is an empty string. If it's true then change bt1's text to 'A'. When button B is pressed, check if bt2's text is an empty string. If it is true then change bt2's text to 'B'.
Snippets
Button:
pos: 0, 0
text: 'A'
on_press:
print("Button {} pressed".format(self.text))
print("\tlen(bt1.text)={}".format(len(bt1.text)))
# Assign Text
if len(bt1.text) == 0: bt1.text = self.text
elif len(bt2.text) == 0: bt2.text = self.text
# Assign Opacity
if bt2.opacity == 1: bt3.opacity = 1
elif bt1.opacity == 1: bt2.opacity = 1
elif bt1.opacity == 0: bt1.opacity = 1
Button:
pos: 100, 0
text: 'B'
on_press:
print("Button {} pressed".format(self.text))
print("\tlen(bt1.text)={}".format(len(bt1.text)))
# Assign Text
if len(bt1.text) == 0: bt1.text = self.text
elif len(bt2.text) == 0: bt2.text = self.text
# Assign Opacity
if bt2.opacity == 1: bt3.opacity = 1
elif bt1.opacity == 1: bt2.opacity = 1
elif bt1.opacity == 0: bt1.opacity = 1
Example - Change Button's Text
main.py
from kivy.lang import Builder
from kivy.base import runTouchApp
runTouchApp(Builder.load_string('''
FloatLayout:
size_hint: None, None
size: 100, 100
Button:
id: bt1
pos: 200, 300
opacity: 0
on_press: self.opacity = 0
Button:
id: bt2
pos: 300, 300
opacity: 0
on_press: self.opacity = 0
Button:
id: bt3
pos: 400, 300
opacity: 0
on_press: self.opacity = 0
Button:
pos: 0, 0
text: 'A'
on_press:
print("Button {} pressed".format(self.text))
print("\tlen(bt1.text)={}".format(len(bt1.text)))
# Assign Text
if len(bt1.text) == 0: bt1.text = self.text
elif len(bt2.text) == 0: bt2.text = self.text
# Assign Opacity
if bt2.opacity == 1: bt3.opacity = 1
elif bt1.opacity == 1: bt2.opacity = 1
elif bt1.opacity == 0: bt1.opacity = 1
Button:
pos: 100, 0
text: 'B'
on_press:
print("Button {} pressed".format(self.text))
print("\tlen(bt1.text)={}".format(len(bt1.text)))
# Assign Text
if len(bt1.text) == 0: bt1.text = self.text
elif len(bt2.text) == 0: bt2.text = self.text
# Assign Opacity
if bt2.opacity == 1: bt3.opacity = 1
elif bt1.opacity == 1: bt2.opacity = 1
elif bt1.opacity == 0: bt1.opacity = 1
Button:
pos: 200, 0
text: 'C'
on_press:
print("Button {} pressed".format(self.text))
# Assign Opacity
if bt2.opacity == 1: bt3.opacity = 1
elif bt1.opacity == 1: bt2.opacity = 1
elif bt1.opacity == 0: bt1.opacity = 1
'''))
Output - Change Button's Text
Solution - Change Button's Opacity
When button A is pressed, check if bt1's opacity is equal to 0. If it's true then change it to 1. If it's false then change bt2's opacity from 0 to 1.
Snippets
Button:
pos: 0, 0
text: 'A'
on_press:
if bt1.opacity == 0: bt1.opacity = 1
elif bt1.opacity == 1: bt2.opacity = 1
Example - Change Button's Opacity
main.py
from kivy.lang import Builder
from kivy.base import runTouchApp
runTouchApp(Builder.load_string('''
FloatLayout:
size_hint: None, None
size: 100, 100
Button:
id: bt1
text: 'bt1'
pos: 200, 300
opacity: 0
on_press: self.opacity = 0
Button:
id: bt2
text: 'bt2'
pos: 300, 300
opacity: 0
on_press: self.opacity = 0
Button:
pos: 0, 0
text: 'A'
on_press:
if bt1.opacity == 0: bt1.opacity = 1
elif bt1.opacity == 1: bt2.opacity = 1
'''))
Output - Solution 1

How to display game on the screen?

I'm trying to make an kivy app with starting menu, but I can't display my Pong Game on the second screen. How should I refer to the game to make it visible? I tried and searched but can't find anything. I'm 100% sure that PongGame work corectly, I just can't display it. It would be great if someone could show me how to do it corectly.
Main.py:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, WipeTransition
from kivy.properties import ObjectProperty
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
from kivy.uix.popup import Popup
from kivy.uix.label import Label
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))
if self.player1.score == 10:
popup = Popup(title='Test popup', content=Label(text='Hello world'), auto_dismiss=False)
return popup
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 ScreenThree(Screen):
pass
class ScreenTwo(Screen):
pass
class ScreenOne(Screen):
pass
class Manager(ScreenManager):
screen_one = ObjectProperty(None)
screen_two = ObjectProperty(None)
screen_three = ObjectProperty(None)
class ScreensApp(App):
def build(self):
m = Manager(transition=WipeTransition())
return m
if __name__ == '__main__':
ScreensApp().run()
screen.kv:
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongPaddle>:
size: 25, 200
canvas:
Rectangle:
pos:self.pos
size:self.size
<PongGame>:
ball: pong_ball
player1: player_left
player2: player_right
canvas:
Rectangle:
pos: self.center_x-5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: str(root.player1.score)
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: str(root.player2.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
<ScreenOne>:
Button:
text: "Screen 1 >> Screen 2"
on_press: root.manager.current = 'screen2'
<ScreenTwo>:
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
<ScreenThree>:
Button:
text: "Screen 3 >> Screen 1"
on_press: root.manager.current = 'screen1'
<Manager>:
id: screen_manager
screen_one: screen_one
screen_two: screen_two
screen_three: screen_three
ScreenOne:
id: screen_one
name: 'screen1'
manager: screen_manager
ScreenTwo:
id: screen_two
name: 'screen2'
manager: screen_manager
ScreenThree:
id: screen_three
name: 'screen3'
manager: screen_manager
Well! there were a lot of errors in your program and I had to make ton's of improvement. (I understand it as you are beginner)
First of all, please read complete kivy language documentation, as I can clearly see that you directly started with coding without grasping the basics.
You may make couple of good games but in the long run you will face such problems which can't be solved without clear concepts.
And unfortunately you won't be able to discover the true power of kivy. :)
You might also wanna do revision of your python concepts.
Some improvements aren't worth mentioning but were important, you will get an idea when you read the code.
Improvement 1:
An application can be built if you return a widget on build(), or if you set self.root.(But you cannot make the application again n again)
as you did here:
<ScreenTwo>:
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
Improvement 2:
As you click on button play ping pong which is of screen game. your game starts with serve of ball.
on_release: root.current = 'game';game.serve_ball()
(for knowledge)
If you still get black screen you might want to check the name of kivy file, for that you could either go to kivy documentation or this link
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 __init__(self, *args, **kwargs):
super(PongGame, self).__init__(*args, **kwargs)
Clock.schedule_interval(self.update, 1.0 / 60.0)
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 Manager(ScreenManager):
pass
class ScreensApp(App):
def build(self):
self.load_kv('t6.kv')
return Manager(transition=WipeTransition())
if __name__ == '__main__':
ScreensApp().run()
Here is the kv file.
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongPaddle>:
size: 25, 200
canvas:
Rectangle:
pos:self.pos
size:self.size
<PongGame>:
ball: pong_ball
player1: player_left
player2: player_right
canvas:
Rectangle:
pos: self.center_x-5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: str(root.player1.score)
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: str(root.player2.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
<Manager>:
id: screen_manager
Screen:
name: 'home'
Button:
text: 'Play Ping Pong'
halign: 'center'
valign: 'middle'
font_size: 100
text_size: self.size
on_release: root.current = 'game';game.serve_ball()
Screen:
name: 'game'
PongGame:
id: game

Categories

Resources