Struggling to pass a variable to kivy window. I have read similar threads all over the place but none of the fixes seem to work for me. Im sure this is simple to someone who knows their way around tiny, unfortunately I don't.
main.py
import kivy
from kivy.uix.togglebutton import ToggleButton
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.app import App
kivy.require('1.10.0')
from phue import Bridge
import nest
b = Bridge('xxx.xxx.x.xxx')
b.connect()
b.get_api()
lights = b.lights
class Controller(GridLayout):
print("launching")
def __init__(self):
super(Controller, self).__init__()
def KitchenSpot1(self,state):
lights[0].name
lights[0].on = state
def update(dt):
if b.get_light(1, 'on')== True:
#print("down") # When this line is commented out I get an continuous accurate update on the status of the light, showing that its working.
return 'down' # This is the part I want passed to the state criteria in the ivy window
else:
#print("up")# When this line is commented out I get an continuous accurate update on the status of the light, showing that its working.
return 'down' # This is the part I want passed to the state criteria in the ivy window
class ActionApp(App):
def build(self):
Clock.schedule_interval(Controller.update, 1.0 / 60.0)
return Controller()
myApp = ActionApp()
myApp.run()
action.kv
<Controller>:
cols: 4
rows: 3
spacing: 10
ToggleButton:
id: KitchenSpot1Toggle
text: "Kitchen Spot 1"
on_press: root.KitchenSpot1(True)
#on_release: root.KitchenSpot1(False)
#state1 = app.update.h
state: Controller.update # This is the part that is throwing up the error.
The error:
11: #on_release: root.KitchenSpot1(False)
12: #state1 = app.update.h
>> 13: state: Controller.update
14:
15:
...
NameError: name 'Controller' is not defined
Thanks in advance to anyone that can help me.
Make update an instance method and use a StringProperty to update state property in your kv:
main.py:
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.uix.gridlayout import GridLayout
from kivy.uix.togglebutton import ToggleButton
from phue import Bridge
import nest
b = Bridge('xxx.xxx.x.xxx')
b.connect()
b.get_api()
lights = b.lights
class Controller(GridLayout):
state = StringProperty('normal') # <<<<<<<<<<<<
def __init__(self, **kwargs):
super(Controller, self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1.0 / 60.0)
def KitchenSpot1(self,state):
lights[0].name
lights[0].on = state
def update(self, dt):
if b.get_light(1, 'on'):
self.state = 'down' # <<<<<<<<<<<<
else:
self.state = 'normal' # <<<<<<<<<<<<
class ActionApp(App):
def build(self):
return Controller()
if __name__ == "__main__":
myApp = ActionApp()
myApp.run()
action.kv:
<Controller>:
cols: 4
rows: 3
spacing: 10
state: "normal" # <<<<<<<<<<<<
ToggleButton:
id: KitchenSpot1Toggle
text: "Kitchen Spot 1"
on_press: root.KitchenSpot1(True)
#on_release: root.KitchenSpot1(False)
#state1 = app.update.h
state: root.state # <<<<<<<<<<<<
Here is a more generic simplified answer from the kivy documentation, look for the section called "Keyword arguments and init()" because there are some other ways to do it as well.
The following code passes myvar to the build() method of MyApp. It does this by over-riding the init() of the Kivy App class by a new init() that calls App.init() and then continues with whatever extra initialisation you want. You can then store variables in the MyApp class instances and use them in build().
from kivy.app import App
from kivy.uix.label import Label
myvar = 'Hello Kivy'
class MyApp(App):
def __init__(self, myvar, **kwargs):
super(MyApp, self).__init__(**kwargs)
self.myvar = myvar
def build(self):
widget = Label(text=self.myvar)
return widget
if __name__ == '__main__':
MyApp(myvar).run()
Related
In my Kivy-App, i generate Buttons via a python-class based on a dictionary (in the following example i use a list but that's just an example for the underlying problem).
Within the App, the dictionary gets changed and i want to display that change (obviously) in my App (by adding/ removing/ rearranging the Buttons).
To achieve this, my approach is to either restart the entire App or only reload that particular BoxLayout. Unfortunately, non of my attempts worked out so far and i could not find any (working) solution on the internet.
This is my code example:
Python Code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
buttonlist = []
counter = 0
class MainWindow(BoxLayout):
def addbutton(self):
global buttonlist
global counter
buttonlist.append(counter)
counter += 1
class ButtonBox(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
global buttonlist
for button in buttonlist:
b = Button(text=str(button))
self.add_widget(b)
class KivyApp(App):
def build(self):
return MainWindow()
KivyApp().run()
KV Code:
<MainWindow>:
BoxLayout:
ButtonBox:
Button:
text: "add Button"
on_press: root.addbutton()
My closest attempt was something containing a restart-Method like:
def restart(self):
self.stop()
return KivyApp().run()
and calling:
App.get_running_app().restart()
But for some reason, this does not stop the App but opens a second instance of the App within the first one (resulting in App in App in App in App if pressed often)
You can rebuild the ButtonBox by first calling clear_widgets() on the ButtonBox instance. Here is a modified version of your code that does that:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
kv = '''
<MainWindow>:
BoxLayout:
ButtonBox:
id: box
Button:
text: "add Button"
on_press: root.addbutton()
'''
buttonlist = ['Abba', 'Dabba', 'Doo']
counter = 3
class MainWindow(BoxLayout):
def addbutton(self):
global buttonlist
global counter
buttonlist.append(str(counter))
counter += 1
self.ids.box.reload()
class ButtonBox(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
self.reload()
def reload(self):
# method to rebuild the ButtonBox contents
global buttonlist
self.clear_widgets()
for button in buttonlist:
b = Button(text=str(button))
self.add_widget(b)
class KivyApp(App):
def build(self):
Builder.load_string(kv)
return MainWindow()
KivyApp().run()
I used your kv as a string, just for my own convenience.
Here is my actual code. It does what I want except when I change the state with a script, it doesn't give the information. It only gives the information when the change is made with a user interaction.
from kivymd.app import MDApp
from kivy.lang import Builder
KV = '''
BoxLayout:
ToggleButton:
id: toggle_button
text: "click on me"
'''
class ExampleApp(MDApp):
loading_layout = None
def build(self):
screen = Builder.load_string(KV)
screen.ids["toggle_button"].bind(on_press=lambda instance: self.on_click(instance))
print("I change the state and I want to be aware")
screen.ids["toggle_button"].state = "down"
print("on_click method isn't enough :(")
return screen
def on_click(self, instance):
print("State changed !", instance.state)
ExampleApp().run()
Question: How can I link a function to the state changement and not only on the click ?
What you could do is take advantage of the on_state method of the ToggleButton class. Here is an example:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.togglebutton import ToggleButton
KV = '''
BoxLayout:
CustomToggle:
id: toggle_button
text: "click on me"
'''
class CustomToggle(ToggleButton):
def on_state(self, *args):
print('State changed!', self.state)
def on_press(self):
print("Button pressed!", self.state)
class ExampleApp(MDApp):
loading_layout = None
def build(self):
screen = Builder.load_string(KV)
screen.ids["toggle_button"].state = "down"
return screen
ExampleApp().run()
The on_state method is automatically bound to the state attribute, so that whenever the state is changed the function is called.
Firstly, this is a large piece of code so I will try to simplify it for the sake of this question. I have a Kivy Language script which has a root widget, with an action bar and boxlayout. The code's general structure goes a bit like this (I do not believe this is required for answering this question but here it is anyway): Root > MenuBarWidg > BoxLayout > Image + Other buttons/labels....
Now, here is what my widget looks like in kivy (for the boxlayout):
<DisplayPhoto>:
Image:
id: image_display
allow_strech: True
#StringProperty in the class
source: root.image_path
Button:
Inside my python script:
class DisplayPhoto(BoxLayout):
image_path = StringProperty()
def __init__(self, **kwargs):
super(DisplayPhoto, self).__init__(self)
self.image_path = 'reload.png'
#this is called from another class on a button press
def update(self):
self.image_path = 'new_image_path.png'
Upon calling update in the python script, nothing happens. I have tried print(self.image_path) which displays new_image_path.png, but it is also a string - not a kivy object.
I have tried things such as updating the source by calling the id etc but got nowhere with that. Any help is appreciated!
I guess the problem is how you call that update() method.
Please refer to the following code
One way of doing it:
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.config import Config
Config.set('graphics', 'position', 'custom')
Config.set('graphics', 'left', 0)
Config.set('graphics', 'top', 0)
Builder.load_file('main.kv')
class MainView (BoxLayout):
image_source = StringProperty()
def __init__(self, **kwargs):
super(MainView, self).__init__(**kwargs)
self.image_source = 'pic1.png'
class AnotherClass(BoxLayout):
def change_image(self):
app = App.get_running_app()
app.root.ids['my_image'].source = 'pic2.png'
class ImageChangeApp (App):
def build(self):
return MainView()
if __name__ == '__main__':
ImageChangeApp().run()
main.kv:
<MainView>:
Image:
id: my_image
source: root.image_source
AnotherClass:
<AnotherClass>:
Button:
text: 'Change picture'
on_release: root.change_image()
Another way of doing it is using event dispatcher
main.py:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.config import Config
Config.set('graphics', 'position', 'custom')
Config.set('graphics', 'left', 0)
Config.set('graphics', 'top', 0)
Builder.load_file('main.kv')
class MainView (BoxLayout):
pass
class DisplayPhoto(BoxLayout):
image_path = StringProperty()
def __init__(self, **kwargs):
super(DisplayPhoto, self).__init__(**kwargs)
self.image_path = 'pic1.png'
self.register_event_type('on_image_path')
def on_image_path(self, instance, val):
print(instance)
print(val)
self.image_path = val
class AnotherClass(BoxLayout):
def change_image(self):
app = App.get_running_app()
app.root.ids['display_photo'].dispatch('on_image_path', self, 'pic2.png')
class ImageChangeApp (App):
def build(self):
return MainView()
if __name__ == '__main__':
ImageChangeApp().run()
main.kv
<MainView>:
DisplayPhoto:
id: display_photo
AnotherClass:
<DisplayPhoto>:
Image:
source: root.image_path
<AnotherClass>:
Button:
text: 'Change picture'
on_release: root.change_image()
New to kivy, and OOP.
I'm trying to update a label in kivy with data I pull from a temp sensor. The code that pulls in the sensor data is in labeltempmod. I created a function getTheTemp() that is called every second. In the function I try to assign the text of the label via Label(text=(format(thetemp)), font_size=80). The program ignores this. What am I doing wrong here?
#This is a test to see if I can write the temp to label
import labeltempmod
import kivy
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
def getTheTemp(dt):
thetemp = labeltempmod.readtemp()
Label(text=(format(thetemp)), font_size=80)
print thetemp
class LabelWidget(BoxLayout):
pass
class labeltestApp(App):
def build(self):
# call get_temp 0.5 seconds
Clock.schedule_interval(getTheTemp, 1)
return LabelWidget()
if __name__ == "__main__":
labeltestApp().run()
Here is the kivy language file:
<LabelWidget>:
orientation: 'vertical'
TextInput:
id: my_textinput
font_size: 80
size_hint_y: None
height: 100
text: 'default'
FloatLayout:
Label:
id: TempLabel
font_size: 150
text: 'Temp Test'
Thanks.
Sorry but you never update something You are just creating another label
Try this:
class LabelWidget(BoxLayout):
def __init__(self, **kwargs):
super(LabelWidget, self).__init__(**kwargs)
Clock.schedule_interval(self.getTheTemp, 1)
def getTheTemp(self, dt):
thetemp = labeltempmod.readtemp()
self.ids.TempLabel.text = thetemp
print thetemp
class labeltestApp(App):
def build(self):
return LabelWidget()
if __name__ == "__main__":
labeltestApp().run()
Update : for your last request, I think the best way to do that is:
...
class LabelWidget(BoxLayout):
def __init__(self, **kwargs):
super(LabelWidget, self).__init__(**kwargs)
self.Thetemp = None
Clock.schedule_interval(self.getTheTemp, 1)
def getTheTemp(self, dt):
if self.Thetemp is None:
self.thetemp = labeltempmod.readtemp()
else:
self.thetemp = labeltempmod.readtemp(self.theTemp)
self.ids.TempLabel.text = str(self.thetemp)
I'm having issues with parsing a data structure to a widget in Kivy, which would then access the structure and be able to show a value on the screen be updated continuously via a clock interval (not sure of a better to do this yet).
I have highlighted the issues in the (non-working) code below:
main.py
from kivy.app import App
from test import TestWidget
class TestApp(App):
def build(self):
testStructTable = {'randomVal1': 1, 'testVal': 2, 'randomVal2': 3}
# Issue here parsing the table like this?
return TestWidget(testStructTable)
if __name__ == '__main__':
TestApp().run()
test.py
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.relativelayout import RelativeLayout
from kivy.properties import NumericProperty
class TestWidget(RelativeLayout):
def __init__(self, testStructTable, **kwargs):
super(TestWidget, self).__init__(**kwargs)
Builder.load_file('test.kv')
sm = ScreenManager()
sm.add_widget(MainScreen(name='MainScreen'))
self.add_widget(sm)
# Error accessing the table
print self.testStructTable
# Have the update_test_val continuously called
#Clock.schedule_interval(MainScreen.update_test_val(testStructTable), 1 / 60)
class MainScreen(Screen):
def __init__(self, **kwargs):
testVal = NumericProperty(0)
def update_test_val(self, testStructTable):
# Get testVal from testStructTable
# Something like:
# self.testVal = testStructTable.testVal + 1 ?
self.testVal = self.testVal + 1
test.kv
<MainScreen>:
FloatLayout:
Label:
text: str(root.testVal)
font_size: 80
My aim is to have the testVal constantly updating on the screen by accessing that data structure, however I am currently unable to achieve this, can you please advise?
In your __init__ method you're passing testStructTable and then you're trying to access self.testStructTable which does not exist untill you explicitly make an assignment:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.relativelayout import RelativeLayout
from kivy.properties import NumericProperty
class TestWidget(RelativeLayout):
def __init__(self, testStructTable, **kwargs):
super(TestWidget, self).__init__(**kwargs)
print(testStructTable)
self.testStructTable = testStructTable
print(self.testStructTable)
class TestApp(App):
def build(self):
testStructTable = {'randomVal1': 1, 'testVal': 2, 'randomVal2': 3}
# Issue here parsing the table like this?
return TestWidget(testStructTable)
if __name__ == '__main__':
TestApp().run()