Reputation: 313
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:
search for a city by name or coordinates
it lists all the cities that match your search
you can choose a city
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
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