Jason Acheampong
Jason Acheampong

Reputation: 73

how do you access a widget that is in a different class in kivy

How can I access the conditions value that is located inside my CurrentWeather widget from Python in my AddLocationForm? I want to edit the conditions value from my AddLocationForm instead of doing it in CurrentWeather. This is my code, so far:

Python:

from kivy.network.urlrequest import UrlRequest
import json
from kivy.core.window import Window
from kivy.uix.textinput import TextInput
from kivy.uix.listview import ListItemButton
from kivy.factory import Factory

Window.clearcolor = (0.45, 0.3, 1, 0.5)

class AddLocationForm(BoxLayout):
    search_input = ObjectProperty()
    search_results = ObjectProperty()
    currentWeather = ObjectProperty()

    def search_location(self):
        search_template = "http://api.openweathermap.org/data/2.5/weather?q={}&appid=f8020ca79e2de4c1dd5a125c3f22fa18"
        search_url = search_template.format(self.search_input.text)
        request = UrlRequest(search_url, self.found_location)

    def found_location(self, request, data):
        data = json.loads(data.decode()) if not isinstance(data, dict) else data
        weather = "%s" %(data["weather"][0]["description"])
        root.WeatherRoot.CurrentWeather.conditions = weather #Problem is here

class LocationButton(ListItemButton):
    pass

class WeatherApp(App):
    def build(self):
        global root
        root = self.root

if __name__ == '__main__':
    WeatherApp().run()

Kv:

#: import ListItemButton kivy.uix.listview.ListItemButton
#: import ListAdapter kivy.adapters.listadapter.ListAdapter
#: import main main

WeatherRoot:

<WeatherRoot>:
    AddLocationForm

<AddLocationForm>:
    orientation: "vertical"
    search_input: search_box 
    search_results: search_results_list
    BoxLayout:
        height: "40dp"
        size_hint_y: None
        TextInput:
            id: search_box 
            size_hint_x: 50
            multiline: False
        Button:
            text: "Search"
            size_hint_x: 25
            on_press: root.search_location()
            background_color: (1, 0.5, 0.656, 1)

<CurrentWeather@BoxLayout>:
    id: current_weather
    currentWeather: current_weather
    location: "" 
    conditions: ""
    temp: None
    temp_min: None
    temp_max: None

    orientation: "vertical"
    Label:
        text: root.location
        pos_hint: {'x': 0.435, "y": 1}
        font_size: '40dp'       
        size_hint_y: None
        size_hint_x: None
    BoxLayout:
        orientation: "horizontal"

        Label:
            id: curCon
            text: root.conditions

Upvotes: 1

Views: 2121

Answers (1)

Peter Badida
Peter Badida

Reputation: 12169

Tree:

WeatherRoot:           # root widget

<WeatherRoot>:         # root widget class
    AddLocationForm    # root widget's child

Therefore you'll first access your App's instance with app. The instance after the build method has a root property, which is just an instance of the root widget stored. That's why global root is useless in larger code because of e.g. collisions, confusion with some other name, etc while accessible easier in a pretty way.

To access a class dynamically created in kv from python or kv you need to use the widget tree (to get to an already existing instance):

app.root.children[0]...

or Factory to create an instance of the widget e.g. if you'd like to create a new one after some condition is evaluated.

But!

You've misunderstood how the dynamic classes work in kv - the <Name@Widget>: is just a declaration, not an instance. Like a template. To actually work with it you have to put it somewhere, for example:

python:

cw = Factory.CurrentWeather()
somewhere.add_widget(cw)

kv:

WeatherRoot:
    CurrentWeather:  # here

<WeatherRoot>:
    AddLocationForm:
        CurrentWeather:  # or here

If you choose to add it in python, you can easily use some variable to store an instance and still be able to access it later through widget tree. On the other hand, when added in kv, you don't have an access to __init__ and you can't really store an instance into a variable without sillyish hacks like this:

#:import Factory kivy.factory.Factory

<Blob@Button>:

BoxLayout:
    Button:
        on_release: self.new = Factory.Blob(); self.parent.add_widget(self.new)

Therefore to access it you have to first create an instance of it and either store the instance, or go trough the widget tree.

Upvotes: 1

Related Questions