I'm new to Kivy, and I'm trying to get a better handle on Events, as well as a few basics of the framework.
For that purpose, can anyone provide the code for a simple clock implemented in Kivy, which shows the current time, and updates every second?
Fundamentally, the Kivy clock attempts to execute any scheduled callback rhythmically as determined by the specified fps (frame per second, see maxfps in config ). That is, ideally, given e.g. a desired fps of 30, the clock will execute the callbacks at intervals of 1 / 30 seconds, or every 33.33 ms.
Build and distribute beautiful Python cross-platform GUI apps with ease. Kivy runs on Android, iOS, Linux, macOS and Windows.
What about a simple graphical one:
EDIT (12.02.2022): Refactored the whole code to make it more readable
import collections
import datetime
import math
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.graphics import Color, Line
Builder.load_string('''
<MyClockWidget>:
on_pos: self.update_clock()
on_size: self.update_clock()
FloatLayout
id: face
size_hint: None, None
pos_hint: {"center_x":0.5, "center_y":0.5}
size: 0.9*min(root.size), 0.9*min(root.size)
canvas:
Color:
rgb: 0.1, 0.1, 0.1
Ellipse:
size: self.size
pos: self.pos
FloatLayout
id: hands
size_hint: None, None
pos_hint: {"center_x":0.5, "center_y":0.5}
size: 0.9*min(root.size), 0.9*min(root.size)
''')
Position = collections.namedtuple('Position', 'x y')
class MyClockWidget(FloatLayout):
def on_parent(self, myclock, parent):
"""
Add number labels when added in widget hierarchy
"""
for i in range(1, 13):
number = Label(
text=str(i),
pos_hint={
# pos_hint is a fraction in range (0, 1)
"center_x": 0.5 + 0.45*math.sin(2 * math.pi * i/12),
"center_y": 0.5 + 0.45*math.cos(2 * math.pi * i/12),
}
)
self.ids["face"].add_widget(number)
def position_on_clock(self, fraction, length):
"""
Calculate position in the clock using trygonometric functions
"""
center_x = self.size[0]/2
center_y = self.size[1]/2
return Position(
center_x + length * math.sin(2 * math.pi * fraction),
center_y + length * math.cos(2 * math.pi * fraction),
)
def update_clock(self, *args):
"""
Redraw clock hands
"""
time = datetime.datetime.now()
hands = self.ids["hands"]
seconds_hand = self.position_on_clock(time.second/60, length=0.45*hands.size[0])
minutes_hand = self.position_on_clock(time.minute/60+time.second/3600, length=0.40*hands.size[0])
hours_hand = self.position_on_clock(time.hour/12 + time.minute/720, length=0.35*hands.size[0])
hands.canvas.clear()
with hands.canvas:
Color(0.2, 0.5, 0.2)
Line(points=[hands.center_x, hands.center_y, seconds_hand.x, seconds_hand.y], width=1, cap="round")
Color(0.3, 0.6, 0.3)
Line(points=[hands.center_x, hands.center_y, minutes_hand.x, minutes_hand.y], width=2, cap="round")
Color(0.4, 0.7, 0.4)
Line(points=[hands.center_x, hands.center_y, hours_hand.x, hours_hand.y], width=3, cap="round")
class MyApp(App):
def build(self):
clock_widget = MyClockWidget()
# update initially, just after construction of the widget is complete
Clock.schedule_once(clock_widget.update_clock, 0)
# then update every second
Clock.schedule_interval(clock_widget.update_clock, 1)
return clock_widget
if __name__ == '__main__':
MyApp().run()
Old answer:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Line
from kivy.uix.floatlayout import FloatLayout
from math import cos, sin, pi
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import NumericProperty
import datetime
kv = '''
#:import math math
[ClockNumber@Label]:
text: str(ctx.i)
pos_hint: {"center_x": 0.5+0.42*math.sin(math.pi/6*(ctx.i-12)), "center_y": 0.5+0.42*math.cos(math.pi/6*(ctx.i-12))}
font_size: self.height/16
<MyClockWidget>:
face: face
ticks: ticks
FloatLayout:
id: face
size_hint: None, None
pos_hint: {"center_x":0.5, "center_y":0.5}
size: 0.9*min(root.size), 0.9*min(root.size)
canvas:
Color:
rgb: 0.1, 0.1, 0.1
Ellipse:
size: self.size
pos: self.pos
ClockNumber:
i: 1
ClockNumber:
i: 2
ClockNumber:
i: 3
ClockNumber:
i: 4
ClockNumber:
i: 5
ClockNumber:
i: 6
ClockNumber:
i: 7
ClockNumber:
i: 8
ClockNumber:
i: 9
ClockNumber:
i: 10
ClockNumber:
i: 11
ClockNumber:
i: 12
Ticks:
id: ticks
r: min(root.size)*0.9/2
'''
Builder.load_string(kv)
class MyClockWidget(FloatLayout):
pass
class Ticks(Widget):
def __init__(self, **kwargs):
super(Ticks, self).__init__(**kwargs)
self.bind(pos=self.update_clock)
self.bind(size=self.update_clock)
def update_clock(self, *args):
self.canvas.clear()
with self.canvas:
time = datetime.datetime.now()
Color(0.2, 0.5, 0.2)
Line(points=[self.center_x, self.center_y, self.center_x+0.8*self.r*sin(pi/30*time.second), self.center_y+0.8*self.r*cos(pi/30*time.second)], width=1, cap="round")
Color(0.3, 0.6, 0.3)
Line(points=[self.center_x, self.center_y, self.center_x+0.7*self.r*sin(pi/30*time.minute), self.center_y+0.7*self.r*cos(pi/30*time.minute)], width=2, cap="round")
Color(0.4, 0.7, 0.4)
th = time.hour*60 + time.minute
Line(points=[self.center_x, self.center_y, self.center_x+0.5*self.r*sin(pi/360*th), self.center_y+0.5*self.r*cos(pi/360*th)], width=3, cap="round")
class MyClockApp(App):
def build(self):
clock = MyClockWidget()
Clock.schedule_interval(clock.ticks.update_clock, 1)
return clock
if __name__ == '__main__':
MyClockApp().run()
Here's an extremely simple clock:
from kivy.app import App
from kivy.uix.label import Label
from kivy.clock import Clock
import time
class IncrediblyCrudeClock(Label):
def update(self, *args):
self.text = time.asctime()
class TimeApp(App):
def build(self):
crudeclock = IncrediblyCrudeClock()
Clock.schedule_interval(crudeclock.update, 1)
return crudeclock
if __name__ == "__main__":
TimeApp().run()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With