Property change dispatch help

212 views
Skip to first unread message

Sahil Mahajan

unread,
Oct 1, 2015, 5:55:31 AM10/1/15
to Kivy users support
Hello, I have been trying to figure this out for the past few days now. It seems like a simple problem, but I find it difficult since I am new to Kivy.

My question is "How do I make a property change be observed everywhere, and not just one place?"

I am able to enable and disable a button by using a switch. Switch has a boolean property called "active." However, when I try and print the value "active," it always returns false, regardless whether the switch is active or inactive.

Here is my main.py
__version__ = "1.0"

import kivy
import plyer
kivy
.require('1.0.9')

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.switch import Switch
from kivy.utils import platform
from kivy.lib import osc
from kivy.clock import Clock
from kivy.properties import Property

activityport
= 3001
serviceport
= 3000



class switch(Switch):
       
def callback(self):
                button
.disabled = not self.active


class button(Button):
       
def callback(self):
                plyer
.vibrator.vibrate(3)

class vibrate(Widget):
       
pass


class ActiveApp(App):
       
def build(self):
               
if platform == 'android':
                       
from android import AndroidService
                        service
= AndroidService()
                        service
.start()
                       
self.service = service

                osc
.init()
                oscid
= osc.listen(ipAddr='127.0.0.1', port=activityport)
                osc
.bind(oscid, switch.callback, '~/kivyinstall/sahil/reply/step3_active')
               
Clock.schedule_interval(lambda *x: osc.readQueue(oscid), 0)

               
return vibrate()

       
def send(self):
                osc
.sendMsg('~/kivyinstall/sahil/reply/step3_active/service',[switch().property('active').get(switch()), ], port=serviceport)



if __name__ == '__main__':
   
ActiveApp().run()






Here is my active.kv:
#:kivy 1.0.9


<switch>:
        on_touch_down
: self.callback()
        on_touch_down
: app.send()


<button>:
        text
: "Vibrate"
        size
: 300,300
       
#on_release: self.callback()

<vibrate>:
       
Label:
        button
:
                center_x
: self.parent.width * 3/4
                center_y
: self.parent.height * 1/2

       
switch:
                center_x
: self.parent.width * 1/4
                center_y
: self.parent.height * 1/2


And here is my service/main.py
from time import sleep
from kivy.lib import osc


serviceport
= 3000

def switch_callback(message, *args):
       
print("%s" % message)


if __name__ == '__main__':
        osc
.init()
        oscid
= osc.listen(ipAddr='127.0.0.1', port=serviceport)
        osc
.bind(oscid, switch_callback, '~/kivyinstall/sahil/reply/step3_active/service')

       
while True:
                osc
.readQueue(oscid)
                sleep
(.1)



I am using OSC to send messages. When I run main.py and service/main.py at the same time clicking on the widget sends a message to the service port. It is here where I see that the message always returns False, even if the switch's active property is True. Why does button.disabled change with self.active, but when I try and print self.active, it always returns False?

Thank you for your help.

Alexander Taylor

unread,
Oct 1, 2015, 6:06:24 AM10/1/15
to Kivy users support
You have 'switch().property('active').get(switch())'

This doesn't give you the active property of your switch, but instead presumably (I didn't check) of a new switch object, in which case it has the default value of False.

Also, you should give your widgets names beginning with an upper case letter, e.g. Vibrate, as kv uses this to distinguish properties from child widgets. In the case of your switch, that also means you should give it a name that doesn't clash with the kivy built in widget.

Sahil Mahajan

unread,
Oct 1, 2015, 6:09:43 PM10/1/15
to Kivy users support
I see. I tried to make the code see the same switch object in this following code. I am not sure if I did this correctly.

main.py:

__version__
= "1.0"

import kivy
import plyer
kivy
.require('1.0.9')

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.switch import Switch
from kivy.utils import platform
from kivy.lib import osc
from kivy.clock import Clock


activityport
= 3001
serviceport
= 3000



class ActiveSwitch(Switch):
       
def callback(self):
               
VibrateButton.disabled = not self.active

class VibrateButton(Button):
       
def callback(self):
                plyer
.vibrator.vibrate(3)

class Vibrate(Widget):
       
switch = ActiveSwitch()
       
def update(self):
               
return self.switch.property('active').get(self.switch)



class ActiveApp(App):
       
def build(self):
               
if platform == 'android':
                       
from android import AndroidService
                        service
= AndroidService()
                        service
