Error in assigning numeric value in cock.schedule_interval

50 views
Skip to first unread message

Luigi Marongiu

unread,
May 18, 2020, 2:23:15 AM5/18/20
to Kivy users support
Hello, 
I am trying to run a function 'every minute on the minute', thus I am calculating the time left between the current time and 60 seconds, then pass this value to Clock.schedule_interval() in order to play a sound. But I got:
```
time is: 08:14:14
minutes 14
seconds 14
next minute 15
seconds left 46
timer (to pass over): <NumericProperty name=>
 Traceback (most recent call last):
   File "Clock.py", line 68, in <module>
     ClockApp().run()
   File "/home/marongiuluigi/.local/lib/python3.5/site-packages/kivy/app.py", line 828, in run
     self.load_kv(filename=self.kv_file)
   File "/home/marongiuluigi/.local/lib/python3.5/site-packages/kivy/app.py", line 599, in load_kv
     root = Builder.load_file(rfilename)
   File "/home/marongiuluigi/.local/lib/python3.5/site-packages/kivy/lang/builder.py", line 301, in load_file
     return self.load_string(data, **kwargs)
   File "/home/marongiuluigi/.local/lib/python3.5/site-packages/kivy/lang/builder.py", line 402, in load_string
     root=widget, rule_children=rule_children)
   File "/home/marongiuluigi/.local/lib/python3.5/site-packages/kivy/uix/widget.py", line 469, in apply_class_lang_rules
     rule_children=rule_children)
   File "/home/marongiuluigi/.local/lib/python3.5/site-packages/kivy/lang/builder.py", line 538, in apply
     rule_children=rule_children)
   File "/home/marongiuluigi/.local/lib/python3.5/site-packages/kivy/lang/builder.py", line 659, in _apply_rule
     child, crule, rootrule, rule_children=rule_children)
   File "/home/marongiuluigi/.local/lib/python3.5/site-packages/kivy/lang/builder.py", line 654, in _apply_rule
     child = cls(__no_builder=True)
   File "Clock.py", line 46, in __init__
     self.timer = Clock.schedule_interval(self.update_time, 1)
   File "kivy/properties.pyx", line 497, in kivy.properties.Property.__set__
   File "kivy/properties.pyx", line 526, in kivy.properties.Property.set
   File "kivy/properties.pyx", line 657, in kivy.properties.NumericProperty.convert
 ValueError: clockLabel.timer has an invalid format (got <ClockEvent (1.0) callback=<bound method clockLabel.update_time of <__main__.clockLabel object at 0x7d78c0197ee8>>>)
```
This is true even if I pass the variable with the time left directly or convert it into kivy's NumericProperty.
How can I properly pass the time left to `Clock`? 
Thank you

Elliot Garbus

unread,
May 18, 2020, 9:29:29 AM5/18/20
to kivy-...@googlegroups.com

Share some code.

 

It looks like the problem is the callback function, not the time.

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/53938d4d-9316-4898-ac77-c8b369b77943%40googlegroups.com.

 

Elliot Garbus

unread,
May 18, 2020, 11:35:30 AM5/18/20
to kivy-...@googlegroups.com

Here is a working example:

 

from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.core.audio import SoundLoader

import datetime

kv =
"""
BoxLayout:
    Label:
        text: 'Sound Every Minute'
        font_size: 50
"""


class TimeToMinApp(App):
   
def __init__(self, **kwargs):
       
self.sound = SoundLoader.load('music\iterate-040.wav')
       
super().__init__(**kwargs)

   
def build(self):
       
return Builder.load_string(kv)

   
def on_start(self):
        Clock.schedule_once(
self.next_min, self.seconds_to_minute())
       
print(f'seconds to minute: {self.seconds_to_minute()}')

   
def next_min(self, dt):
       
self.sound.play()
        Clock.schedule_interval(
self.play_chime, 60)

   
def play_chime(self, dt):
        
self.sound.play()

   
@staticmethod
   
def seconds_to_minute():
        now = datetime.datetime.now()
        next_min = now.replace(
microsecond=0, hour=0, second=0) + datetime.timedelta(minutes=1)
       
return (next_min - now.replace(hour=0)).total_seconds()


TimeToMinApp().run()

Elliot Garbus

unread,
May 18, 2020, 1:26:34 PM5/18/20
to kivy-...@googlegroups.com

I added a little test – and see I am off by one second.  I haven’t taken the time to see why…

I added a second to the wait time.

 

I’m reminded of a joke:

The 2 hardest problems in computer science are:  Memory Leaks, Race Conditions, and off by one problems

 

 

 

from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.core.audio import SoundLoader
from kivy.properties import StringProperty

import datetime

kv =
"""
BoxLayout:
    orientation: 'vertical'

    Label:
        text: 'Sound Every Minute'
        font_size: 50
    Label:
        text: 'Chime Time: ' + app.chime_time
   
"""


class TimeToMinApp(App):
    chime_time = StringProperty(
'Time of Chime')

   
def __init__(self, **kwargs):
       
self.sound = SoundLoader.load('music\iterate-040.wav')
       
