when I press on a button i would like run a function everey minute.
I am using sched.schedulerbut my kivy app crash.
s.enter(60, 1, self.graph_data, (s,))
s.run()
Someone can help me ?
You can use the Clock object provided by kivy:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
class TestApp(App):
def build(self):
self.timer = None
btn1 = Button(text='Click to start')
btn1.bind(state=self.callbackStart)
btn2 = Button(text='Click to stop')
btn2.bind(state=self.callbackStop)
bl = BoxLayout()
bl.add_widget(btn1)
bl.add_widget(btn2)
return bl
def callbackStart(self, instance, value):
print('My button1 <%s> state is <%s>' % (instance, value))
if self.timer is not None:
Clock.unschedule(self.timer)
self.timer = Clock.schedule_interval(self.timedAction, 0.5)
def callbackStop(self, instance, value):
print('My button2 <%s> state is <%s>' % (instance, value))
Clock.unschedule(self.timer)
def timedAction(self, dt):
print("Repeat")
if __name__ == '__main__':
TestApp().run()
You should also be able to do something like this by using the threading build-in module with the Thread and Event objects.
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
import threading
class MyTimedAction(threading.Thread):
def __init__(self, event):
threading.Thread.__init__(self)
self.stopped = event
def run(self):
while not self.stopped.wait(0.5):
print("Periodic action performed")
class TestApp(App):
def build(self):
# Create the stopTimer event. Initialize as stopped.
self.stopTimer = threading.Event()
self.stopTimer.set()
btn1 = Button(text='Click to start')
btn1.bind(state=self.callbackStart)
btn2 = Button(text='Click to stop')
btn2.bind(state=self.callbackStop)
bl = BoxLayout()
bl.add_widget(btn1)
bl.add_widget(btn2)
return bl
def callbackStart(self, instance, value):
print('My button1 <%s> state is <%s>' % (instance, value))
if self.stopTimer.isSet(): # (Eventually) avoid duplicates if timer already started
self.stopTimer.clear()
thread = MyTimedAction(self.stopTimer)
thread.start()
def callbackStop(self, instance, value):
print('My button2 <%s> state is <%s>' % (instance, value))
self.stopTimer.set()
if __name__ == '__main__':
TestApp().run()
The examples above assumes that if the timer is already started it doesn't start again another timed job on a new thread.
The brief GUI code is just for demo purpose.
Related
I am trying to do an asynchronous label update in Kivi gui.
When I run the function via loop.run_until_complete(self.update()) I get only output to the console, the application window is not updated. What am I missing?
# async kivy app example
import kivy
kivy.require('2.1.0') # replace with your current kivy version !
from kivy.app import App
from kivy.app import async_runTouchApp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
import asyncio, time
class RootWidget(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
btn = Button(text='Run Async func')
btn.fbind('on_press', self.async_callback)
self.lbl = Label(text = 'Click on the button!')
self.add_widget(btn)
self.add_widget(self.lbl)
async def update(self):
async for i in async_func():
print(i)
self.lbl.text = str(i)
def async_callback(self, *args):
loop = asyncio.get_event_loop()
loop.run_until_complete(self.update())
async def async_func():
for i in range(0,100):
yield i
await asyncio.sleep(1)
class AsyncApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(async_runTouchApp(AsyncApp().run(), async_lib='asyncio'))
loop.close()
There is no such error shown in the Python output shell.
What I want is that , the first page should be the Login page or as here, the "ConnectingPage" then Welcome Should Be shown, and then at last a Set of buttons named from 0 to 99 be shown.
Here is the code :
import kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen, FallOutTransition
from kivy.uix.scrollview import ScrollView
from kivy.properties import StringProperty
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.core.window import Window
class ConnectingPage(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.add_widget(Label(text = "Usename:"))
self.username = TextInput(multiline=False)
self.add_widget(self.username)
self.add_widget(Label(text = "Password:"))
self.password = TextInput(multiline=False,password = True)
self.add_widget(self.password)
self.joinbutton = Button(text="Join")
self.joinbutton.bind(on_release = self.click_join_button)
self.add_widget(Label())
self.add_widget(self.joinbutton)
def click_join_button(self, instance):
username = self.username.text
password = self.password.text
#if username == "venu gopal" and password == "venjar":
MyApp.screen_manager.current = "Info"
MyApp.screen_manager.current = "Chat"
class InfoPage(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 1
self.message = Label(text = "welcome",halign="center", valign="middle", font_size=30)
self.add_widget(self.message)
class SomeApp(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
layout = GridLayout(cols=1, spacing=10, size_hint_y=None)
# Make sure the height is such that there is something to scroll.
layout.bind(minimum_height=layout.setter('height'))
for i in range(100):
btn = Button(text=str(i), size_hint_y=None, height=40)
layout.add_widget(btn)
root = ScrollView(size_hint=(1, None), size=(Window.width, Window.height))
root.add_widget(layout)
if __name__ == "__main__":
runTouchApp(root)
class MyApp(App):
screen_manager = ScreenManager(transition=FallOutTransition(duration=2)) # this make screen_manager a class vaiable
def build(self):
# self.screen_manager = ScreenManager()
self.connecting_page = ConnectingPage()
screen = Screen(name='Connect')
screen.add_widget(self.connecting_page)
self.screen_manager.add_widget(screen) # add screen to ScreenManager
# Info page
self.info_page = InfoPage()
screen = Screen(name='Info')
screen.add_widget(self.info_page)
self.screen_manager.add_widget(screen) # add screen to ScreenManager
# Chat App
self.chat_app = SomeApp()
screen = Screen(name='Chat')
screen.add_widget(self.chat_app)
self.screen_manager.add_widget(screen) # add screen to ScreenManager
# return ConnectingPage()
return self.screen_manager
if __name__ == "__main__":
MyApp().run()
The Problem is that: the set of Buttons are getting shown in the start. When the Cross is pressed to close the kivy window, then the Login page is shown and then "welcome" and then the buttons again.
I want It to show from the second Step.
What I believe is that the "line 61" in the code is making a problem. When the code is run it first shows the buttons and so on.
Please Help me find the solution for the above Problem.
Your __init__() method of the SomeApp class starts an App running with the lines:
if __name__ == "__main__":
runTouchApp(root)
and that runTouchApp() does not return until that App completes. So when you run your code, it stops a the line:
self.chat_app = SomeApp()
To fix that, just replace:
if __name__ == "__main__":
runTouchApp(root)
with:
self.add_widget(root)
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!
So i'm learning Kivy for a school project and i got an error when testing out Buttons. Here is my Code:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
from kivy.uix.button import Button
class übung(GridLayout):
def lol(instance):
label.disabled = False
def __init__(self):
super(übung, self).__init__
self.cols = 2
self.label = Label ("Ehrm ... lol")
label.disabled = True
self.btn1 = Button(text="Hello world 1")
self.btn1.bind(on_press=lol)
self.btn2 = Button(text="Hello world 2")
self.btn2.bind(on_press=lol)
class App(App):
def build(self):
return übung()
if __name__ == "__main__":
App().run()
The Error I'm getting is in the title(init takes 1 postitional argument but 2 were given). It is supposed to be two buttons and if you press one it say ehrm ... lol. As i said, it is just for testing purposes.
Thanks in Advance,
me
You have several errors. The error you display is because you have to pass the argument (text) to the Label constructor by name:
self.label = Label (text="Ehrm ... lol")
Your code should look something like this:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class übung(GridLayout):
def __init__(self, **kwargs):
super(übung, self).__init__(**kwargs)
self.cols = 2
self.label = Label(text = "Ehrm ... lol")
self.label.disabled = True
self.btn1 = Button(text="Hello world 1")
self.btn1.bind(on_press=self.lol)
self.btn2 = Button(text="Hello world 2")
self.btn2.bind(on_press=self.lol)
self.add_widget(self.label)
self.add_widget(self.btn1)
self.add_widget(self.btn2)
def lol(self, event):
self.label.disabled = False
class App(App):
def build(self):
return übung()
if __name__ == "__main__":
App().run()
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()