.start()
                       
self.service = service

                osc
.init()
                oscid
= osc.listen(ipAddr='127.0.0.1', port=activityport)

                osc
.bind(oscid, Vibrate.update, '~/kivyinstall/sahil/reply/step3_active')
               
Clock.schedule_interval(lambda *x: osc.readQueue(oscid), 0)

               
return Vibrate()

       
def send(self):
                osc
.sendMsg('~/kivyinstall/sahil/reply/step3_active/service',[Vibrate().update(), ], port=serviceport)




if __name__ == '__main__':
   
ActiveApp().run()


active.kv:

#:kivy 1.0.9


<ActiveSwitch>:
        on_touch_down
: self.callback(), app.send()


<VibrateButton>:

        text
: "Vibrate"
        size
: 300,300
       
#on_release: self.callback()

<Vibrate>:
       
switch: enable
       
Label:
       
VibrateButton:

                center_x
: self.parent.width * 3/4
                center_y
: self.parent.height * 1/2


       
ActiveSwitch:
                id
: enable
                center_x
: self.parent.width * 1/4
                center_y
: self.parent.height * 1/2


Also, when I run the code, I get the following error. For some reason OSC is not able to send this message.

Error:
 Traceback (most recent call last):
   
File "main.py", line 55, in <module>
     
ActiveApp().run()
   
File "/usr/lib/python2.7/dist-packages/kivy/app.py", line 824, in run
     runTouchApp
()
   
File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 487, in runTouchApp
     
EventLoop.window.mainloop()
   
File "/usr/lib/python2.7/dist-packages/kivy/core/window/window_sdl2.py", line 539, in mainloop
     
self._mainloop()
   
File "/usr/lib/python2.7/dist-packages/kivy/core/window/window_sdl2.py", line 300, in _mainloop
     
EventLoop.idle()
   
File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 330, in idle
     
self.dispatch_input()
   
File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 315, in dispatch_input
     post_dispatch_input
(*pop(0))
   
File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 221, in post_dispatch_input
     listener
.dispatch('on_motion', etype, me)
   
File "_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6856)
   
File "/usr/lib/python2.7/dist-packages/kivy/core/window/__init__.py", line 904, in on_motion
     
self.dispatch('on_touch_down', me)
   
File "_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6856)
   
File "/usr/lib/python2.7/dist-packages/kivy/core/window/__init__.py", line 920, in on_touch_down
     
if w.dispatch('on_touch_down', touch):
   
File "_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6856)
   
File "/usr/lib/python2.7/dist-packages/kivy/uix/widget.py", line 382, in on_touch_down
     
if child.dispatch('on_touch_down', touch):
   
File "_event.pyx", line 695, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6815)
   
File "_event.pyx", line 1168, in kivy._event.EventObservers.dispatch (kivy/_event.c:11690)
   
File "_event.pyx", line 1052, in kivy._event.EventObservers._dispatch (kivy/_event.c:10730)
   
File "/usr/lib/python2.7/dist-packages/kivy/lang.py", line 1465, in custom_callback
     
exec(__kvlang__.co_value, idmap)
   
File "./active.kv", line 5, in <module>
     on_touch_down
: self.callback(), app.send()
   
File "main.py", line 50, in send
     osc
.sendMsg('~/kivyinstall/sahil/reply/step3_active/service',[Vibrate().update(), ], port=serviceport)
   
File "main.py", line 31, in update
     
return self.switch.property('active').get(self.switch)
 
TypeError: Argument 'obj' has incorrect type (expected kivy._event.EventDispatcher, got kivy.weakproxy.WeakProxy)
[ERROR  ] [OSC         ] Error in Tuio recv()
Traceback (most recent call last):
 
File "/usr/lib/python2.7/dist-packages/kivy/lib/osc/oscAPI.py", line 229, in run
    message
= self.socket.recv(65535)
error
: [Errno 4] Interrupted system call

Alexander Taylor

unread,
Oct 1, 2015, 7:27:54 PM10/1/15
to kivy-...@googlegroups.com
I didn't check if this causes your problem, but in the Vibrate class you
should replace 'switch = ActiveSwitch()' with something like 'switch =
ObjectProperty()' (and import that if you haven't already).

Also, replace 'self.switch.property('active').get(self.switch)' with
just 'self.switch.active'.

