I am a beginner to Kivy (though not to Python), and I am struggling to get the ids from a kv string into my main code. I have the following, but the 'print' statement tells me that there are no IDs. The application itself runs with no errors.
import kivy
kivy.require('2.1.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
kvString = """
MainScreen:
id: maincontainer
cols: 1
thumbnails: thumbnails.__self__
GridLayout:
id: thumbnails
cols: 3
rows: 3
Image:
source: "test.png"
"""
class MainScreen(GridLayout):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
# This prints 0
print("Ids: {}".format(len(self.ids.items())))
class ExampleApp(App):
def build(self):
root = Builder.load_string(kvString)
return root
if __name__ == "__main__":
ExampleApp().run()
When I ran your code, I got a critical warning that there are no screens, and therefore the app will terminate. As soon as I switched MainScreen to a screen, it worked out perfectly. Here is the code:
.py
import kivy
kivy.require('2.1.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.image import Image
sm = ScreenManager()
#I removed some unnecessary code such as the cols, thumbnail, etc.
kvString = """
<MainScreen>
GridLayout:
id: thumbnails
cols: 3
rows: 3
Image:
source: "test.png"
"""
#NEEDS TO INHERIT SCREEN
class MainScreen(Screen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
# This now prints 1
print(f'There are {len(self.ids.items())} id(s)')
class ExampleApp(App):
def build(self):
root = Builder.load_string(kvString)
#Adds the screen
sm.add_widget(MainScreen(name='screen'))
return sm
if __name__ == "__main__":
ExampleApp().run()
Related
Expected output: ['name', 'age']
Actual output: ['name']
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
KV = '''
BoxLayout:
Label:
id: name
text: "Bob"
'''
class MyApp(App):
def build(self):
box = Builder.load_string(KV)
box.add_widget(Label(text="24", id="age"))
print(list(box.ids.keys()))
return box
if __name__ == "__main__":
MyApp().run()
What can I do to get id of widget that I add with add_widget ?
The ids dictionary is created when a kv file (or string) is parsed. You cannot add an id to the ids dictionary by adding an id to a widget. However, you can manually add an id to the ids. See this related question.
Let's add this to the official kivy Github !
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
KV = '''
ObjBoxLayout:
Label:
id: name
text: "Bob"
'''
class MyApp(App):
def build(self):
box = Builder.load_string(KV)
box.add_widget(Label(text="24"), key="age")
print(list(box.ids.keys()))
return box
class ObjBoxLayout(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def add_widget(self, widget, index=0, canvas=None, key=None):
if key: self.ids[key] = widget
super(ObjBoxLayout, self).add_widget(widget, index, canvas)
if __name__ == "__main__":
MyApp().run()
Im trying to display a mapview on my naviwindow. However, i dont really know how to implement the class MapviewApp to the naviwindow. Any help would be appreciated since im new to kivy !!
.py file
from kivy.garden.mapview import MapView, MapMarker
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
KV = """
WindowManager:
NaviWindow:
<NaviWindow>:
Label:
text:"hi"
"""
class MapViewApp(App):
def build(self):
map = MapView(zoom=18, lat=1.3099, lon=103.7775, double_tap_zoom = True)
marker_1 = MapMarker(lat=1.3099, lon=103.7775)
map.add_marker(marker_1)
return map
class WindowManager(ScreenManager):
pass
class NaviWindow(Screen):
pass
class MyMainApp(App):
def build(self):
return Builder.load_string(KV)
if __name__ == "__main__":
MyMainApp().run()
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()
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()
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()