I need to make a countdown and then use it on mobile phone.
I heard that kivy is good graphic for mobile phones, so I tried to use this.
Im trying to run infinite while loop that will count down the time and the kivy app that will create window and display remaining time, both at the same time. I am also using pygame clocks to time it. But when I run this code the while loop is counting the time, but it only creates white blank window and after some time another window.
What am I doing wrong ?
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.core.window import Window
from multiprocessing import Process
import pygame
class MyFloat(FloatLayout):
def __init__(self, **kwargs):
super(MyFloat, self).__init__(**kwargs)
self.background = Image(source="textures/background.jpg", allow_stretch=True, keep_ratio=False)
self.add_widget(self.background)
class MyApp(App):
def build(self):
return MyFloat()
def check_time():
global days
global hours
global minutes
global seconds
global miliSeconds
miliSeconds -= 1
if miliSeconds < 0:
miliSeconds += 100
seconds -= 1
if seconds < 0:
seconds += 60
minutes -= 1
if minutes < 0:
minutes += 60
hours -= 1
if hours < 0:
hours += 24
days -= 1
if days < 0:
global active
active = False
print("End")
print("days: ", days)
print("hours: ", hours)
print("minutes: ", minutes)
print("seconds: ", seconds)
print("miliseconds: ", miliSeconds)
def loop():
while active:
check_time()
clock.tick(100)
clock = pygame.time.Clock()
days = 0
hours = 0
minutes = 0
seconds = 30
miliSeconds = 0
active = True
if __name__ == "__main__":
p1 = Process(target=loop)
p2 = Process(target=MyApp().run)
p1.start()
p2.start()
Just change the last part of your code to:
if __name__ == "__main__":
p1 = Process(target=loop)
p1.start()
MyApp().run()
You don't need pygame nor while True because kivy has class Clock with functions
# call my_callback every 0.5 seconds
Clock.schedule_interval(my_callback, 0.5)
# call my_callback in 5 seconds
Clock.schedule_once(my_callback, 5)
# call my_callback as soon as possible (usually next frame.)
Clock.schedule_once(my_callback)
Start task:
task = Clock.schedule_interval(check_time, 0.1)
Stop task:
task.cancel()
And your function will get delta_time between executions so you can check if it was executed exactly after 0.1 second or not. And you can use this to display correct time.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.clock import Clock
class MyFloat(FloatLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.background = Image(source="textures/background.jpg", allow_stretch=True, keep_ratio=False)
self.add_widget(self.background)
class MyApp(App):
def build(self):
return MyFloat()
def check_time(dt):
global days
global hours
global minutes
global seconds
global miliSeconds
global active
miliSeconds -= 1
if miliSeconds < 0:
miliSeconds += 100
seconds -= 1
if seconds < 0:
seconds += 60
minutes -= 1
if minutes < 0:
minutes += 60
hours -= 1
if hours < 0:
hours += 24
days -= 1
if days < 0:
active = False
print("End")
task.cancel() # <-- stoping task
print("days: ", days)
print("hours: ", hours)
print("minutes: ", minutes)
print("seconds: ", seconds)
print("miliseconds: ", miliSeconds)
days = 0
hours = 0
minutes = 0
seconds = 1
miliSeconds = 0
active = True
if __name__ == "__main__":
task = Clock.schedule_interval(check_time, 0.1) # <-- starting task
MyApp().run()
EDIT:
Example which uses Clock to display current time in Label
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.clock import Clock
import datetime
class MyFloat(FloatLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.label = Label()
self.add_widget(self.label)
self.task = Clock.schedule_interval(self.update_label, 0.1)
def update_label(self, dt):
now = datetime.datetime.now()
self.label.text = now.strftime("%Y.%m.%d %H:%M.%S")
class MyApp(App):
def build(self):
return MyFloat()
if __name__ == "__main__":
MyApp().run()
BTW:
You may need multiprocessing/threading when you have to run long-running code. But it may need also Queue to communicate with main process. Usually GUIs can't modify widgets in separated processes/threads.
Related
I would like to display the time with kivy. I don't want that the label use the whole page. How can I do ?
I want it to appear the same as in the following photo:
from kivy.app import App
from datetime import datetime
from datetime import timedelta
from kivy.clock import Clock
from kivy.uix.label import Label
class myApp(App):
def build(self):
self.now = datetime.now()
# Schedule the self.update_clock function to be called once a second
Clock.schedule_interval(self.update_clock, 1)
self.my_label = Label(text= self.now.strftime('%H:%M:%S'))
return self.my_label # The label is the only widget in the interface
def update_clock(self, *args):
# Called once a second using the kivy.clock module
# Add one second to the current time and display it on the label
self.now = self.now + timedelta(seconds = 1)
self.my_label.text = self.now.strftime('%H:%M:%S')
myApp().run()
I need to create a 60seconds countdown timer in kivy. It will start immediately when the code is run and it should print 'countdown completed' when the countdown is in 0.
Have not been able to derive any code for this. I just need a simple 60s countdown timer
As #Bryan Oakley mentioned:
Stackoverflow isn't designed to be a free code-writing service
but I love Kivy so maybe this will help someone someday...
from kivy.app import App
from kivy.uix.label import Label
from kivy.clock import Clock
class countDownClock(Label):
counter = 60
def update(self, *args):
if self.counter > 0:
self.text = str(self.counter -1)
self.counter -= 1
else:
self.text = "KaBoom"
class TimeApp(App):
def build(self):
countdown = countDownClock()
Clock.schedule_interval(countdown.update, 1)
return countdown
if __name__ == "__main__":
TimeApp().run()
this was developed using Tkinter,
from tkinter import *
import time, sys
window = Tk()
window.title("Countdown")
print('\nHello\nInstructions: add time.\n')
hourz = input('Hours: ')
minz = input('Minutes: ')
secz = input('Seconds: ')
hour = int(hourz); min = int(minz); sec = int(secz)
var = StringVar()
var.set("00:00:00")
label_title = Label(window, textvariable=var)
label_title.pack()
window.update()
print('Check the window !\n')
while hour > -1:
while min > -1:
while sec > 0:
sec = sec - 1
time.sleep(1)
sec1 = ('%02.f' % sec) # format
min1 = ('%02.f' % min)
hour1 = ('%02.f' % hour)
var.set('\r' + str(hour1) + ' : ' + str(min1) + ' : ' + str(sec1))
window.update()
min = min - 1
sec = 60
hour = hour - 1
min = 59
print(' Finish !\n')
window.mainloop()
I have a countdown timer that starts at a random integer number within the range given by the randint() arguments and counts down to zero. The aim is to make the timer restart at a new random number the first time it reaches zero (i.e. lap function) and to display "FINISH" the second time it reaches zero.
This is my first time using kivy, so apologies if the solution is obvious. At present I only require two iterations, but I may need to adjust this later so that the timer will lap any number of times before finally stopping. The number of laps will be determined in the code before running the app, and not by the app user whilst running the app.
from kivy.app import App
from kivy.uix.label import Label
from kivy.animation import Animation
from kivy.properties import NumericProperty
from random import randint
class IncrediblyCrudeClock(Label):
for i in range(2):
r=randint(3,7)
a = NumericProperty(r) # Number of seconds to countdown
def start(self): #Function to initiate the countdown
Animation.cancel_all(self) # stop any current animations
self.anim = Animation(a=0, duration=self.a) #a=0 sets the
#final destination of a. duration sets the time taken to reach stopping
#point (i.e 5 seconds for a=5)
def finish_callback(animation, incr_crude_clock):
if self.i==1:
incr_crude_clock.text = "FINISHED" #when the clock
#reaches zero, display "FINISHED"
self.anim.bind(on_complete=finish_callback) #call the
#finish_callback function once a=0
self.anim.start(self) #Start the animation (otherwise clock
#stuck at 5 for a=5)
class TimeApp(App):
def build(self):
crudeclock = IncrediblyCrudeClock()
crudeclock.start()
return crudeclock
if __name__ == "__main__":
TimeApp().run()
<IncrediblyCrudeClock>
text: str(round(self.a, 1))
The app does run as expected for the first countdown. A random number is selected and the timer counts down to zero, but it stops and displays "FINISHED" after the first count down. It seems as though the for loop is iterating from zero to one before the app is actually started, so, by the time the countdown starts, i is already equal to 1 (in stead of running first from a to zero with i=0 and then from new a to zero with i=1). I imagine this is because the for loop is in the wrong place (i.e. not upon calling the start function), but I have been unable to work out how to rectify this.
This is also my first time using stack overflow, so please let me know if you need to know anything else.
Here is a version that repeats the countdown a specified number of times:
from random import randint
from kivy.animation import Animation
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.label import Label
class IncrediblyCrudeClock(Label):
a = NumericProperty(0) # Number of seconds to countdown
def __init__(self, **kwargs):
self.max_laps = kwargs.pop('laps', 2) # default is to do 2 laps
self.lap_counter = 0
super(IncrediblyCrudeClock, self).__init__(**kwargs)
def start(self, *args):
self.lap_counter += 1
self.a = randint(3, 7)
self.anim = Animation(a=0, duration=self.a)
if self.lap_counter >= self.max_laps:
# this is the last lap, set on_complete to call self.finish_callback
self.anim.bind(on_complete=self.finish_callback)
else:
# not finished yet, call self.start again
self.anim.bind(on_complete=self.start)
print('starting anim number', self.lap_counter)
self.anim.start(self)
def finish_callback(self, animation, incr_crude_clock):
print('in finish_callback')
self.text = 'FINISHED'
Builder.load_string('''
<IncrediblyCrudeClock>
text: str(round(self.a, 1))
''')
class TimeApp(App):
def build(self):
# specify the number of repetitions in the constructor
crudeclock = IncrediblyCrudeClock(laps=3)
crudeclock.start()
return crudeclock
if __name__ == "__main__":
TimeApp().run()
Hard to understand your code, but here is a version of your IncrediblyCrudeClock that works:
class IncrediblyCrudeClock(Label):
a = NumericProperty(0) # Number of seconds to countdown
def start(self):
self.a = randint(3, 7)
self.anim = Animation(a=0, duration=self.a)
self.anim.bind(on_complete=self.secondAnim)
print('starting first anim')
self.anim.start(self)
def secondAnim(self, animation, incr_crude_clock):
self.a = randint(3, 7)
self.anim = Animation(a=0, duration=self.a)
self.anim.bind(on_complete=self.finish_callback)
print('starting second anim')
self.anim.start(self)
def finish_callback(self, animation, incr_crude_clock):
print('in finish_callback')
self.text = 'FINISHED'
This is a very simple approach. I'm sure that the start and secondAnim methods could be combined as one method with a bit more logic.
I am trying to create a clock with 3 arcs, one for hours, minutes, and seconds. The app currently draws each of the arcs, with different colors and radii as expected. The arcs also fill according to the time at application launch. The red arc represents hours, the green arc represents minutes, and the blue arc represents seconds. From here, I am trying to update the canvas so that the arcs change according to the time (once every second). I would also like a label that displays the time in the center of the clock. Can anyone help me with this problem? I am relatively new to Kivy so I'm not really sure what I should be doing.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Line
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import Clock
from datetime import datetime
# update canvas every second
# display time in center of clock
class drawClock(FloatLayout):
def __init__(self, **kwargs):
super(drawClock, self).__init__(**kwargs)
self.bind(pos=self.updateClock)
self.bind(size=self.updateClock)
def updateClock(self, *kwargs):
with self.canvas:
now = datetime.now()
hour = now.hour
minute = now.minute
second = now.second
hourAngle = (hour%12) * 30
minuteAngle = minute * 6
secondAngle = second * 6
Line(circle = (self.center_x, self.center_y, 80, 0, hourAngle), width = 2, color = Color(1,0,0))
Line(circle = (self.center_x, self.center_y, 65, 0, minuteAngle), width = 2, color = Color(0,1,0))
Line(circle = (self.center_x, self.center_y, 50, 0, secondAngle), width = 2, color = Color(0,0,1))
class mainApp(App):
def build(self):
return drawClock()
if __name__ == '__main__':
mainApp().run()
You have to use the Clock.schedule_interval() to update the painting, but instead of creating new Lines you must reuse them so you save resources:
from kivy.app import App
from kivy.graphics import Color, Line
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import Clock
from datetime import datetime
class drawClock(FloatLayout):
def __init__(self, **kwargs):
super(drawClock, self).__init__(**kwargs)
self.bind(pos=self.updateClock)
self.bind(size=self.updateClock)
Clock.schedule_interval(self.updateClock, 1)
self.create_canvas()
def create_canvas(self):
with self.canvas:
self.hour_line = Line(width = 2, color = Color(1,0,0))
self.minute_line = Line(width = 2, color = Color(0,1,0))
self.second_line = Line(width = 2, color = Color(0,0,1))
def updateClock(self, *kwargs):
now = datetime.now()
hour = now.hour
minute = now.minute
second = now.second
hourAngle = (hour%12) * 30
minuteAngle = minute * 6
secondAngle = second * 6
self.hour_line.circle = self.center_x, self.center_y, 80, 0, hourAngle
self.minute_line.circle = self.center_x, self.center_y, 65, 0, minuteAngle
self.second_line.circle = self.center_x, self.center_y, 50, 0, secondAngle
class mainApp(App):
def build(self):
return drawClock()
if __name__ == '__main__':
mainApp().run()
Use the update() method. This is a crude way, but it will give you what you want. The update method basically updates the window.
A more efficient way to do it is use the after() method.
The after basically tells what to do after, so figure it out.
window.after(delay_ms, callback=None, *args)
Those are the paramters for after().
For example, this would be to wait one second then carry out func up
window.after(1000, up)
I'm quite new to python programming and I'm currently building a photobooth using Kivy and Python.
In general it is working (I can press the button and it starts the function to take 3 pictures and updates the tumbnail on the screen) , but I'm not able to change the label text (actionLabel) to show a countdown before the takePhotos function starts.
import os, time, Image, sys, datetime, subprocess,glob
import kivy
kivy.require('1.10.0') # replace with your current kivy version !
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.image import Image as kivyImage
from kivy.clock import Clock
from kivy.graphics import Color, Rectangle
import RPi.GPIO as GPIO
#GPIO varialbes
#buttonPin = 23
GPIO.setmode(GPIO.BCM)
GPIO.setup(23,GPIO.IN)
GPIO_status = False
# Some variables
photoTitle = "My first Photobox!"
total_photos = 3 #Number of photos to be takes
#Function for photo taking
def takePhotos():
#Take first picture - Folder for inbound pictures /home/pi/PB_Inbox/photobooth%H%M%S.jpg
time.sleep(3)
subprocess.call("gphoto2 --capture-image-and-download --filename /home/pi/PB_Inbox/photobooth%H%M%S.jpg", shell=True)
#Take all other picture
for x in range (0,total_photos-1):
subprocess.call("gphoto2 --capture-image-and-download --filename /home/pi/PB_Inbox/photobooth%H%M%S.jpg", shell=True)
#Process pictures
subprocess.call("sudo sh /home/pi/Documents/Photo_Booth/Image_prep_3", shell=True)
print('done')
class MyApp(App):
# Display the latest thumbnail
photo = kivyImage(source="/home/pi/PB_Thumb/Thumb.png")
actionLabel = Label(text="Push the button", size_hint=(1, 0.2),color=[1,0,0,1],font_size='40sp')
def build(self):
# Set up the layout
photobox = GridLayout(rows=3, spacing=10, padding=10)
# Create the UI objects (and bind them to callbacks, if necessary)
headerLabel = Label(text="The Greatest Photobox", size_hint=(1, 0.1),font_size='40sp') # Button: 20% width, 100% height
# Add the UI elements to the layout
photobox.add_widget(headerLabel)
photobox.add_widget(self.photo)
photobox.add_widget(self.actionLabel)
# Periodically refresh the displayed photo using the callback function
Clock.schedule_interval(self.callback, 0.3)
return photobox
# Callback for thumbnail refresh and listening to GPIO for input
def callback(self, instance):
self.photo.reload()
if self.readSensor() == False:
pass
#print('waiting')
else:
#provided as an argument
takePhotos()
#Read status of the sensor and return True if Buzzer has been pushed
def readSensor(self):
sensor = GPIO.input(23)
if sensor == 1:
return True
else:
return False
if __name__ == '__main__':
MyApp().run()
Can someone show me how to do this?
Thx
Use Clock.create_trigger to trigger the count down.
Remove the time.sleep(3) in function, takePhotos().
Use Clock.schedule_once to invoke function takePhotos() i.e. Clock.schedule_once(takePhotos, 3)
Separate the Kivy App into Python Script and kv file.
Programming Guide ยป Events and Properties
In Kivy applications, you have to avoid long/infinite loops or sleeping.
Example
main.py
import os, time, sys, datetime, subprocess, glob
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from kivy.properties import ObjectProperty, NumericProperty
import RPi.GPIO as GPIO
# GPIO variables
# buttonPin = 23
GPIO.setmode(GPIO.BCM)
GPIO.setup(23, GPIO.IN)
GPIO_status = False
# Some variables
total_photos = 3 # Number of photos to be takes
# Function for photo taking
def takePhotos(dt):
# Take 3 picture
for x in range(total_photos):
subprocess.call("gphoto2 --capture-image-and-download --filename /home/pi/PB_Inbox/photobooth%H%M%S.jpg", shell=True)
# Process pictures
subprocess.call("sudo sh /home/pi/Documents/Photo_Booth/Image_prep_3", shell=True)
print('done')
class PhotoBox(BoxLayout):
photo = ObjectProperty(None)
action_button = ObjectProperty(None)
count_down_trigger = ObjectProperty(None)
count = NumericProperty(3)
def __init__(self, **kwargs):
super(PhotoBox, self).__init__(**kwargs)
# Display the latest thumbnail
self.photo.source = "/home/pi/PB_Thumb/Thumb.png"
self.count_down_trigger = Clock.create_trigger(self.count_down, 1)
def on_press_camera_button(self):
self.count = 3
self.count_down_trigger()
def count_down(self, dt):
self.action_button.text = str(self.count)
self.count -= 1
if self.count >= 0:
self.count_down_trigger()
else:
self.action_button.text = "Say Cheese!"
# Periodically refresh the displayed photo using the callback function
# Clock.schedule_interval(self.callback, 0.3) # infinite loop
Clock.schedule_once(self.callback, 0.3)
self.action_button.text = "Push the button" # reset text
# Callback for thumbnail refresh and listening to GPIO for input
def callback(self, dt):
self.photo.reload()
if self.readSensor():
# provided as an argument
Clock.schedule_once(takePhotos, 3) # call takePhotos method once after 3 seconds
else:
pass
#print('waiting')
# Read status of the sensor and return True if Buzzer has been pushed
def readSensor(self):
sensor = True # GPIO.input(23)
if sensor == 1:
return True
else:
return False
class MyApp(App):
title = "My first Photobox!"
def build(self):
return PhotoBox()
if __name__ == '__main__':
MyApp().run()
my.kv
#:kivy 1.10.0
<PhotoBox>:
photo: photo
action_button: action_button
orientation:'vertical'
spacing: 10
padding: 10
Label:
text: "The Greatest Photobox"
size_hint: (1, 0.1)
font_size: '40sp'
AsyncImage:
id: photo
Button:
id: action_button
text: "Push the button"
size_hint: (1, 0.2)
color: [1,0,0,1]
font_size: '40sp'
on_press: root.on_press_camera_button()
Output