Also, 'osc.bind(oscid,Vibrate.update...' probably doesn't make sense,
you likely need to pass the update method of an instance, not of the
object itself.
> if__name__ =='__main__':
> ActiveApp().run()
>
> |
>
> active.kv:
>
> |
> #:kivy 1.0.9
>
>
> <ActiveSwitch>:
> on_touch_down:self.callback(),app.send()
>
>
> <VibrateButton>:
> text:"Vibrate"
> size:300,300
> #on_release: self.callback()
>
> <Vibrate>:
> switch:enable
> Label:
> VibrateButton:
> center_x:self.parent.width *3/4
> center_y:self.parent.height *1/2
>
> ActiveSwitch:
> id:enable
> center_x:self.parent.width *1/4
> center_y:self.parent.height *1/2
> |
>
>
> Also, when I run the code, I get the following error. For some reason
> OSC is not able to send this message.
>
> Error:
> |
> Traceback(most recent call last):
> returnself.switch.property('active').get(self.switch)
> TypeError:Argument'obj'has incorrect type (expected
> kivy._event.EventDispatcher,got kivy.weakproxy.WeakProxy)
> [ERROR ][OSC ]ErrorinTuiorecv()
> Traceback(most recent call last):
> File"/usr/lib/python2.7/dist-packages/kivy/lib/osc/oscAPI.py",line
> 229,inrun
> message =self.socket.recv(65535)
> error:[Errno4]Interruptedsystem call
>
> |
>
>
>
>
>
>
> On Thursday, October 1, 2015 at 3:06:24 AM UTC-7, Alexander Taylor wrote:
>
> You have '|switch().property('active').get(switch())*'
>
> This doesn't give you the active property of your switch, but
> instead presumably (I didn't check) of a new switch object, in which
> case it has the default value of False.
>
> Also, you should give your widgets names beginning with an upper
> case letter, e.g. Vibrate, as kv uses this to distinguish properties
> from child widgets. In the case of your switch, that also means you
> should give it a name that doesn't clash with the kivy built in widget.
> *|
> if__name__ =='__main__':
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "Kivy users support" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/kivy-users/qS-2FmB6t6Q/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> kivy-users+...@googlegroups.com
> <mailto:kivy-users+...@googlegroups.com>.
> For more options, visit https://groups.google.com/d/optout.


signature.asc

Sahil Mahajan

unread,
Oct 1, 2015, 8:16:56 PM10/1/15
to Kivy users support
I made the changes to the main.py and active.kv files.

The problem lies with connecting "switch" in the Vibrate class to an instance of the ActiveSwitch class. For some reason, I am unable to obtain the state of "active." However, when I put "VibrateButton.disabled' into osc.sendMsg, it returns true when the button is disabled and false when it isn't. Why does it work with VibrateButton.disabled but not ActiveSwitch.active?

In the kv file, I attempted to connect "switch" with the ActiveSwitch class. Did I do this correctly? If yes, how can I get self.switch.active to return True or False when ActiveSwitch is active or inactive?

main.py:
kivy.require('1.0.9')

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.switch import Switch
from kivy.utils import platform
from kivy.lib import osc
from kivy.clock import Clock
from kivy.properties import BooleanProperty


activityport
= 3001
serviceport
= 3000



class ActiveSwitch(Switch):
       
def callback(self):
               
VibrateButton.disabled = not self.active

class VibrateButton(Button):
       
def callback(self):
                plyer
.vibrator.vibrate(3)

class Vibrate(Widget):

       
switch = BooleanProperty(False)
       
def update(self):
               
return self.switch.active


class ActiveApp(App):
       
def build(self):
               
if platform == 'android':
                       
from android import AndroidService
                        service
= AndroidService()
                        service
.start()
                       
self.service = service
               
                osc
.init()
                oscid
= osc.listen(ipAddr='127.0.0.1', port=activityport)

                osc
.bind(oscid, Vibrate().update, '~/kivyinstall/sahil/reply/step3_active')

               
Clock.schedule_interval(lambda *x: osc.readQueue(oscid), 0)

               
return Vibrate()

       
def send(self):
                osc
.sendMsg('~/kivyinstall/sahil/reply/step3_active/service',[Vibrate().update(), ], port=serviceport)



if __name__ == '__main__':
   
ActiveApp().run()


active.kv:
#:kivy 1.0.9


<ActiveSwitch>:
        on_touch_down
: self.callback(), app.send()


<VibrateButton>:
        text
: "Vibrate"
        size
: 300,300
       
#on_release: self.callback()

<Vibrate>:
       
switch: enable
       
Label:
       
VibrateButton:

                center_x
: self.parent.width * 3/4
                center_y
: self.parent.height * 1/2


       
ActiveSwitch:
                id
: enable
                center_x
: self.parent.width * 1/4
                center_y
: self.parent.height * 1/2

Sahil Mahajan

unread,
Oct 1, 2015, 8:20:28 PM10/1/15
to Kivy users support
Forgot to mention, when I run this, it always returns False. When I put switch = BooleanProperty(True), it still returns False.

Alexander Taylor

unread,
Oct 2, 2015, 6:07:14 AM10/2/15
to kivy-...@googlegroups.com
You still have a lot of confusion about the difference between a class
definition (e.g. VibrateButton) and a class instance (e.g. the result of
VibrateButton()). All your problems relate to this, e.g. setting
'VibrateButton.disabled = not self.active' which does *not* set the
disabled property of any particular VibrateButton instance but instead
overrides the property itself in the definition of the VibrateButton class.

Likewise, Vibrate().update refers to the update method of a new Vibrate
instance that has no relation to anything else in your program and is
meaningless.

Note that when you do e.g. 'a = Vibrate(); b = Vibrate()', you now have
two different objects representing two different instances of the
Vibrate class. In this context, consider what it would even mean to do
Vibrate().update - which of these would it refer to, and why? (The
answer is neither). Likewise, doing something like
'Vibrate.some_property = True' would affect neither of them.

On 02/10/15 01:20, Sahil Mahajan wrote:
> Forgot to mention, when I run this, it always returns False. When I put
> switch = BooleanProperty(True), it still returns False.
>
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "Kivy users support" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/kivy-users/qS-2FmB6t6Q/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> kivy-users+...@googlegroups.com
> <mailto:kivy-users+...@googlegroups.com>.
signature.asc

Sahil Mahajan

unread,
Oct 3, 2015, 12:59:14 PM10/3/15
to Kivy users support
I now understand what I was doing wrong. I was confused about the kivy language in the active.kv file about how to make instances of classes. Previously, when I made the vibrate button disabled, I was not actually making the specific instance of the VibrateButton class disabled, but rather the property of the VibrateButton class. Here are my new active.kv and main.py files:

active.kv:
#:kivy 1.0.9



<VibrateButton>:
        text
: "Vibrate"
        size
: 300,300

<Vibrate>:
        switch_active
: switch_active
       
Label:

       
VibrateButton:
                center_x
: self.parent.width * 3/4
                center_y
: self.parent.height * 1/2

                disabled
: not switch_active.active
                on_release
: self.vibrate()

       
ActiveSwitch:
                id
: switch_active
                on_touch_down
: root.ping()

                center_x
: self.parent.width * 1/4
                center_y
: self.parent.height * 1/2


main.py:
__version__ = "1.0"

import kivy
import
plyer
kivy
.require('1.0.9')


from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.switch import Switch
from kivy.utils import platform
from kivy.clock import Clock
from kivy.properties import BooleanProperty
from kivy.lib import osc

activityport
= 3001
serviceport
= 3000

def message_callback(message, *args):
       
print("got a message! %s" % message)

class ActiveSwitch(Switch):
       
pass

class VibrateButton(Button):
       
def vibrate(self):
                plyer
.vibrator.vibrate(3)

class Vibrate(Widget):
       
def ping(self):
                osc
.sendMsg('~/kivyinstall/sahil/reply/step3_active/service', [not self.switch_active.active, ], port=serviceport)




class ActiveApp(App):
       
def build(self):
               
if platform == 'android':
                       
from android import AndroidService
                        service
= AndroidService()
                        service
.start()
                       
self.service = service

                osc
.init()
                oscid
= osc.listen(ipAddr='127.0.0.1', port=activityport)

                osc
.bind(oscid, message_callback, '~/kivyinstall/sahil/reply/step3_active')

               
Clock.schedule_interval(lambda *x: osc.readQueue(oscid), 0)

               
return Vibrate()



if __name__ == '__main__':
   
ActiveApp().run()


In the active.kv file, to make the specific instance of the VibrateButton class disabled, I had to write "disabled: not switch_active.active" under the VibrateButton rule in the Vibrate class rule, where switch_active is the id of the specific instance of ActiveSwitch class.

The code now returns True when the switch is disabled and False when it is not. Thank you so much for your help! I greatly appreciate it.
Reply all
Reply to author
Forward
0 new messages