super().__init__(**kwargs)

   
def build(self):
       
return Builder.load_string(kv)

   
def on_start(self):
        Clock.schedule_once(
self.next_min, self.seconds_to_minute())

   
def next_min(self, dt):
        
self.sound.play()
        Clock.schedule_interval(
self.play_chime, 60)
       
self.chime_time = str(datetime.datetime.now())
       
    
def play_chime(self, dt):
       
self.sound.play()
        now = datetime.datetime.now()
       
print(now)
        
self.chime_time = str(now)

   
@staticmethod
   
def seconds_to_minute():

        now = datetime.datetime.now()
        next_min = now.replace(
microsecond=0, hour=0, second=0) + datetime.timedelta(minutes=1)
       
return (next_min - now.replace(hour=0)).total_seconds() + 1


TimeToMinApp().run()

Luigi Marongiu

unread,
May 18, 2020, 2:18:39 PM5/18/20
to Kivy users support
ops sorry I forgot the attachments...



--
Best regards,
Luigi
Clock.py
bell.mp3
clock.kv
sleepy.jpg

Elliot Garbus

unread,
May 18, 2020, 5:52:03 PM5/18/20
to kivy-...@googlegroups.com

Summary of changes, move most of the attributes to be instance variables, rather than class variables

The problem you were experiencing was caused by setting the NumericProperty Timer to Clock.schedule_interval()

I changed the second use of timer to self.one_second_timer.

 

class clockLabel(Label):
    current_time = StringProperty(strftime(
'%H:%M:%S'))
    timer = NumericProperty()

   
def __init__(self, **kwargs):
       
super().__init__(**kwargs)
       
self.sound = SoundLoader.load('bell.mp3')
       
self.curr_min = int(strftime('%M'))
       
self.curr_sec = int(strftime('%S'))
       
self.next_call = self.curr_min + 1
       
self.timer = self.time_left = 60 - self.curr_sec
       
print("time is:", strftime('%H:%M:%S'))
       
print("minutes", self.curr_min)
       
print("seconds", self.curr_sec)
       
