Python 3.7 Kivy 1.11.1 OSX 10.14.6
Im am writing a Kivy app with 25 fps that should have a fast event loop with 10ms. The problem is that the loop is called much faster - almost instantly. Is there a way to fix this? Here is my test code:
from kivy.config import Config
Config.read("config.ini")
Config.set('graphics', 'maxfps', '25')
Config.set('kivy', 'kivy_clock', 'free_all')
Config.write()
from kivy.uix.relativelayout import RelativeLayout
from kivy.app import App
from time import time
from kivy.clock import Clock
from collections import deque
from numpy import median
class Screen(RelativeLayout):
durations_ms = deque(maxlen=100)
last_event_loop_time = time()
counter = 1
@staticmethod
def event_loop(dt):
duration_ms = (time() - Screen.last_event_loop_time) * 1000
Screen.durations_ms.append(duration_ms)
Screen.last_event_loop_time = time()
if Screen.counter == 0:
print("Interval time (ms) median: {0:.3f}, min: {1:.3f}, max: {2:.3f}"
.format(median(Screen.durations_ms), min(Screen.durations_ms), max(Screen.durations_ms)))
Screen.counter = (Screen.counter + 1) % 100
def __init__(self, **kwargs):
super(Screen, self).__init__(**kwargs)
Clock.schedule_interval_free(self.event_loop, 0.010)
class TestApp(App):
def build(self):
return Screen()
if __name__ == "__main__":
TestApp().run()
The output is:
Interval time (ms) median: 0.035, min: 0.030, max: 1.431
Interval time (ms) median: 0.061, min: 0.030, max: 0.408
Interval time (ms) median: 0.037, min: 0.031, max: 0.551
Your problem piqued my interest. I suggest using a different approach to scheduling your event_loop
using the Python sched
module. Here is a modified version of your code that does that:
from kivy.config import Config
Config.read("config.ini")
Config.set('graphics', 'maxfps', '25')
Config.set('kivy', 'kivy_clock', 'free_all')
Config.write()
from kivy.uix.relativelayout import RelativeLayout
from kivy.app import App
import sched
from time import time, sleep
from collections import deque
from numpy import median
class Screen(RelativeLayout):
durations_ms = deque(maxlen=100)
last_event_loop_time = time()
counter = 1
s = sched.scheduler(time, sleep) # create the scheduler
def event_loop(self):
duration_ms = (time() - Screen.last_event_loop_time) * 1000
Screen.last_event_loop_time = time()
Screen.durations_ms.append(duration_ms)
if Screen.counter == 0:
print("Interval time (ms) median: {0:.3f}, min: {1:.3f}, max: {2:.3f}"
.format(median(Screen.durations_ms), min(Screen.durations_ms), max(Screen.durations_ms)))
Screen.counter = (Screen.counter + 1) % 100
# enter the next call to event_loop into the scheduler
Screen.s.enter(0.01, 1, self.event_loop)
def __init__(self, **kwargs):
super(Screen, self).__init__(**kwargs)
# enter the first call to event_loop and start the scheduler
Screen.s.enter(0.01, 1, self.event_loop)
Screen.s.run()
class TestApp(App):
def build(self):
return Screen()
if __name__ == "__main__":
TestApp().run()
Note that this runs your event_loop
on the main thread. If your event_loop
is doing GUI updates, this is good, otherwise it is very bad.
If your GUI is suffering as a result of this running on the main thread, you can run it on another thread using this version:
from kivy.config import Config
Config.read("config.ini")
Config.set('graphics', 'maxfps', '25')
Config.set('kivy', 'kivy_clock', 'free_all')
Config.write()
from kivy.uix.relativelayout import RelativeLayout
from kivy.app import App
import sched
from time import time, sleep
from collections import deque
from numpy import median
import threading
class Screen(RelativeLayout):
durations_ms = deque(maxlen=100)
last_event_loop_time = time()
counter = 1
def run_event_loop(self):
self.s = sched.scheduler(time, sleep)
self.s.enter(0.01, 1, self.event_loop)
self.s.run()
def event_loop(self):
duration_ms = (time() - Screen.last_event_loop_time) * 1000
Screen.last_event_loop_time = time()
Screen.durations_ms.append(duration_ms)
if Screen.counter == 0:
print("Interval time (ms) median: {0:.3f}, min: {1:.3f}, max: {2:.3f}"
.format(median(Screen.durations_ms), min(Screen.durations_ms), max(Screen.durations_ms)))
Screen.counter = (Screen.counter + 1) % 100
self.s.enter(0.01, 1, self.event_loop)
def __init__(self, **kwargs):
super(Screen, self).__init__(**kwargs)
threading.Thread(target=self.run_event_loop, daemon=True).start()
class TestApp(App):
def build(self):
return Screen()
if __name__ == "__main__":
TestApp().run()
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.