Reputation: 13965
I am learning to write Kivy applications. I created a toy app comprised of two grid layouts. One contains two buttons, and the other contains two labels. The task was to modify the labels when the buttons were clicked. I was able to do this pretty simply when all of the layouts were in the same .kv file by simply creating id's and attributes in the standard manner.
What I am unable to do is to do the same thing when I split the layouts into separate .kv files. I want to do this as the project I am actually working on is too complex to be contained in a single .kv file. So here is my code:
The main python code:
# filename DynamicApp.py
import kivy
kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.lang import Builder
Builder.load_file("myfirstgrid.kv")
Builder.load_file("mysecondgrid.kv")
class DynamicWidgets(RelativeLayout):
pass
class DynamicApp(App):
def build(self):
return DynamicWidgets()
if __name__ == "__main__":
DynamicApp().run()
The base kivy file:
# File name: DynamicApp.kv
#:kivy 1.9.0
<DynamicWidgets>:
MyFirstGrid:
MySecondGrid:
The kivy for the first grid
# File name: myfirstgrid.kv
#:kivy 1.9.0
<MyFirstGrid@GridLayout>
id: _my_first_grid
rows: 1
cols: 2
Label:
id: _label_1
text: "Hello World"
Label:
id: _label_2
text: "Hello World"
The Kivy for the second grid
# File name: myseoncdgrid.kv
#:kivy 1.9.0
#:import mybutton mybutton
<MySecondGrid@GridLayout>
size_hint: 0.25, 0.25
pos_hint: {'center_x': 0.5, 'y' : 0}
rows: 1
cols: 2
MyButton:
text: 'Do it'
label_1: _label_1
MyButton:
text: 'Do it Again'
label_2: _label_2
mybutton.py controls the button actions and is not shown here because I cannot create attributes and references in the MyButton widgets as it gives me the error NameError: name '_label_2' is not defined
.
I did exactly the same strategy but with all the kv in a single file and it woks fine.
How do I create references and attributes to widgets in other .kv files.
I feel like I am missing something fundamental.
Thanks
Upvotes: 3
Views: 1531
Reputation: 13965
So I think I have been able to answer my own question. It seems that you can in fact reference ids in different KV files as my intuition told me. You just have to make sure you manage the references properly:
# File name: DynamicApp.kv
#:kivy 1.9.0
<DynamicWidgets>:
MyFirstGrid:
id: _my_first_grid
my_second_grid: _my_second_grid
MySecondGrid:
id: _my_second_grid
my_first_grid: _my_first_grid
# File name: myfirstgrid.kv
#:kivy 1.9.0
<MyFirstGrid@GridLayout>
id: _my_first_grid
label_1: _label_1
label_2: _label_2
rows: 1
cols: 2
Label:
id: _label_1
text: "Hello World"
Label:
id: _label_2
text: "Hello World"
# File name: myseoncdgrid.kv
#:kivy 1.9.0
#:import mybutton mybutton
<MySecondGrid@GridLayout>
size_hint: 0.25, 0.25
pos_hint: {'center_x': 0.5, 'y' : 0}
rows: 1
cols: 2
MyButton:
text: 'Do it'
on_press: self.onClick1(*args)
MyButton:
text: 'Do it Again'
on_press: self.onClick2(*args)
#file button.py
from kivy.uix.button import Button
class MyButton(Button):
def onClick1(self, instance):
print 'OK'
mfg = self.parent.my_first_grid
mfg.label_1.text = "Hello Universe"
return True
def onClick2(self, instance):
mfg = self.parent.my_first_grid
mfg.label_2.text = "Hello Galaxy"
return True
Upvotes: 0
Reputation: 8223
According to the docs on the Kv Language, "An id is limited in scope to the rule it is declared in". This means that the label ids are not valid outside of <My*Grid@GridLayout>
. This makes sense because a rule can be applied to a widget in any UI tree and it can't know ahead of time what exists or doesn't exist outside itself.
The solution to your problem is to use Properties. You can bind your label text to a StringProperty
and then when you change it in the code (on a button press), Kivy will update the label for you. If you can't easily reference widgets from each other, you can put the properties on the App
object, which can always be referenced in kv as app
.
ex (untested):
class DynamicApp(App):
label1 = StringProperty("Hello World")
label2 = StringProperty("Hello World")
def build(self):
return DynamicWidgets()
...
<MyFirstGrid@GridLayout>
rows: 1
cols: 2
Label:
text: app.label1
Label:
text: app.label2
...
<MySecondGrid@GridLayout>
size_hint: 0.25, 0.25
pos_hint: {'center_x': 0.5, 'y' : 0}
rows: 1
cols: 2
MyButton:
text: 'Do it'
on_press: app.label1 = "did it"
MyButton:
text: 'Do it Again'
on_press: app.label2 = "did it again"
Upvotes: 1