Kivy simple countdown minute and second timer - python

So I want to build a kivy program that basically just countdown a certain minutes and seconds.
this is how far I have come:
from kivy.app import App
from kivy.uix.label import Label
from kivy.clock import Clock
from datetime import datetime, date, time
class IncrediblyCrudeClock(Label):
a = time(0, 1, 1)
def update(self, *args):
self.text = str(self.a)
print(str(self.a))
self.a = datetime.combine(date.today(), self.a) - datetime.combine(date.today(), time(0,0,1))
class TimeApp(App):
def build(self):
crudeclock = IncrediblyCrudeClock()
Clock.schedule_interval(crudeclock.update, 1)
return crudeclock
if __name__ == "__main__":
TimeApp().run()
the problem is that when I try to deduct the old time from the new time, sothat I have 1 second less displayed, I get the following error:
self.a = datetime.combine(date.today(), self.a) datetime.combine(date.today(), time(0,0,1))
TypeError: combine() argument 2 must be datetime.time, not datetime.timedelta
this makes me think that, after the first sustraction, a is now not a "time" object anymore, but a "timedelta" wich unfortunatly can not be deducted.
any help would be great!

There is a simpler approach if you just need a countdown. You can use kivy's Animation class which is described very nicely by #inclement on youtube.
So here's the code (main.py and time.kv):
main.py
from kivy.app import App
from kivy.uix.label import Label
from kivy.animation import Animation
from kivy.properties import StringProperty, NumericProperty
class IncrediblyCrudeClock(Label):
a = NumericProperty(5) # seconds
def start(self):
Animation.cancel_all(self) # stop any current animations
self.anim = Animation(a=0, duration=self.a)
def finish_callback(animation, incr_crude_clock):
incr_crude_clock.text = "FINISHED"
self.anim.bind(on_complete=finish_callback)
self.anim.start(self)
class TimeApp(App):
def build(self):
crudeclock = IncrediblyCrudeClock()
crudeclock.start()
return crudeclock
if __name__ == "__main__":
TimeApp().run()
time.kv
<IncrediblyCrudeClock>
text: str(round(self.a, 1))
Enjoy! :)
Update:
The OP requested a solution without a kv-file -- so here is one:
from kivy.app import App
from kivy.uix.label import Label
from kivy.animation import Animation
from kivy.properties import StringProperty, NumericProperty
class IncrediblyCrudeClock(Label):
a = NumericProperty(5) # seconds
def start(self):
Animation.cancel_all(self) # stop any current animations
self.anim = Animation(a=0, duration=self.a)
def finish_callback(animation, incr_crude_clock):
incr_crude_clock.text = "FINISHED"
self.anim.bind(on_complete=finish_callback)
self.anim.start(self)
def on_a(self, instance, value):
self.text = str(round(value, 1))
class TimeApp(App):
def build(self):
crudeclock = IncrediblyCrudeClock()
crudeclock.start()
return crudeclock
if __name__ == "__main__":
TimeApp().run()

Related

Python Kivy: Update Label

i am new in the Kivy Topic and i have got a simple question (i think).
With the function "zufall" i create a random number.
This number should update every 2 seconds in the label.
But when i am running the code, the error "Label.text accept only str" occurs.
But from my opinion i made the "random_number" to a string. Or is there another problem, with my thinking?
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
import random
from kivy.clock import Clock
from kivy.properties import StringProperty
class ConnectPage(GridLayout):
# runs on initialization
def zufall(self, *args):
random_number = random.randrange(10)
random_number = str(random_number)
print(random_number)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2 # used for our grid
self.add_widget(Label(text='OEE'))
self.add_widget(Label(text=self.zufall))
class EpicApp(App):
def build(self):
t = ConnectPage()
Clock.schedule_interval(t.zufall, 2)
return t
if __name__ == "__main__":
EpicApp().run()
Can someone of you give me a hint?
Firstly you have to return your random number from your zufall function, and call that function from your __init__ like this:
# runs on initialization
def zufall(self, *args):
random_number = random.randrange(10)
random_number = str(random_number)
return random_number
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2 # used for our grid
self.add_widget(Label(text='OEE'))
self.add_widget(Label(text=self.zufall()))

Updating multiple Labels with kivy

