Woody Pride
Woody Pride

Reputation: 13965

Creating attributes to widgets in separate .kv files

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

Answers (2)

Woody Pride
Woody Pride

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

bj0
bj0

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

Related Questions