Martin Spasov
Martin Spasov

Reputation: 313

Kivy doesn't display widget

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty, DictProperty
from kivy.network.urlrequest import UrlRequest
import json
from kivy.uix.listview import ListItemButton
from kivy.uix.label import Label
from kivy.factory import Factory

class LocationButton(ListItemButton):
    pass

class WeatherRoot(BoxLayout):

    def show_current_weather(self,location):
        self.clear_widgets()
        current_weather = Factory.CurrentWeather()
        current_weather.location = location
        self.add_widget(current_weather)

    def show_add_location_form(self):
        self.clear_widgets()
        self.add_widget(AddLocationForm())

class AddLocationForm(BoxLayout):

    search_input = ObjectProperty()
    search_results = ObjectProperty()

    def search_location_name(self):
        search_template = 'http://api.openweathermap.org/data/2.5/' + 'find?q={}&type=like'
        search_url = search_template.format(self.search_input.text)
        request = UrlRequest(search_url, self.found_location_name)
        if len(self.search_results.item_strings) == 0:
            self.search_results.item_strings.append('City not found')

    def search_location_coor(self):
        try:
            search_coor = self.search_input.text
            search_input_coordinates = search_coor.split(',')
            search_template_coordinates = 'http://api.openweathermap.org/data/2.5/' + "weather?lat=%s&lon=%s&radius=10" % (search_input_coordinates[0] , search_input_coordinates[1])
            request_coordinates = UrlRequest(search_template_coordinates, self.found_location_coordinates)
        except (IndexError):
            self.search_results.item_strings.append('Coordinates needed are Lattitude and Longtitude!')


    def found_location_coordinates(self,request_coordinates,data_coordinates):
        try:
            data_coordinates = json.loads(data_coordinates.decode()) if not isinstance(data_coordinates, dict) else data_coordinates
            cities_coordinates = ['{} ({})'.format(data_coordinates['name'],data_coordinates['sys']['country'])]
            self.search_results.item_strings = cities_coordinates
            self.search_results.adapter.data[:]
            self.search_results.adapter.data.extend(cities_coordinates)
            self.search_results._trigger_reset_populate()
        except KeyError:
            self.search_results.item_strings.append('Wrong Coordinates')

    def found_location_name(self, request, data):
        data = json.loads(data.decode()) if not isinstance(data, dict) else data
        cities = ['{} ({})'.format(d['name'],d['sys']['country']) for d in data['list']]
        self.search_results.item_strings = cities
        self.search_results.adapter.data[:]
        self.search_results.adapter.data.extend(cities)
        self.search_results._trigger_reset_populate()
        if len(self.search_results.item_strings) == 0:
            self.search_results.item_strings.append('City not found')


class WeatherApp(App):
    pass


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

and the kv file:

#:import main main
#:import ListAdapter kivy.adapters.listadapter.ListAdapter
WeatherRoot:

<WeatherRoot>:
    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: .5
            Button:
                text: 'Search'
                size_hint_x: .25
                on_press: self.parent.parent.search_location_name()
            Button:
                text: 'Current Location'
                size_hint_x: .25
                on_press: self.parent.parent.search_location_coor()
        ListView:
            id: search_results_list
            item_strings: []
            adapter:
                 ListAdapter(data=[], cls=main.LocationButton)
<LocationButton>:
    on_press: app.root.show_current_weather(self.text)
<CurrentWeather@BoxLayout>:
    location: ''
    conditions: None
    temp: None
    temp_min: None
    temp_max: None
    orientation: 'vertical'
    Label:
        text: root.location
    BoxLayout:
        size_hint_y: None
        height: '40dp'
        Button:
            text: 'Add Location'
            on_press: app.root.show_add_location_form()
        Button:
            text: 'Forecast'

What does and doesnt do:

  1. search for a city by name or coordinates

  2. it lists all the cities that match your search

  3. you can choose a city

  4. it opens black page with the city's name and 2 buttons(Add Location and Forecast)

If you click on the add location button it should open the initial window, but instead I get a black screen, Am I missing something?

This was code was taken from the book Creating apps with kivy

Upvotes: 0

Views: 2015

Answers (1)

inclement
inclement

Reputation: 29450

I don't know what the book intends, but the code you've given has the problem that clicking 'Add location' runs the show_add_location_form method.

def show_add_location_form(self):
        self.clear_widgets()
        self.add_widget(AddLocationForm())

The first part of this clears all your widgets, giving you that black screen. The second part adds an AddLocationForm, but if you look at the rest of the code you'll notice that this actually has no defined appearance...therefore the screen stays black.

I'd normally give some suggestions to fix it, but since it's a book quote are you sure this isn't deliberate and the book just hasn't done the next bit yet?

That said, I think the following change will probably work, removing the definition of AddLocationForm from the WeatherRoot rule so that every AddLocationForm instance has the same rule. In this case, it should make your code work by applying the rule to the new AddLocationForm instance. I haven't tried it though.

<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: .5
        Button:
            text: 'Search'
            size_hint_x: .25
            on_press: self.parent.parent.search_location_name()
        Button:
            text: 'Current Location'
            size_hint_x: .25
            on_press: self.parent.parent.search_location_coor()
    ListView:
        id: search_results_list
        item_strings: []
        adapter:
             ListAdapter(data=[], cls=main.LocationButton)

Note that (assuming it works) this is just a quick fix, there are better ways to structure your app if you want to use multiple screens with different content. I assume the book is getting to that.

Upvotes: 2

Related Questions