self.one_second_timer = Clock.schedule_interval(self.update_time, 1# This was the problem
       
self.ticker = Clock.schedule_interval(self.ring_bell, self.timer)
       
# self.ticker = Clock.schedule_interval(self.ring_bell, time_left)


   
def update_time(self, dt):
       
self.current_time = strftime('%H:%M:%S')

   
def ring_bell(self, dt):
       
print("BOING")
       
self.sound.play()


   
def cancel_time(self):
       
self.one_second_timer.cancel()

Luigi Marongiu

unread,
May 20, 2020, 4:18:03 AM5/20/20
to Kivy users support
I see! I named a variable twice, how silly of me. Thank you for the fix.
However, the sync is not good still: I have these issues:
1. I can hear the bell the first time, so it is not a problem of codec but of launching the sound each time.
2. The `ring_bell` function is called twice each time
3. The update does not go in sync every minute; I think I will have to work on how to hold the horses until `00` is stuck and then re-launch the thing. Can I move the variables related to time into a Clock.schedule_interval?
What I get is:
```
$ python3 Clock.py
[INFO   ] [Logger      ] Record log in /home/marongiuluigi/.kivy/logs/kivy_20-05-20_11.txt
[INFO   ] [Kivy        ] v1.11.1
[INFO   ] [Kivy        ] Installed at "/home/marongiuluigi/.local/lib/python3.5/site-packages/kivy/__init__.py"
[INFO   ] [Python      ] v3.5.3 (default, Sep 27 2018, 17:25:39)
[GCC 6.3.0 20170516]
[INFO   ] [Python      ] Interpreter at "/usr/bin/python3"
[INFO   ] [Factory     ] 184 symbols loaded
[INFO   ] [ImageLoaderFFPy] Using ffpyplayer 4.3.1
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil, img_ffpyplayer, img_gif
[INFO   ] [Text        ] Provider: sdl2
[INFO   ] [Window      ] Provider: sdl2(['window_egl_rpi'] ignored)
[INFO   ] [GL          ] Using the "OpenGL" graphics system
[INFO   ] [GL          ] Backend used <sdl2>
[INFO   ] [GL          ] OpenGL version <b'3.1 Mesa 19.2.0-devel'>
[INFO   ] [GL          ] OpenGL vendor <b'Red Hat'>
[INFO   ] [GL          ] OpenGL renderer <b'virgl'>
[INFO   ] [GL          ] OpenGL parsed version: 3, 1
[INFO   ] [GL          ] Shading version <b'1.40'>
[INFO   ] [GL          ] Texture max size <16384>
[INFO   ] [GL          ] Texture max units <16>
[INFO   ] [Window      ] auto add sdl2 input provider
[INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked
[INFO   ] [SoundFFPy   ] Using ffpyplayer 4.3.1
[INFO   ] [Audio       ] Providers: audio_ffpyplayer, audio_sdl2
time is: 10:14:31
minutes 14
seconds 31
next minute 15
seconds left 29
waiting for 29 seconds
new waiting is 60 seconds
time is: 10:15:00
minutes 15
seconds 0
next minute 16
seconds left 60
waiting for 60 seconds
new waiting is 60 seconds
[INFO   ] [Base        ] Start application main loop
[INFO   ] [GL          ] NPOT texture support is available
##### KIVY IS LAUNCHED BUT REMAINS FROZEN #####
```

Thank you



--
Best regards,
Luigi
clock.kv
bell.mp3
Clock.py

Elliot Garbus

unread,
May 20, 2020, 11:13:02 AM5/20/20
to kivy-...@googlegroups.com

Code below.

The `ring_bell` function is called twice each time

 

You were instancing the kv code twice.  You have a root widget in the kv file, and were instancing the root widget in python. This is fixed in the code below.

 

The update does not go in sync every minute; I think I will have to work on how to hold the horses until `00` is stuck and then re-launch the thing. Can I move the variables related to time into a Clock.schedule_interval?

 

In the code below you can see I have used a Clock.schedule_once to consume the time to the top of the minute, and then use Clock.schedule_interval() to ring the bell every minute. The method seconds to minute, calculates the number of seconds until the top of the hour.  I have rounded the result to hundredths of a second.

 
 
 
import kivy
kivy.require(
'1.10.1')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.core.text import LabelBase
from kivy.properties import StringProperty,  NumericProperty
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.core.audio import SoundLoader


from time import strftime
import datetime
import os

# set sound env
# `python3 -m pip install ffpyplayer`
os.environ['KIVY_VIDEO'] = 'ffpyplayer'         

class MyGrid(Widget):
   
pass

class
ImageButton(ButtonBehavior, Image):
   
pass

class
ClockLabel(Label):
    current_time = StringProperty(strftime(
'%H:%M:%S'))

   
def __init__(self, **kwargs):
       
super().__init__(**kwargs)
       
self.sound = SoundLoader.load('bell.mp3')
       
self.main_clock = Clock.schedule_interval(self.update_time, 1)
        Clock.schedule_once(
self.next_min, self.seconds_to_minute())

   
def update_time(self, dt):
       
self.current_time = strftime('%H:%M:%S')

   
def ring_bell(self, dt):
       
print(f'ring_bell: {strftime("%H:%M:%S")}')
       
self.sound.play()

   
def next_min(self, dt):
       
self.sound.play()
        Clock.schedule_interval(
self.ring_bell, 60)
       
print(f"first chime: {datetime.datetime.now()}")

   
@staticmethod
   
def seconds_to_minute():

        now = datetime.datetime.now()
        next_min = now.replace(
microsecond=0, hour=0, second=0) + datetime.timedelta(minutes=1)
       
return round((next_min - now.replace(hour=0)).total_seconds() + 1, 2)

   
def cancel_time(self):
       
self.main_clock.cancel()

   
def find_time(self):
       
pass
       
# to be defined, maybe a Clock.schedule_interval that set the time


class ClockApp(App):
   
pass
   
# def build(self):
    #     return MyGrid()  # this causes 2 copies of the code to be instanced.


if __name__ == "__main__":
    ClockApp().run()

Luigi Marongiu

unread,
May 21, 2020, 2:58:33 AM5/21/20
to Kivy users support
Thank you. I actually simplified the code since I realized that I don't have to test the time: I already have a time test each second with the main clock. When seconds are 0 I ring the bells. 
Now the main problem left is that the sound is played only the first time. Is there a way to re-schedule the sound?
Best regards



--
Best regards,
Luigi
clock.kv
Clock.py
bells.mp3
sleepy.jpg

Elliot Garbus

unread,
May 21, 2020, 8:58:00 AM5/21/20
to kivy-...@googlegroups.com

You have a root widget in the kv file, and you are instancing the root widget in python.  This causes 2 copies of the kv code to be used.  This is why you see 2 print outs.

I hear a bell at every minute, but it is possible it is causing problems on your system. 

 

class ClockApp(App):
   
# def build(self):
    #     return MyGrid()
   
pass

Luigi Marongiu

unread,
May 22, 2020, 12:56:45 PM5/22/20
to Kivy users support
I have seen there was a leftover `MyGrid` at the end of the kivy file.
I removed it and now the time is printed only once. Still the time is
played only once.
I don't get what is the extra root on the kivy file...
Thanks
> To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/5ec67ad0.1c69fb81.79fea.8b61SMTPIN_ADDED_MISSING%40gmr-mx.google.com.



--
Best regards,
Luigi
Clock.py
clock.kv
bells.mp3
sleepy.jpg

Elliot Garbus

unread,
May 22, 2020, 1:14:17 PM5/22/20
to kivy-...@googlegroups.com

On Windows10, I hear a bell every minute.  Just an idea, try to seek to the beginning of the sound file.

 


def update_time(self, dt):
   
self.current_time = strftime('%H:%M:%S'
)
   
self.curr_sec =  int(strftime('%S'))
   
if self.curr_sec == 0:
       
print("time is:", strftime('%H:%M:%S'))
       
self.sound.play()
       
self.sound.seek(0)
Reply all
Reply to author
Forward
0 new messages