Running functions in kivy extremely frequently (Kivy Clock/FreeClock) - python

I am working on an app that takes in data from a Bluetooth device (that I handle simply through the serial module). That I know how to do. But giving it a new life in Kivy - displaying it - creates a problem of everything being synced with FPS. I want to run the function all the time in the background, hundreds of times a second. It's fair to say that out of 10000 incoming packets 10 might be useful. So if I would go by clock scheduling it would have to be (well) under 20 ms per cycle.
Simply put: how do I run one of the functions separately from the FPS? Is there a clean way of using the Free version of the clock for only one function and how is that achieved?
I want a simple solution, I can reinvent the wheel, but I don't want to.
Any help is appreciated, thank you.

You could use threading for this.
Here is a little example of using threading with kivy:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from kivy.properties import NumericProperty
import threading
import time
Builder.load_string('''
<MyLayout>:
Label:
text: str(root.data)
''')
class MyLayout(BoxLayout):
data = NumericProperty(0)
count = 0
running = True
def __init__(self,**kwargs):
super(MyLayout,self).__init__(**kwargs)
Clock.schedule_once(self.after_init)
def after_init(self, dt):
threading.Thread(target=self.func).start()
def func(self):
while self.running:
self.data += 1
time.sleep(0.1)
class MyApp(App):
def build(self):
self.root = MyLayout()
return self.root
def on_stop(self):
self.root.running = False
MyApp().run()

Related

How do I fix 'ValueError: callback must be a callable, got 18:19:46'

