Weather App GUI with API Integration
Abstract
Section titled “Abstract”Create a comprehensive desktop weather application that integrates with the OpenWeatherMap API to provide real-time weather data, 5-day forecasts, and weather history. This project demonstrates advanced GUI development, API integration, threading for responsive UI, and data persistence with JSON file handling.
Prerequisites
Section titled “Prerequisites”- Python 3.7 or above
- Text Editor or IDE
- Solid understanding of Python syntax and OOP concepts
- Knowledge of Tkinter for GUI development
- Familiarity with API requests and JSON handling
- Understanding of threading and asynchronous programming
- Basic knowledge of weather data and meteorology concepts
flowchart TD
n0(["script start"])
n2["main()"]
subgraph WeatherApp
n400["__init__()"]
n401["_fetch_weather_data()"]
n402["_show_error()"]
n403["_update_weather_display()"]
n404["create_weather_labels()"]
n405["get_mock_current_weather()"]
n406["get_mock_forecast_data()"]
n407["get_weather()"]
n408["refresh_weather()"]
n409["setup_ui()"]
n410["update_forecast_display()"]
end
n400 --> n409
n401 --> n405
n401 --> n406
n403 --> n402
n403 --> n410
n408 --> n407
n409 --> n404
n409 --> n407
n0 --> n2
Getting Started
Section titled “Getting Started”Create a new project
Section titled “Create a new project”- Create a new project folder and name it
weatherAppGUI. - Create a new file and name it
weatherappgui.py. - Sign up for a free API key at OpenWeatherMap.
- Install required dependencies:
pip install requests - Open the project folder in your favorite text editor or IDE.
- Copy the code below and paste it into your
weatherappgui.pyfile.
Write the code
Section titled “Write the code”- Add the following code to your
weatherappgui.pyfile.
Weather App GUI with API Integration
pch.viewSource# Weather App with GUI (Tkinter)
import tkinter as tk
from tkinter import ttk, messagebox
import requests
import json
from datetime import datetime, timedelta
from typing import Dict, Optional, List
import threading
class WeatherApp:
def __init__(self, root):
self.root = root
self.root.title("Weather App")
self.root.geometry("800x600")
self.root.configure(bg='#2c3e50')
# API key (you would get this from OpenWeatherMap)
self.api_key = "your_api_key_here" # Replace with actual API key
self.base_url = "http://api.openweathermap.org/data/2.5"
# Store weather data
self.current_weather = None
self.forecast_data = None
self.setup_ui()
def setup_ui(self):
"""Setup the user interface"""
# Title
title_label = tk.Label(
self.root,
text="Weather App",
font=("Arial", 24, "bold"),
bg='#2c3e50',
fg='#ecf0f1'
)
title_label.pack(pady=20)
# Search frame
search_frame = tk.Frame(self.root, bg='#2c3e50')
search_frame.pack(pady=10)
tk.Label(
search_frame,
text="Enter City:",
font=("Arial", 12),
bg='#2c3e50',
fg='#ecf0f1'
).pack(side=tk.LEFT, padx=5)
self.city_entry = tk.Entry(
search_frame,
font=("Arial", 12),
width=20
)
self.city_entry.pack(side=tk.LEFT, padx=5)
self.city_entry.bind('<Return>', lambda event: self.get_weather())
self.search_button = tk.Button(
search_frame,
text="Get Weather",
command=self.get_weather,
font=("Arial", 10, "bold"),
bg='#3498db',
fg='white',
padx=10
)
self.search_button.pack(side=tk.LEFT, padx=5)
# Current weather frame
self.current_frame = tk.LabelFrame(
self.root,
text="Current Weather",
font=("Arial", 14, "bold"),
bg='#34495e',
fg='#ecf0f1',
padx=20,
pady=15
)
self.current_frame.pack(pady=20, padx=20, fill='x')
# Weather info labels
self.weather_labels = {}
self.create_weather_labels()
# Forecast frame
self.forecast_frame = tk.LabelFrame(
self.root,
text="5-Day Forecast",
font=("Arial", 14, "bold"),
bg='#34495e',
fg='#ecf0f1',
padx=20,
pady=15
)
self.forecast_frame.pack(pady=10, padx=20, fill='both', expand=True)
# Buttons frame
buttons_frame = tk.Frame(self.root, bg='#2c3e50')
buttons_frame.pack(pady=10)
self.refresh_button = tk.Button(
buttons_frame,
text="Refresh",
command=self.refresh_weather,
font=("Arial", 10),
bg='#27ae60',
fg='white',
padx=15
)
self.refresh_button.pack(side=tk.LEFT, padx=5)
self.save_button = tk.Button(
buttons_frame,
text="Save Data",
command=self.save_weather_data,
font=("Arial", 10),
bg='#e67e22',
fg='white',
padx=15
)
self.save_button.pack(side=tk.LEFT, padx=5)
# Status bar
self.status_var = tk.StringVar()
self.status_var.set("Ready")
status_bar = tk.Label(
self.root,
textvariable=self.status_var,
relief=tk.SUNKEN,
anchor=tk.W,
bg='#2c3e50',
fg='#ecf0f1'
)
status_bar.pack(side=tk.BOTTOM, fill=tk.X)
def create_weather_labels(self):
"""Create labels for weather information"""
# City and country
self.weather_labels['city'] = tk.Label(
self.current_frame,
text="City: -",
font=("Arial", 14, "bold"),
bg='#34495e',
fg='#ecf0f1'
)
self.weather_labels['city'].grid(row=0, column=0, columnspan=2, pady=5, sticky='w')
# Temperature
self.weather_labels['temp'] = tk.Label(
self.current_frame,
text="Temperature: -",
font=("Arial", 24, "bold"),
bg='#34495e',
fg='#e74c3c'
)
self.weather_labels['temp'].grid(row=1, column=0, columnspan=2, pady=10)
# Weather description
self.weather_labels['desc'] = tk.Label(
self.current_frame,
text="Description: -",
font=("Arial", 12),
bg='#34495e',
fg='#ecf0f1'
)
self.weather_labels['desc'].grid(row=2, column=0, columnspan=2, pady=5)
# Feels like
self.weather_labels['feels_like'] = tk.Label(
self.current_frame,
text="Feels like: -",
font=("Arial", 11),
bg='#34495e',
fg='#ecf0f1'
)
self.weather_labels['feels_like'].grid(row=3, column=0, pady=5, sticky='w')
# Humidity
self.weather_labels['humidity'] = tk.Label(
self.current_frame,
text="Humidity: -",
font=("Arial", 11),
bg='#34495e',
fg='#ecf0f1'
)
self.weather_labels['humidity'].grid(row=3, column=1, pady=5, sticky='w')
# Pressure
self.weather_labels['pressure'] = tk.Label(
self.current_frame,
text="Pressure: -",
font=("Arial", 11),
bg='#34495e',
fg='#ecf0f1'
)
self.weather_labels['pressure'].grid(row=4, column=0, pady=5, sticky='w')
# Wind speed
self.weather_labels['wind'] = tk.Label(
self.current_frame,
text="Wind: -",
font=("Arial", 11),
bg='#34495e',
fg='#ecf0f1'
)
self.weather_labels['wind'].grid(row=4, column=1, pady=5, sticky='w')
# Visibility
self.weather_labels['visibility'] = tk.Label(
self.current_frame,
text="Visibility: -",
font=("Arial", 11),
bg='#34495e',
fg='#ecf0f1'
)
self.weather_labels['visibility'].grid(row=5, column=0, pady=5, sticky='w')
# UV Index (if available)
self.weather_labels['uv'] = tk.Label(
self.current_frame,
text="UV Index: -",
font=("Arial", 11),
bg='#34495e',
fg='#ecf0f1'
)
self.weather_labels['uv'].grid(row=5, column=1, pady=5, sticky='w')
def get_weather(self):
"""Get weather data for the specified city"""
city = self.city_entry.get().strip()
if not city:
messagebox.showwarning("Warning", "Please enter a city name")
return
# Show loading status
self.status_var.set("Loading weather data...")
self.search_button.config(state='disabled')
# Use threading to prevent UI freezing
thread = threading.Thread(target=self._fetch_weather_data, args=(city,))
thread.daemon = True
thread.start()
def _fetch_weather_data(self, city: str):
"""Fetch weather data in a separate thread"""
try:
# Get current weather
current_url = f"{self.base_url}/weather?q={city}&appid={self.api_key}&units=metric"
# For demo purposes, we'll use mock data
# In real implementation, you would use: response = requests.get(current_url)
mock_current_data = self.get_mock_current_weather(city)
# Get forecast
# forecast_url = f"{self.base_url}/forecast?q={city}&appid={self.api_key}&units=metric"
mock_forecast_data = self.get_mock_forecast_data(city)
# Update UI in main thread
self.root.after(0, self._update_weather_display, mock_current_data, mock_forecast_data)
except Exception as e:
self.root.after(0, self._show_error, f"Error fetching weather data: {str(e)}")
def get_mock_current_weather(self, city: str) -> Dict:
"""Generate mock current weather data for demo"""
import random
temps = [15, 18, 22, 25, 28, 30, 12, 8, 5, 2]
conditions = ["Clear", "Cloudy", "Rainy", "Sunny", "Partly Cloudy", "Overcast"]
return {
"name": city.title(),
"sys": {"country": "XX"},
"main": {
"temp": random.choice(temps),
"feels_like": random.choice(temps) + random.randint(-3, 3),
"humidity": random.randint(30, 80),
"pressure": random.randint(1000, 1030)
},
"weather": [{
"main": random.choice(conditions),
"description": random.choice(conditions).lower()
}],
"wind": {
"speed": random.randint(5, 25)
},
"visibility": random.randint(5000, 10000),
"dt": int(datetime.now().timestamp())
}
def get_mock_forecast_data(self, city: str) -> Dict:
"""Generate mock forecast data for demo"""
import random
forecast_list = []
for i in range(40): # 5 days * 8 times per day (3-hour intervals)
date = datetime.now() + timedelta(hours=i*3)
temp = random.randint(10, 30)
forecast_list.append({
"dt": int(date.timestamp()),
"dt_txt": date.strftime("%Y-%m-%d %H:%M:%S"),
"main": {
"temp": temp,
"temp_min": temp - 2,
"temp_max": temp + 2
},
"weather": [{
"main": random.choice(["Clear", "Cloudy", "Rain"]),
"description": "demo weather"
}]
})
return {"list": forecast_list}
def _update_weather_display(self, current_data: Dict, forecast_data: Dict):
"""Update the weather display with fetched data"""
try:
self.current_weather = current_data
self.forecast_data = forecast_data
# Update current weather
city_country = f"{current_data['name']}, {current_data['sys']['country']}"
self.weather_labels['city'].config(text=f"City: {city_country}")
temp = round(current_data['main']['temp'])
self.weather_labels['temp'].config(text=f"{temp}°C")
desc = current_data['weather'][0]['description'].title()
self.weather_labels['desc'].config(text=f"Description: {desc}")
feels_like = round(current_data['main']['feels_like'])
self.weather_labels['feels_like'].config(text=f"Feels like: {feels_like}°C")
humidity = current_data['main']['humidity']
self.weather_labels['humidity'].config(text=f"Humidity: {humidity}%")
pressure = current_data['main']['pressure']
self.weather_labels['pressure'].config(text=f"Pressure: {pressure} hPa")
wind_speed = current_data['wind']['speed']
self.weather_labels['wind'].config(text=f"Wind: {wind_speed} m/s")
visibility = current_data.get('visibility', 0) / 1000
self.weather_labels['visibility'].config(text=f"Visibility: {visibility:.1f} km")
# Update forecast
self.update_forecast_display(forecast_data)
# Update status
self.status_var.set(f"Last updated: {datetime.now().strftime('%H:%M:%S')}")
except Exception as e:
self._show_error(f"Error updating display: {str(e)}")
finally:
self.search_button.config(state='normal')
def update_forecast_display(self, forecast_data: Dict):
"""Update the forecast display"""
# Clear existing forecast widgets
for widget in self.forecast_frame.winfo_children():
widget.destroy()
# Group forecast by day
daily_forecasts = {}
for item in forecast_data['list'][:40]: # 5 days
date = datetime.fromtimestamp(item['dt']).date()
if date not in daily_forecasts:
daily_forecasts[date] = []
daily_forecasts[date].append(item)
# Display daily forecasts
for i, (date, forecasts) in enumerate(list(daily_forecasts.items())[:5]):
# Calculate daily min/max temperatures
temps = [f['main']['temp'] for f in forecasts]
min_temp = round(min(temps))
max_temp = round(max(temps))
# Get most common weather condition
conditions = [f['weather'][0]['main'] for f in forecasts]
most_common = max(set(conditions), key=conditions.count)
# Create forecast frame
day_frame = tk.Frame(self.forecast_frame, bg='#34495e')
day_frame.grid(row=0, column=i, padx=5, pady=5, sticky='nsew')
# Configure grid weights
self.forecast_frame.grid_columnconfigure(i, weight=1)
# Day label
day_name = date.strftime('%A') if date == datetime.now().date() else date.strftime('%a')
day_label = tk.Label(
day_frame,
text=day_name,
font=("Arial", 10, "bold"),
bg='#34495e',
fg='#ecf0f1'
)
day_label.pack(pady=2)
# Date label
date_label = tk.Label(
day_frame,
text=date.strftime('%m/%d'),
font=("Arial", 9),
bg='#34495e',
fg='#bdc3c7'
)
date_label.pack()
# Weather condition
condition_label = tk.Label(
day_frame,
text=most_common,
font=("Arial", 9),
bg='#34495e',
fg='#ecf0f1'
)
condition_label.pack(pady=2)
# Temperature range
temp_label = tk.Label(
day_frame,
text=f"{max_temp}°/{min_temp}°",
font=("Arial", 10, "bold"),
bg='#34495e',
fg='#e74c3c'
)
temp_label.pack()
def _show_error(self, message: str):
"""Show error message"""
messagebox.showerror("Error", message)
self.status_var.set("Error occurred")
self.search_button.config(state='normal')
def refresh_weather(self):
"""Refresh current weather data"""
city = self.city_entry.get().strip()
if city:
self.get_weather()
else:
messagebox.showwarning("Warning", "Please enter a city name first")
def save_weather_data(self):
"""Save current weather data to file"""
if not self.current_weather:
messagebox.showwarning("Warning", "No weather data to save")
return
try:
filename = f"weather_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
data_to_save = {
"current_weather": self.current_weather,
"forecast": self.forecast_data,
"saved_at": datetime.now().isoformat()
}
with open(filename, 'w') as f:
json.dump(data_to_save, f, indent=2)
messagebox.showinfo("Success", f"Weather data saved to {filename}")
self.status_var.set(f"Data saved to {filename}")
except Exception as e:
messagebox.showerror("Error", f"Failed to save data: {str(e)}")
def main():
"""Main function to run the weather app"""
root = tk.Tk()
app = WeatherApp(root)
# Center the window
root.update_idletasks()
width = root.winfo_width()
height = root.winfo_height()
x = (root.winfo_screenwidth() // 2) - (width // 2)
y = (root.winfo_screenheight() // 2) - (height // 2)
root.geometry(f'{width}x{height}+{x}+{y}')
# Show instructions for API key
messagebox.showinfo(
"API Key Required",
"To use real weather data, please:\n"
"1. Sign up at https://openweathermap.org/api\n"
"2. Get your free API key\n"
"3. Replace 'your_api_key_here' in the code\n\n"
"For now, the app will show demo data."
)
root.mainloop()
if __name__ == "__main__":
main() - Replace
your_api_key_herewith your actual OpenWeatherMap API key. - Save the file.
- Run the following command to run the application.
C:\Users\username\Documents\weatherAppGUI> python weatherappgui.py
Weather App
Enter City: New York
[Click "Get Weather"]
Current Weather in New York, US
Temperature: 22°C
Description: Clear Sky
Humidity: 65%, Wind: 8 m/s
5-Day Forecast displayed with daily temperaturesExplanation
Section titled “Explanation”- The
import tkinter as tkstatement imports the Tkinter library for creating the GUI interface. - The
import requestsimports the requests library for making HTTP API calls to OpenWeatherMap. - The
class WeatherApp:defines the main application class that manages the GUI and weather data. - The
setup_ui()method creates all the GUI components including entry fields, buttons, and display labels. - The
get_weather()method handles user input and initiates the weather data fetching process. - The threading implementation prevents the UI from freezing during API calls.
- The
_fetch_weather_data()method makes API requests to get current weather and forecast data. - The weather display updates show current conditions, temperature, humidity, wind speed, and pressure.
- The forecast display shows a 5-day weather prediction with daily temperature ranges.
- The
save_weather_data()method allows users to save weather information to JSON files. - Error handling manages network issues, invalid city names, and API response errors.
- The status bar provides real-time feedback about the application’s current state.
Next Steps
Section titled “Next Steps”Congratulations! You have successfully created a Weather App GUI in Python. Experiment with the code and see if you can modify the application. Here are a few suggestions:
- Add weather maps and radar imagery
- Implement location-based weather using GPS coordinates
- Create weather alerts and notifications
- Add multiple city comparison features
- Implement weather history charts and graphs
- Add weather widgets for desktop integration
- Create different color themes based on weather conditions
- Add voice announcements for weather updates
- Implement weather-based activity recommendations
Conclusion
Section titled “Conclusion”In this project, you learned how to create a Weather App GUI in Python using Tkinter and API integration. You also learned about threading, data persistence, real-time data fetching, and creating responsive user interfaces. You can find the source code on GitHub
1. WeatherApp Class Structure
Section titled “1. WeatherApp Class Structure”class WeatherApp:
def __init__(self):
self.root = tk.Tk()
self.api_key = "YOUR_API_KEY_HERE"
self.base_url = "http://api.openweathermap.org/data/2.5/"
self.weather_data = {}
self.favorites = []The main class manages:
- GUI Components: All Tkinter widgets and layouts
- API Communication: HTTP requests to OpenWeatherMap
- Data Management: Weather data storage and retrieval
- User Preferences: Favorite cities and settings
2. API Integration
Section titled “2. API Integration”def get_weather_data(self, city, units="metric"):
"""Fetch current weather data from API"""
try:
url = f"{self.base_url}weather"
params = {
"q": city,
"appid": self.api_key,
"units": units
}
response = requests.get(url, params=params, timeout=10)
return response.json()
except requests.RequestException as e:
self.show_error(f"API Error: {e}")
return None3. Threading for Responsive UI
Section titled “3. Threading for Responsive UI”def fetch_weather_threaded(self, city):
"""Fetch weather data in separate thread"""
def worker():
self.show_loading(True)
data = self.get_weather_data(city)
self.root.after(0, lambda: self.display_weather(data))
self.show_loading(False)
thread = threading.Thread(target=worker, daemon=True)
thread.start()4. Data Persistence
Section titled “4. Data Persistence”def save_weather_data(self, city, data):
"""Save weather data locally"""
filename = f"weather_data_{city.lower()}.json"
try:
with open(filename, 'w') as f:
json.dump(data, f, indent=2)
except IOError as e:
self.show_error(f"Save Error: {e}")Usage Examples
Section titled “Usage Examples”Basic Usage
Section titled “Basic Usage”# Run the application
app = WeatherApp()
app.run()API Configuration
Section titled “API Configuration”# Set your API key
app = WeatherApp()
app.set_api_key("your_openweathermap_api_key")
app.run()Custom Settings
Section titled “Custom Settings”# Configure default settings
app = WeatherApp()
app.set_default_units("imperial") # Fahrenheit, mph
app.set_default_city("New York")
app.run()Running the Application
Section titled “Running the Application”Command Line
Section titled “Command Line”python weatherappgui.pyExpected Output
Section titled “Expected Output”Weather App Starting...
API Key: Configured ✓
GUI: Initialized ✓
Ready for use!User Interface Guide
Section titled “User Interface Guide”Main Window Components
Section titled “Main Window Components”1. Search Section
Section titled “1. Search Section”- City Input: Text field for entering city names
- Search Button: Fetch weather for entered city
- Units Toggle: Switch between Celsius/Fahrenheit
2. Current Weather Display
Section titled “2. Current Weather Display”Current Weather in New York
Temperature: 22°C (Feels like 25°C)
Condition: Partly Cloudy
Humidity: 65%
Wind: 15 km/h NE
Pressure: 1013 hPa
Visibility: 10 km
UV Index: 6 (High)3. Forecast Section
Section titled “3. Forecast Section”- 5-Day Forecast: Daily weather predictions
- Hourly Forecast: 24-hour detailed forecast
- Weather Charts: Temperature and precipitation graphs
4. Additional Features
Section titled “4. Additional Features”- Favorites List: Quick access to saved cities
- Weather History: Previously fetched data
- Settings Panel: Customize app preferences
Advanced Features
Section titled “Advanced Features”1. Weather Alerts
Section titled “1. Weather Alerts”def check_weather_alerts(self, weather_data):
"""Check for severe weather conditions"""
alerts = []
# Temperature alerts
temp = weather_data['main']['temp']
if temp > 35: # Celsius
alerts.append("⚠️ Extreme Heat Warning")
elif temp < -10:
alerts.append("❄️ Extreme Cold Warning")
# Wind alerts
wind_speed = weather_data['wind']['speed']
if wind_speed > 20: # m/s
alerts.append("💨 High Wind Warning")
return alerts2. Data Visualization
Section titled “2. Data Visualization”def create_temperature_chart(self, forecast_data):
"""Create temperature trend chart"""
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
# Extract temperature data
temps = [item['main']['temp'] for item in forecast_data]
times = [item['dt_txt'] for item in forecast_data]
# Create chart
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(times, temps, marker='o')
ax.set_title('Temperature Forecast')
ax.set_ylabel('Temperature (°C)')
# Embed in Tkinter
canvas = FigureCanvasTkAgg(fig, self.chart_frame)
canvas.draw()
canvas.get_tk_widget().pack()3. Favorites Management
Section titled “3. Favorites Management”def add_to_favorites(self, city):
"""Add city to favorites list"""
if city not in self.favorites:
self.favorites.append(city)
self.save_favorites()
self.update_favorites_list()
def save_favorites(self):
"""Persist favorites to file"""
with open('favorites.json', 'w') as f:
json.dump(self.favorites, f)Configuration Options
Section titled “Configuration Options”Settings File (config.json)
Section titled “Settings File (config.json)”{
"api_key": "your_api_key_here",
"default_city": "London",
"default_units": "metric",
"update_interval": 300,
"theme": "light",
"show_forecast": true,
"auto_refresh": true
}Customization Examples
Section titled “Customization Examples”# Theme customization
def apply_dark_theme(self):
"""Apply dark theme to UI"""
self.root.configure(bg='#2b2b2b')
self.main_frame.configure(bg='#2b2b2b')
# Update all widget colors
# Unit conversion
def convert_temperature(self, temp, from_unit, to_unit):
"""Convert temperature between units"""
if from_unit == "celsius" and to_unit == "fahrenheit":
return (temp * 9/5) + 32
elif from_unit == "fahrenheit" and to_unit == "celsius":
return (temp - 32) * 5/9
return tempSample Weather Data
Section titled “Sample Weather Data”Current Weather Response
Section titled “Current Weather Response”{
"weather": [
{
"id": 801,
"main": "Clouds",
"description": "few clouds",
"icon": "02d"
}
],
"main": {
"temp": 22.5,
"feels_like": 24.8,
"temp_min": 20.1,
"temp_max": 25.3,
"pressure": 1013,
"humidity": 65
},
"wind": {
"speed": 4.2,
"deg": 180
},
"dt": 1693737600,
"name": "London"
}Forecast Response Structure
Section titled “Forecast Response Structure”{
"list": [
{
"dt": 1693737600,
"main": {
"temp": 22.5,
"humidity": 65
},
"weather": [
{
"main": "Clouds",
"description": "few clouds"
}
],
"dt_txt": "2025-09-02 12:00:00"
}
]
}Error Handling
Section titled “Error Handling”Common Issues and Solutions
Section titled “Common Issues and Solutions”1. API Key Issues
Section titled “1. API Key Issues”def validate_api_key(self):
"""Validate API key before making requests"""
test_url = f"{self.base_url}weather?q=London&appid={self.api_key}"
try:
response = requests.get(test_url, timeout=5)
if response.status_code == 401:
raise ValueError("Invalid API key")
return True
except requests.RequestException:
return False2. Network Connectivity
Section titled “2. Network Connectivity”def check_internet_connection(self):
"""Check if internet connection is available"""
try:
requests.get('https://www.google.com', timeout=3)
return True
except requests.RequestException:
return False3. Invalid City Names
Section titled “3. Invalid City Names”def validate_city_name(self, city):
"""Validate city name format"""
if not city or len(city.strip()) < 2:
raise ValueError("City name too short")
# Check for valid characters
import re
if not re.match(r'^[a-zA-Z\s\-\.]+$', city):
raise ValueError("Invalid characters in city name")
return city.strip().title()Data Export Features
Section titled “Data Export Features”Export to CSV
Section titled “Export to CSV”def export_weather_data(self, filename="weather_export.csv"):
"""Export weather data to CSV"""
import csv
with open(filename, 'w', newline='') as csvfile:
fieldnames = ['city', 'date', 'temperature', 'humidity', 'description']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for record in self.weather_history:
writer.writerow(record)Weather Analytics
Section titled “Weather Analytics”def analyze_weather_trends(self, city):
"""Analyze weather trends for a city"""
data = self.load_weather_history(city)
if not data:
return None
temps = [record['temperature'] for record in data]
analysis = {
'average_temp': sum(temps) / len(temps),
'max_temp': max(temps),
'min_temp': min(temps),
'temp_range': max(temps) - min(temps)
}
return analysisTroubleshooting
Section titled “Troubleshooting”Common Problems
Section titled “Common Problems”1. API Rate Limiting
# Solution: Implement rate limiting
import time
def make_api_request_with_rate_limit(self, url, params):
"""Make API request with rate limiting"""
if hasattr(self, 'last_request_time'):
time_since_last = time.time() - self.last_request_time
if time_since_last < 1: # 1 second minimum between requests
time.sleep(1 - time_since_last)
self.last_request_time = time.time()
return requests.get(url, params=params)2. GUI Freezing
# Solution: Use threading for all API calls
def update_weather_async(self, city):
"""Update weather data asynchronously"""
def fetch_and_update():
data = self.get_weather_data(city)
# Use root.after to update GUI from main thread
self.root.after(0, lambda: self.update_display(data))
threading.Thread(target=fetch_and_update, daemon=True).start()3. Memory Usage
# Solution: Limit data storage
def manage_data_storage(self):
"""Manage memory usage by limiting stored data"""
max_records = 1000
if len(self.weather_history) > max_records:
# Keep only recent records
self.weather_history = self.weather_history[-max_records:]Extensions and Improvements
Section titled “Extensions and Improvements”1. Weather Maps Integration
Section titled “1. Weather Maps Integration”def show_weather_map(self, city):
"""Display weather map for city"""
# Integrate with map services
map_url = f"https://tile.openweathermap.org/map/temp_new/5/{lat}/{lon}.png"
# Display map in new window2. Push Notifications
Section titled “2. Push Notifications”def setup_weather_notifications(self):
"""Setup desktop notifications for weather alerts"""
import plyer
def notify_weather_alert(message):
plyer.notification.notify(
title="Weather Alert",
message=message,
timeout=10
)3. Multiple Language Support
Section titled “3. Multiple Language Support”def set_language(self, lang_code):
"""Set application language"""
self.language = lang_code
self.load_translations()
self.update_ui_text()Performance Optimization
Section titled “Performance Optimization”1. Caching Strategy
Section titled “1. Caching Strategy”import time
def get_weather_with_cache(self, city):
"""Get weather data with caching"""
cache_key = f"weather_{city}"
cache_duration = 300 # 5 minutes
if cache_key in self.cache:
cached_data, timestamp = self.cache[cache_key]
if time.time() - timestamp < cache_duration:
return cached_data
# Fetch fresh data
data = self.get_weather_data(city)
self.cache[cache_key] = (data, time.time())
return data2. Background Updates
Section titled “2. Background Updates”def start_background_updates(self):
"""Start background weather updates"""
def update_worker():
while self.running:
for city in self.favorites:
self.update_weather_cache(city)
time.sleep(600) # Update every 10 minutes
self.update_thread = threading.Thread(target=update_worker, daemon=True)
self.update_thread.start()Next Steps
Section titled “Next Steps”After completing this weather app, consider:
- Mobile Development: Create mobile versions with Kivy or BeeWare
- Web Version: Build web interface with Flask or Django
- Machine Learning: Add weather prediction models
- IoT Integration: Connect with weather sensors
- Advanced Visualizations: Use Plotly for interactive charts
Resources
Section titled “Resources”- OpenWeatherMap API Documentation
- Tkinter Documentation
- Python Threading
- Requests Library
- JSON Handling in Python
Conclusion
Section titled “Conclusion”This weather application demonstrates professional GUI development with Python. It showcases important concepts like API integration, threading, data persistence, and user interface design. The modular structure makes it easy to extend with additional features and customizations.
The application provides a solid foundation for building more complex desktop applications and demonstrates best practices for handling external APIs and creating responsive user interfaces. 🌤️🐍
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading