'How to change the behaviour of a Label, in Python, based on a bound NumericProperty
Question: How to change a Label's behavior, in python, triggered when the Event fires on a NumericProperty change?
I can do this in KivyLang, but I can't figure out how to do it in Python.
I have an external module which "sends" data to the Kivy app. Naturally, I wish widgets to be "bound" to the data - so they change automatically to reflect new data. This is working, for example the following binds a label text to an incoming float value:
from kivy.lang import Builder
from kivy.app import App
from kivy.event import EventDispatcher
from kivy.clock import Clock
from kivy.properties import NumericProperty, StringProperty, ObjectProperty
kv = """
Label:
font_size: 50
text: "Number: {}".format(app.data_store.exampleNumeric)
"""
class MyDataStore(EventDispatcher):
exampleNumeric = NumericProperty(-1)
exampleString: StringProperty("no data yet")
class HappyApp(App):
data_store = ObjectProperty(MyDataStore(), rebind=True)
#this func will be called from external modules, and kivy widgets should auto-refresh the new data
def UpdateDataStore(self, incomingdata):
self.data_store.exampleNumeric = incomingdata
def build(self):
return Builder.load_string(kv)
app=HappyApp()
#just using a Clock to simulate incoming data, to keep this question short
Clock.schedule_once(lambda dt : app.UpdateDataStore(42), 1)
app.run()
This works. However I would like to develop more complex, reactive, behaviour. Simple example: make the Label turn Red colour, if exampleNumericProperty < 10.
I can do this in kivy lang, for example like this:
<MyFloatLayout>:
Label:
font_size: 30
text: "Number: {}".format(app.data_store.exampleNumeric)
color: [1, 0, 0, 1] if app.data_store.exampleNumeric < 10 else [1,1,1,1]
But I don't know how to do that in Python...I don't know "where" to trigger behavioral changes of e.g a specific Label, so they automatically fire whenever a bound Property changes e.g "exampleNumericProperty. Because I might need to make 5 or 10 UI changes, when exampleNumericProperty changes, and it seems a bit unweildly to do that in KV?
Specific Question: The above KVLang will automatically detect if my numericpropery changes, and update the Label Colour accordingly. How do I acheive exactly the same thing, in Python?
I might be thinking about this all wrong. Corrections to bad thinking welcomed. In my head, I'm looking for something like this.
kv = """
Label:
font_size: 50
Monitor_exampleNumeric_for_changes:
self.do_several_things_to_update_this_label_if_exampleNumeric_changed()
"""
def somewhere.do_several_things_to_update_this_label_if_exampleNumeric_changed():
n = app.data_store.exampleNumeric
s = app.data_store.exampleString
if n > 20: label.colour = red
is s = "offline": label.font_size = 50
is s = "online: label.text = ">>" + label.text
Goal: seek to make a number of changes to the Label, depending on various other variables, to be triggered whenever one specific Property changes
If this design.goal seems bad, very happy to be directed towards a better way of doing it.
Solution 1:[1]
its simpler this way. consider this in kv i have a label this way:
Label:
id: lb
pos: ## whatever
size: ## whatever
in main i have this:
Clock.schedule_interval(self.update_label, 1):
def update_label(self, *args):
if float_data = ## whatever u need:
self.ids.lb.color = [1, 1, 1, 1] # white
if float_data = ## whatever u need:
self.ids.lb.color = [1, 0, 0, 1] # red
u can start the clock whenver u need to and stop it like this Clock.unschedule(self.update_label)
Solution 2:[2]
I'm not sure whether or not I can follow you. But the following modified version of your example may meet your requirement.
from kivy.lang import Builder
from kivy.app import App
from kivy.event import EventDispatcher
from kivy.clock import Clock
from kivy.properties import NumericProperty, StringProperty, ObjectProperty
kv = """
Label:
font_size: 50
text: str(app.modified_value)
# text: "Number: {}".format(app.data_store.exampleNumeric)
"""
class MyDataStore(EventDispatcher):
exampleNumeric = NumericProperty(-1)
exampleString: StringProperty("no data yet")
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Bind a callback function to listen to any changes in its value.
self.bind(exampleNumeric = self.modify_app_value)
# Change its value (just a simulation).
Clock.schedule_once(lambda *args : setattr(self, "exampleNumeric", 42), 1.0)
def modify_app_value(self, instance, new_value):
# Access the running app.
app = App.get_running_app()
# Now access and modify the target value, implement your logic here.
app.modified_value = new_value
# Or you can call the bound method from app.
# app.UpdateDataStore(new_value)
class HappyApp(App):
data_store = ObjectProperty(MyDataStore(), rebind=True)
modified_value = NumericProperty(1) # This is the target value.
#this func will be called from external modules, and kivy widgets should auto-refresh the new data.
def UpdateDataStore(self, incomingdata):
self.modified_value = incomingdata
# Implement required logic here.
# self.data_store.exampleNumeric = incomingdata
def build(self):
return Builder.load_string(kv)
app=HappyApp()
#just using a Clock to simulate incoming data, to keep this question short
#Clock.schedule_once(lambda dt : app.UpdateDataStore(42), 1)
app.run()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Oussama |
| Solution 2 |