Hello i'm relatively new to python and kivy and I've been looking around but i cant quite find the answer, i'm trying to make a hub where I can display live data from my rasberry-pi. I made a clock widget using a Label, and it updates time, but when i try to add my CPU usage only one label shows up. I thought the widgets might be covering each other but I used size_hint and it wont help. When i run the code below, only the timex will show up and only if i delete the Clock.schedule_interval(display32.timex, (1)) will the *updatex * display.
Thanks alot for the help
from kivy.app import App
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
import psutil
from kivy.lang import Builder
import time
class Display(Label, FloatLayout):
def __init__(self):
super(Display, self).__init__()
def updatex(self, *args):
self.cpu2 = psutil.cpu_percent()
self.text=str(self.cpu2)
self.size_hint=(.5, .25)
self.pos=(500, 500)
self.texture_size=(0.1,0.1)
def timex(self, *args):
self.time2 = time.asctime()
self.text = str(self.time2)
self.size_hint = (.5, .25)
self.pos = (30, 500)
self.size_hint=(0.1,0.1)
class TimeApp(App):
def build(self):
display32 = Display()
Clock.schedule_interval(display32.updatex, (1))
Clock.schedule_interval(display32.timex, (1))
return display32
if __name__ == "__main__":
TimeApp().run()
Instead of passing both labels to the clock, you can define a widget, add both labels and start the clock on the widget.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.clock import Clock
import psutil
import time
class Display(Widget):
def draw(self, *args):
self.clear_widgets()
self.add_widget(Label(text=str(psutil.cpu_percent()), pos=(500, 500)))
self.add_widget(Label(text=str(time.asctime()), pos=(30, 500)))
class TimeApp(App):
def build(self):
display32 = Display()
Clock.schedule_interval(display32.draw, 1)
return display32
if __name__ == '__main__':
TimeApp().run()

Python How to influence running program

Button Start alarm starts ringing of alarm. I want to stop ringing by button Stop alarm. I don't know to influece running program. How must I repair function stop_alarm?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.widget import Widget
import winsound
class ControlPanel(BoxLayout):
def __init__(self, **kwargs):
# make sure we aren't overriding any important functionality
super(ControlPanel, self).__init__(**kwargs)
self.alarm_status = True
self.orientation = "vertical"
butOn = Button(text = "Start alarm: ", on_release = self. start_alarm)
butStop = Button(text = "Stop alarm: ", on_release = self.stop_alarm)
self.add_widget(butOn)
self.add_widget(butStop)
def start_alarm(self, obj):
while self.alarm_status == True:
winsound.PlaySound("alarm.wav", winsound.SND_FILENAME)
def stop_alarm(self, obj):
self.alarm_status = False
class LifeApp(App):
def build(self):
return ControlPanel()
if __name__ == '__main__':
LifeApp().run()
The main problem with your code is that you have a while loop on your main GUI thread. So what you need to do is to run the start_alarm on a different thread than your main GUI thread for the GUI thread to be responsive.
As for not being able to play alarm next time, you didn't set the alarm_status flag to True again.Once you have that you can start and stop the sound as many times you want.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.widget import Widget
import time
import winsound
import threading
class ControlPanel(BoxLayout):
def __init__(self, **kwargs):
# make sure we aren't overriding any important functionality
super(ControlPanel, self).__init__(**kwargs)
self.alarm_status = True
self.orientation = "vertical"
butOn = Button(text = "Start alarm: ", on_release = self.start_thread)
butStop = Button(text = "Stop alarm: ", on_release = self.stop_alarm)
self.add_widget(butOn)
self.add_widget(butStop)
def start_alarm(self, obj):
while self.alarm_status == True:
winsound.PlaySound("alarm.wav", winsound.SND_FILENAME)
#Set Alarm_Status True so that next time it works
self.alarm_status = True
def stop_alarm(self, obj):
self.alarm_status = False
#Function to run your start_alarm on a different thread
def start_thread(self,obj):
t1 = threading.Thread(target=self.start_alarm,args=(obj,))
t1.start()
class LifeApp(App):
def build(self):
return ControlPanel()
if __name__ == '__main__':
LifeApp().run()
Hope this helps!

Kivy - Parsing structure to widget