So I'm trying to get a good grasp on kivy and I want to create a basic clock app and I keep running into a problem when I go to update the clock label. I've looked at other threads on here and still cant find the right solution. I believe I am doing something wrong at Clock.schedule_interval but again, I'm trying to get a grasp on kivy and I'm not sure. Any help would be greatly appriciated
Python code
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from kivy.clock import Clock
import time
Builder.load_file('myApp.kv')
class ClockScreen(Widget):
def updateClock(self):
hour = time.strftime("%H")
minute = time.strftime("%M")
second = time.strftime("%S")
Clocktime = self.root.ids.clock.text
print(Clocktime)
newTime = self.root.ids.clock.text = f'{hour}:{minute}:{second}'
return newTime
class FirstApp(App):
def build(self):
return ClockScreen()
def on_start(self):
Clock.schedule_interval(ClockScreen.updateClock(self), 1)
FirstApp().run()
```
KV File
:
FloatLayout:
size: root.width, root.height
Label:
id: clock
text: "12:00"
Try
def on_start(self):
Clock.schedule_interval(ClockScreen.updateClock, 1)
Reason:
ClockScreen.updateClock is a callable (a function handle). This is also explained in the docs.
ClockScreen.updateClock(self) is actually calling the function that returns the time, and you cannot schedule a string (18:19:46, as per your error).
However, this doesn't say which instance of the ClockScreen gets updated (you are referencing the function via a class rather than a specific instance), so this may be more accurate for what you want
class FirstApp(App):
def build(self):
return ClockScreen()
def on_start(self):
cs = self.build()
Clock.schedule_interval(cs.updateClock, 1)

Cannot disable buttons in Kivy (Python)

I have problem with disabling of buttons in kivy library. When I disable button, it simply not disable. It waits in some strange way.
Let me show you my code:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
import time
class MainApp(App):
def build(self):
self.l = FloatLayout()
b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
b.bind(on_press=self.press)
self.l.add_widget(b)
return self.l
def press(self, btn):
btn.disabled = True
time.sleep(3.0)
btn.disabled = False
app = MainApp()
app.run()
When I press button, I want to disable it for 3 sec. But instead of it program "freeze" (without disabling of button), and then after 3 secs do animation of press (button blinks with blue color). Of cource program must "freeze" because of time.sleep(3.0), but after disabling of button (Which must be gray, but it dont change color...)
How to solve it? If I put there instead time.sleep() something like for cycle (with about 10 milions of cycle) to imitate of "doing something" by program, it behaves in the same way...
So how I can solve it? How to disable button in kivy, then do something and after it is done enable button again?
Thanks!
EDIT: My problem isn't, that program freezes for 3 seconds. I understand that calling time.sleep() is blocking. What I don't understand is why button is not disabled before (and during) sleep...
The time.sleep is blocking the code. Instead you need to use Clock to enable the button after 3 seconds. Below is the corrected code to achieve your target:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import Clock
from functools import partial
class MainApp(App):
def build(self):
self.l = FloatLayout()
b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
b.bind(on_press=self.press)
self.l.add_widget(b)
return self.l
def press(self, btn):
btn.disabled = True
Clock.schedule_once(partial(self.btn_enable, btn), 3)
def btn_enable(self, btn, *args):
btn.disabled = False
app = MainApp()
app.run()
TL; DR
The animation happens after the press function is called. This means that you freeze the program when doing time.sleep.
What to do about it?
Instead, you need to do something non-blocking, meaning that it runs in three seconds, but it doesn't cause the program to freeze. Something that would probably work is to utilize threads (something similar to the example, but dealing with sending variables across threads).
Example
Here is an example for your code that does not work, so you can understand the gist of it. Most likely, you are going have to deal with passing variables across threads:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
# import time
import threading
class MainApp(App):
def build(self):
self.l = FloatLayout()
b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
b.bind(on_press=self.press)
self.l.add_widget(b)
return self.l
def press(self, btn):
btn.disabled = True
# time.sleep(3.0)
threading.Timer(3.0, lambda: btn.disabled = False).start()
app = MainApp()
app.run()
This was inspired by this answer.

".cancel()" function of Kivy.Clock doesn't work?

I have currently a problem regarding the "Clock" library of the framework
"Kivy". I am creating a Clock-Thread and want to terminate/cancel it but it does not seem to work.
I already read the documentation of Kivy.Clock function and did the same like them.
The link to the Kivy.Clock function
My Code:
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
class MainWindow(BoxLayout):
def __init__(self):
super(MainWindow, self).__init__()
Window.size = (1280, 720)
ev1 = Clock.schedule_interval(self.print_func, 1)
ev1.cancel()
def print_func(self, dt = None):
print ("Test")
Imagine that a .kv file exist and that the MainWindow class is getting called. Everything works fine, except of the canceling of the Clock interval.
Expected result should be that the Clock-Thread should be terminated or rather set to inactive.
Actual result is that it has no effect and the Clock-Thread is still running.
EDIT: I got this function of Clock to work..
I found out that the .cancel() and .unschedule() functions for Clock.schedule_interval() don‘t work. I created then a trigger: Clock.create_trigger() and tried it with .cancel() and .unschedule(). These both functions worked perfectly.

Unloading audio samples in kivy

Audio unload does not seem to actually release memory, at least as far as top on linux is concerned. Repeatedly loading and unloading causes memory usage to creep upward. Did I miss something obvious?
from __future__ import print_function
import kivy
kivy.require('1.9.0') # replace with your current kivy version !
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.core.audio import SoundLoader
class SoundTest(BoxLayout):
def __init__(self, **kwargs):
super(SoundTest, self).__init__(**kwargs)
button = Button(text='Play sound')
self.add_widget(button)
button.bind(on_release=self.PlaySound)
def PlaySound(self, widget):
snd = SoundLoader.load('test.ogg')
snd.bind(on_stop=self.UnloadSoundWhenDone)
snd.play()
print ("play", snd)
def UnloadSoundWhenDone(self, snd):
print ("unload", snd)
snd.unload()
class MyApp(App):
def build(self):
return SoundTest()
if __name__ == '__main__':
MyApp().run()
Update: this seems to be using the kivy.core.audio.audio_gstplayer.SoundGstplayer backend.
Actually, the memory does not appear to creep upward indefinitely, so while SoundGstplayer seems to allocate itself much more than the SDL2 backend, this probably as intended.
del an_obj
Try to delete the actual object, maybe? I am not exactly sure what you are doing.

Update widgets after specified time

I cannot understand how to update kivy screen.
This is my python file:
import kivy
kivy.require('1.1.1')
from kivy.app import App
from kivy.uix.widget import Widget
import time
class PongGame(Widget):
labe = ObjectProperty(None)
def settext(self,x):
self.labe.text = str(x)
print "DONE"
class PongApp(App):
def build(self):
game = PongGame()
game.settext(1)
time.sleep(3)
game.settext(5)
time.sleep(3)
game.settext(87)
time.sleep(3)
game.settext(5)
return game
if __name__ == '__main__':
PongApp().run()
this is my kv file
#:kivy 1.0.9
<PongGame>:
labe:lab
Label:
id: lab
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: str("Hello")
When I run it all it freezes. Then all I see is a 5.
How can i get the others to show up?
def build(self):
game = PongGame()
game.settext(1)
time.sleep(3)
game.settext(5)
time.sleep(3)
game.settext(87)
time.sleep(3)
game.settext(5)
return game
Your problem is that sleep blocks the entire thread - your whole program just stops doing anything for the duration of the call. That includes any graphical interface that has been drawn, it can't update or even receive input because it's running in the same thread and doesn't get run again until your blocking function call has stopped.
Actually, you also have the problem that all these changes take place before the gui is even drawn (the game is displayed in the kivy window only after it's returned from build).
You have to instead think in terms of kivy's main loop and clock - in the background kivy is trying to run certain functions like touch detection and graphical updates as frequently as possible. If you run some code in this loop that takes a long time, none of the rest of kivy can work until your code terminates. This is normal in gui programming, you always have to be careful not to block the main program loop.
The easiest way in kivy to schedule something regularly is to create a function and hook into this event loop, telling the clock to run your function after some interval or after every repeat of some time period. In your case, you want to change the text every 3 seconds.
Here is a short example of how to achieve exactly what you want:
from kivy.app import App
from kivy.uix.widget import Widget
import time
from kivy.properties import ObjectProperty
from functools import partial
from kivy.lang import Builder
from kivy.clock import Clock
Builder.load_string('''
<PongGame>:
labe:lab
Label:
id: lab
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: str("Hello")
''')
class PongGame(Widget):
labe = ObjectProperty(None)
def settext(self, x, *args):
self.labe.text = str(x)
print "DONE"
class PongApp(App):
def build(self):
game = PongGame()
game.settext(1)
Clock.schedule_once(partial(game.settext, 5), 3)
Clock.schedule_once(partial(game.settext, 87), 6)
Clock.schedule_once(partial(game.settext, 5), 9)
return game
if __name__ == '__main__':
PongApp().run()
There are a few important notes. One is that I used functools.partial() because you need to pass a function to Clock.schedule_once, and partial creates a function from an existing function (here game.settext) and some default arguments to use (here the numbers for the label). It was also important to add *args to PongGame.settext because the clock automatically passes some extra arguments that we don't care about.
If the meaning of that is not clear to you, experiment with these parameters to see what happens.

Categories