I have a list which I have fetched from a server and here is what I want to do
result = [1,7,0,0,2,4....]
def got_json(req, result):
for i in result:
if i is odd:
wait for 5 seconds and call my_function
now continue
else:
call my_function (now)
So basically I'm looking for more like a time.sleep()
but using time.sleep
just freezes the app, I just want to pause the execution of my for loop in got_json
method and not all other stuff which I suppose time.sleep
does.
I tried using Clock.schedule_once
using this code
class MyWidget(BoxLayout):
def onbuttonclick(self):
my_list = range(10)
for i in my_list:
if (i%2 == 0) :
Clock.schedule_once(self.my_callback, 5)
continue
print("Whatever")
def my_callback(self,dt):
print("called")
The output seems like it is indeed scheduling the function but its not stopping the execution of the for loop, which is what I want Output of above code
Whatever
Whatever
Whatever
Whatever
Whatever
called
called
called
called
called
Output I want to have
Whatever
**5 seconds**
called
Whatever
**5 seconds**
called
and so on...
How can I use the Clock
object to do what I want? Thanks
This is interesting question. Using thread - is most universal solution for tasks like this in general. However, if we talking about this concrete case, you can use generator and it's yield points to return yourself control flow and resume execution later using Clock.schedule_once
:
from functools import wraps
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
def yield_to_sleep(func):
@wraps(func)
def wrapper(*args, **kwargs):
gen = func()
def next_step(*_):
try:
t = next(gen) # this executes 'func' before next yield and returns control to you
except StopIteration:
pass
else:
Clock.schedule_once(next_step, t) # having control you can resume func execution after some time
next_step()
return wrapper
@yield_to_sleep # use this decorator to cast 'yield' to non-blocking sleep
def test_function():
for i in range(10):
if (i % 2 == 0):
yield 5 # use yield to "sleep"
print('Called')
else:
print('Whatever')
class TestApp(App):
def build(self):
test_function()
return BoxLayout()
if __name__ == '__main__':
TestApp().run()
Upd:
def func():
yield 'STEP 1'
yield 'STEP 2'
gen = func()
print('Result of calling generator-function is generator object:', gen, '\n')
res1 = next(gen)
print('''Calling next() on generator object executes original
function to the moment of yield point and freeze it\'s state:\n''', res1, '\n')
print('''Look at line of code that prints this mesage:
It\'s not located inside func(),
but we were able to call it "in the middle" of executing func():
We see msg "STEP 1", but we don't see "STEP 2" yet.
This is what makes generators so cool: we can execute part of it,
do anything we want and resume execution later.
In "yield_to_sleep" this resuming delegated to Clock.schedule_once
making generator being executed after some time and without freezing anything.''', '\n')
res2 = next(gen)
print('Another next() and we on next yield point inside func():\n', res2, '\n')
try:
next(gen)
except StopIteration:
print('''If no yield points left, another next() call will finish func()
and raise StopIteration exception. It\'s not error it\'s just way
to say to outer code that generator is done.''', '\n')