I'm having issues with parsing a data structure to a widget in Kivy, which would then access the structure and be able to show a value on the screen be updated continuously via a clock interval (not sure of a better to do this yet).
I have highlighted the issues in the (non-working) code below:
main.py
from kivy.app import App
from test import TestWidget
class TestApp(App):
def build(self):
testStructTable = {'randomVal1': 1, 'testVal': 2, 'randomVal2': 3}
# Issue here parsing the table like this?
return TestWidget(testStructTable)
if __name__ == '__main__':
TestApp().run()
test.py
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.relativelayout import RelativeLayout
from kivy.properties import NumericProperty
class TestWidget(RelativeLayout):
def __init__(self, testStructTable, **kwargs):
super(TestWidget, self).__init__(**kwargs)
Builder.load_file('test.kv')
sm = ScreenManager()
sm.add_widget(MainScreen(name='MainScreen'))
self.add_widget(sm)
# Error accessing the table
print self.testStructTable
# Have the update_test_val continuously called
#Clock.schedule_interval(MainScreen.update_test_val(testStructTable), 1 / 60)
class MainScreen(Screen):
def __init__(self, **kwargs):
testVal = NumericProperty(0)
def update_test_val(self, testStructTable):
# Get testVal from testStructTable
# Something like:
# self.testVal = testStructTable.testVal + 1 ?
self.testVal = self.testVal + 1
test.kv
<MainScreen>:
FloatLayout:
Label:
text: str(root.testVal)
font_size: 80
My aim is to have the testVal constantly updating on the screen by accessing that data structure, however I am currently unable to achieve this, can you please advise?
In your __init__ method you're passing testStructTable and then you're trying to access self.testStructTable which does not exist untill you explicitly make an assignment:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.relativelayout import RelativeLayout
from kivy.properties import NumericProperty
class TestWidget(RelativeLayout):
def __init__(self, testStructTable, **kwargs):
super(TestWidget, self).__init__(**kwargs)
print(testStructTable)
self.testStructTable = testStructTable
print(self.testStructTable)
class TestApp(App):
def build(self):
testStructTable = {'randomVal1': 1, 'testVal': 2, 'randomVal2': 3}
# Issue here parsing the table like this?
return TestWidget(testStructTable)
if __name__ == '__main__':
TestApp().run()

Nested / alternating apps with Kivy

I'd like to have a kivy app function as a launcher for other kivy apps, depending on input. The way I implemented it below is obviously not working (because the kv file gets reloaded and its styles reapplied, thus adding more and more buttons), and there also seems to be some recursion as the trace suggests when I hit Esc to exit.
I do get the warning that app1.kv is loaded multiple times, however, in the documentation for App.load_kv() it says
This method is invoked the first time the app is being run if no
widget tree has been constructed before for this app.
This implies to me that it should be possible to run() an app multiple times?
Here is my code:
main.py
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.logger import Logger
from kivy.lang import Builder
class OutsideApp(App):
current_app = ObjectProperty(None)
def build(self):
Clock.schedule_interval(self.update, 3)
return Widget()
def update(self, dt):
if isinstance(self.current_app, App):
self.current_app.stop()
if isinstance(self.current_app, App1):
self.current_app = App2()
else:
self.current_app = App1()
self.current_app.run()
class App1(App):
pass
class App2(App):
def build(self):
gl = Builder.load_string("<SequencesGame#GridLayout>:\n cols: 2\n Button:\n text: \"hello 2\"\nSequencesGame:")
return gl
if __name__ == '__main__':
oa = OutsideApp()
oa.run()
app1.kv
#:kivy 1.0.9
<SequencesGame#GridLayout>:
cols: 2
Button:
text: "hello 111"
SequencesGame:
This seems to be an issue even if apps are not nested:
main2.py
from kivy.app import App
from kivy.clock import Clock
from kivy.logger import Logger
from kivy.lang import Builder
class App1(App):
pass
class App2(App):
def build(self):
return Builder.load_string("<SequencesGame#GridLayout>:\n cols: 2\n Button:\n text: \"hello 2\"\nSequencesGame:")
current_app = None
def switch(*args):
global current_app
if isinstance(current_app, App):
current_app.stop()
if isinstance(current_app, App1):
current_app = App2()
else:
current_app = App1()
current_app.run()
if __name__ == '__main__':
Clock.schedule_interval(switch, 2)
switch()
In the end I followed #inclement's suggestion of using multiprocessing. Here is a basic implementation (without communication through Pipes or Queues):
import sys
import multiprocessing
from kivy.app import App
from kivy.lang import Builder
from time import sleep#, localtime, time
def str_to_class(str):
return reduce(getattr, str.split("."), sys.modules[__name__])
class App1(App):
def build(self):
return Builder.load_string("BoxLayout:\n Button:\n text: \"hello 1\"")
class App2(App):
def build(self):
return Builder.load_string("BoxLayout:\n Button:\n text: \"hello 2\"")
class Wrapper(object):
def __init__(self, *kargs, **kwargs):
super(Wrapper, self).__init__()
self.running_app = None
self.next_app = 'App1'
def change_running_app(self, name='App1'):
if self.running_app is not None:
self.running_app.terminate()
self.running_app = multiprocessing.Process(target=self.run_app, kwargs={'name':name})
self.running_app.start()
def run_app(self, name='App1'):
app = str_to_class(name)()
app.run()
def swap(self):
self.change_running_app(name=self.next_app)
self.next_app = ['App1', 'App2']['App1' is self.next_app]
if __name__ == '__main__':
counter = 0
w = Wrapper()
while True:
counter += 1
print "Started App instance {}".format(counter)
w.swap()
sleep(5)

Categories