Simple Weather Forecast App
Abstract
Section titled “Abstract”Consuming a real web API is a rite of passage, and weather is the perfect first API: free, well-documented, and instantly gratifying. In this tutorial you build a Tkinter app that queries the OpenWeatherMap API for a city’s current conditions and displays temperature, “feels like”, humidity, and a description. Then you grow it into something polished — a 5-day forecast, weather icons, °C/°F toggle, geolocation, response caching, and the kind of error handling that separates a demo from a tool.
You will leave understanding:
- How to register for and use an API key safely.
- The request → JSON → parse → display pipeline behind every API client.
- How to read nested JSON (
data["weather"][0]["description"]) without crashing. - Why you check
status_codeand handle the unhappy paths first.
Prerequisites
Section titled “Prerequisites”- Python 3.6 or above.
- A text editor or IDE.
- The
requestslibrary:pip install requests. - A free OpenWeatherMap API key from openweathermap.org/api.
- Tkinter (bundled with Python).
Getting Started
Section titled “Getting Started”Get an API key
Section titled “Get an API key”- Sign up at openweathermap.org (free tier is plenty).
- Copy your API key from the dashboard.
- New keys can take a few minutes to activate — don’t panic if the first call 401s.
Create the project
Section titled “Create the project”- Create a folder named
weather-app. - Inside it, create
simple_weather_forecast_app.py. - Install the dependency:
pip install requests.
Write the code
Section titled “Write the code”simple_weather_forecast_app.py
pch.viewSource"""
Simple Weather Forecast App
A Python application that fetches and displays weather forecast data. Features include:
- Fetching weather data from an API.
- Displaying the forecast in a user-friendly format.
"""
import requests
from tkinter import Tk, Label, Entry, Button, messagebox
API_KEY = "your_openweathermap_api_key"
BASE_URL = "http://api.openweathermap.org/data/2.5/weather"
class WeatherForecastApp:
def __init__(self, root):
self.root = root
self.root.title("Simple Weather Forecast App")
Label(root, text="Enter City Name:").grid(row=0, column=0, padx=10, pady=10)
self.city_entry = Entry(root, width=30)
self.city_entry.grid(row=0, column=1, padx=10, pady=10)
Button(root, text="Get Weather", command=self.get_weather).grid(row=1, column=0, columnspan=2, pady=10)
self.result_label = Label(root, text="", wraplength=400, justify="left")
self.result_label.grid(row=2, column=0, columnspan=2, padx=10, pady=10)
def get_weather(self):
"""Fetch weather data for the entered city."""
city = self.city_entry.get()
if not city:
messagebox.showerror("Error", "Please enter a city name.")
return
params = {"q": city, "appid": API_KEY, "units": "metric"}
try:
response = requests.get(BASE_URL, params=params)
data = response.json()
if response.status_code == 200:
weather = data["weather"][0]["description"].capitalize()
temp = data["main"]["temp"]
feels_like = data["main"]["feels_like"]
humidity = data["main"]["humidity"]
result = (
f"Weather in {city}:\n"
f"Condition: {weather}\n"
f"Temperature: {temp}°C\n"
f"Feels Like: {feels_like}°C\n"
f"Humidity: {humidity}%"
)
self.result_label.config(text=result)
else:
messagebox.showerror("Error", data.get("message", "Failed to fetch weather data."))
except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}")
def main():
root = Tk()
app = WeatherForecastApp(root)
root.mainloop()
if __name__ == "__main__":
main() Run it
Section titled “Run it”C:\Users\Your Name\weather-app> python simple_weather_forecast_app.py
# Enter a city name, click "Get Weather".
# Make sure you replaced API_KEY with your real key first.How it fits together
Section titled “How it fits together”Read from the top: this is what runs when you execute the file, and which function calls which. It is generated from the code, so it cannot drift from it.
flowchart TD RUN(["python simple_weather_forecast_app.py"]) WeatherForecastApp["WeatherForecastApp
class"] main("main") RUN --> main main --> WeatherForecastApp
Step-by-Step Explanation
Section titled “Step-by-Step Explanation”1. The endpoint and key
Section titled “1. The endpoint and key”API_KEY = "your_openweathermap_api_key"
BASE_URL = "http://api.openweathermap.org/data/2.5/weather"BASE_URL is the “current weather” endpoint. The key authenticates you and counts against your rate limit.
2. Building the request
Section titled “2. Building the request”params = {"q": city, "appid": API_KEY, "units": "metric"}
response = requests.get(BASE_URL, params=params)
data = response.json()Passing a params dict lets requests build the query string for you (?q=London&appid=...&units=metric) and handles URL-encoding spaces and special characters. units="metric" gives Celsius; "imperial" gives Fahrenheit.
3. Check status before parsing
Section titled “3. Check status before parsing”if response.status_code == 200:
weather = data["weather"][0]["description"].capitalize()
temp = data["main"]["temp"]
...
else:
messagebox.showerror("Error", data.get("message", "Failed to fetch weather data."))This is the most important habit in API work: handle the failure path first. A typo’d city returns 404 with a helpful message field — surface it instead of crashing on a missing key.
4. Reading nested JSON
Section titled “4. Reading nested JSON”data["weather"][0]["description"] # list → first item → field
data["main"]["temp"] # nested dictOpenWeatherMap nests weather inside a list (a place can have several conditions) and groups numbers under main. Knowing the shape of the JSON is half the battle — print data once and study it.
Protect Your API Key
Section titled “Protect Your API Key”Never hard-code secrets. Use an environment variable:
import os
API_KEY = os.environ["OWM_API_KEY"] # set OWM_API_KEY in your shell, not in code# PowerShell
$env:OWM_API_KEY = "your_key_here"This keeps the key out of screenshots, git history, and shared code.
Add a 5-Day Forecast
Section titled “Add a 5-Day Forecast”The forecast endpoint returns data in 3-hour steps:
FORECAST_URL = "http://api.openweathermap.org/data/2.5/forecast"
def five_day(city):
r = requests.get(FORECAST_URL,
params={"q": city, "appid": API_KEY, "units": "metric"},
timeout=10)
r.raise_for_status()
# Pick the midday (12:00) reading for each day
return [item for item in r.json()["list"] if "12:00:00" in item["dt_txt"]]Loop the result into five labels or a small table.
Show Weather Icons
Section titled “Show Weather Icons”Each condition carries an icon code:
from tkinter import PhotoImage
import requests, io
icon_code = data["weather"][0]["icon"] # e.g. "10d"
url = f"http://openweathermap.org/img/wn/{icon_code}@2x.png"
img_bytes = requests.get(url, timeout=10).content
# Save then load, or use Pillow's ImageTk for in-memory displayAdd a °C / °F Toggle
Section titled “Add a °C / °F Toggle”Store the raw value once, convert for display:
def to_fahrenheit(celsius):
return celsius * 9 / 5 + 32
label.config(text=f"{temp}°C" if self.metric else f"{to_fahrenheit(temp):.1f}°F")Common Mistakes
Section titled “Common Mistakes”| Problem | Cause | Fix |
|---|---|---|
401 Unauthorized | Key missing, wrong, or not yet active | Check the key; wait a few minutes after signup |
KeyError: 'main' | Parsed JSON before checking status | Guard with if status_code == 200 first |
| App freezes on slow network | No timeout | Add timeout=10; fetch on a background thread |
| Spaces in city name break URL | Manual string concatenation | Use the params= dict — it encodes for you |
| Wrong temperature scale | Forgot units | Set units="metric" or "imperial" |
| Rate-limit errors | Polling too often | Cache responses for a few minutes |
Variations to Try
Section titled “Variations to Try”- Geolocation — detect the user’s city via their IP and load it on launch.
- Hourly chart — plot the next 24 hours with
matplotlib. - Favorites — save a list of cities and refresh them all at once.
- Severe-weather alerts — use the One Call API’s
alertsfield. - Background by condition — change the window color for sunny/rainy/snow.
- Caching layer — store responses with a timestamp; skip the API within 10 minutes.
- Voice output — read the forecast aloud (see Weather App with Voice Commands).
Real-World Applications
Section titled “Real-World Applications”- Travel apps — destination weather at a glance.
- Agriculture & logistics — planning around conditions.
- Dashboards — a weather widget on a home-automation panel.
- Event planning — outdoor-event go/no-go decisions.
Educational Value
Section titled “Educational Value”- REST APIs — keys, query parameters, JSON responses.
- Defensive parsing — status checks and
.get()with defaults. - Secret management — environment variables over hard-coding.
- Caching & rate limits — being a good API citizen.
Next Steps
Section titled “Next Steps”- Move the key into an environment variable.
- Add a 5-day forecast and weather icons.
- Implement a °C/°F toggle and response caching.
- Detect the user’s city with geolocation.
Conclusion
Section titled “Conclusion”You built a weather app that turns a city name into live conditions, then layered on a forecast, icons, unit toggles, and caching — all on top of the same request → parse → display loop you’ll reuse for every API you ever touch. The skill transfers directly: swap the endpoint and you have a stock ticker, a currency converter, or a news reader. Full source on GitHub. Explore more API projects on Python Central Hub.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading