一般天氣預報-今明36小時天氣預報---Python TKInter
Python
import tkinter as tk
from tkinter import messagebox
import requests
import json
# 全局變數用於儲存所有縣市的原始天氣資料,避免重複下載
ALL_WEATHER_DATA = None
def fetch_weather_data():
"""從中央氣象署開放資料平台擷取天氣資料"""
# 替換成你自己的 API 金鑰
# (此處省略 URL,請確保您的程式碼中有正確的 URL 和 API 金鑰)
url = "https://opendata.cwa.gov.tw/fileapi/v1/opendataapi/F-C0032-001?Authorization=YOUR_ACTUAL_API_KEY&format=JSON" # 請務必替換為您的金鑰
try:
response = requests.get(url)
response.raise_for_status() # 如果請求不成功,拋出 HTTPError
data = response.json()
return data
except requests.exceptions.RequestException as e:
messagebox.showerror("網路錯誤", f"無法連接到天氣資料服務:{e}")
return None
except requests.exceptions.JSONDecodeError as e:
messagebox.showerror("資料解析錯誤", f"無法解析 API 回應為 JSON:{e}\n請檢查 API 金鑰或資料格式是否正確。")
return None
def parse_and_filter_weather_data(data, target_location_name=None):
"""
解析天氣資料並提取所需資訊。
可選參數 target_location_name 用於只顯示特定縣市的資料。
"""
if not data or "cwaopendata" not in data:
return "資料格式錯誤或無資料,或頂層無 cwaopendata 鍵。"
cwa_data = data["cwaopendata"]
if "dataset" not in cwa_data:
return "cwaopendata 內部無 dataset 鍵。"
actual_dataset = cwa_data["dataset"]
if "location" not in actual_dataset:
return "dataset 內部無 location 鍵。"
locations = actual_dataset["location"]
weather_reports = []
for loc in locations:
location_name = loc.get("locationName", "未知地區")
if target_location_name and location_name != target_location_name:
continue
weather_report_for_loc = f"地區:{location_name}\n"
elements_by_time = {}
for wp in loc.get("weatherElement", []):
element_name = wp.get("elementName")
if element_name:
if element_name not in elements_by_time:
elements_by_time[element_name] = []
for time_slot in wp.get('time', []):
start_time = time_slot.get('startTime', 'N/A')
end_time = time_slot.get('endTime', 'N/A')
parameter_name = time_slot.get('parameter', {}).get('parameterName', 'N/A')
elements_by_time[element_name].append({
'startTime': start_time,
'endTime': end_time,
'parameterName': parameter_name
})
num_time_slots = 0
if "Wx" in elements_by_time:
num_time_slots = len(elements_by_time["Wx"])
elif "PoP" in elements_by_time:
num_time_slots = len(elements_by_time["PoP"])
for i in range(num_time_slots):
weather_report_for_loc += f"\n--- 預報時段 {i+1} ---\n"
if "Wx" in elements_by_time and len(elements_by_time["Wx"]) > i:
start = elements_by_time["Wx"][i]['startTime']
end = elements_by_time["Wx"][i]['endTime']
weather_report_for_loc += f"時間:{start[5:16]} 至 {end[5:16]}\n"
if "Wx" in elements_by_time and len(elements_by_time["Wx"]) > i:
weather_report_for_loc += f"天氣現象:{elements_by_time['Wx'][i]['parameterName']}\n"
if "PoP" in elements_by_time and len(elements_by_time["PoP"]) > i:
weather_report_for_loc += f"降雨機率:{elements_by_time['PoP'][i]['parameterName']}%\n"
if "MinT" in elements_by_time and len(elements_by_time["MinT"]) > i:
weather_report_for_loc += f"最低溫度:{elements_by_time['MinT'][i]['parameterName']}°C\n"
if "MaxT" in elements_by_time and len(elements_by_time["MaxT"]) > i:
weather_report_for_loc += f"最高溫度:{elements_by_time['MaxT'][i]['parameterName']}°C\n"
if "CI" in elements_by_time and len(elements_by_time["CI"]) > i:
weather_report_for_loc += f"舒適度:{elements_by_time['CI'][i]['parameterName']}\n"
weather_report_for_loc += "====================\n"
weather_reports.append(weather_report_for_loc)
if not weather_reports:
return f"找不到 {target_location_name} 的天氣資料,或資料處理失敗。"
return "\n".join(weather_reports)
def display_selected_weather(location_name):
"""根據選擇的縣市名稱顯示天氣預報"""
global ALL_WEATHER_DATA
if ALL_WEATHER_DATA is None:
messagebox.showwarning("資料未載入", "請稍候,天氣資料正在初始化中...")
return
parsed_data = parse_and_filter_weather_data(ALL_WEATHER_DATA, location_name)
text_area.delete("1.0", tk.END)
text_area.insert(tk.END, parsed_data)
def initialize_app():
"""初始化應用程式:下載資料並建立縣市按鈕"""
global ALL_WEATHER_DATA
text_area.insert(tk.END, "載入中,請稍候...\n") # 給予使用者反饋
root.update_idletasks() # 強制更新 GUI 以顯示訊息
ALL_WEATHER_DATA = fetch_weather_data()
if ALL_WEATHER_DATA:
try:
locations = ALL_WEATHER_DATA["cwaopendata"]["dataset"]["location"]
location_names = sorted([loc.get("locationName", "未知") for loc in locations])
# 動態生成按鈕 (修改部分在此)
max_cols = 8 # 每行最多 8 個按鈕
row = 0
col = 0
for name in location_names:
button = tk.Button(button_frame, text=name, command=lambda n=name: display_selected_weather(n), font=("Arial", 10), width=8, height=2) # 調整寬高
button.grid(row=row, column=col, padx=3, pady=3) # 調整內邊距
col += 1
if col >= max_cols: # 如果達到最大列數,換行
col = 0
row += 1
# 預設顯示第一個縣市的天氣
if location_names:
display_selected_weather(location_names[0])
except KeyError as e:
messagebox.showerror("資料結構錯誤", f"無法從 API 資料中找到預期的鍵:{e}\n請檢查 API 資料格式是否已變更。")
text_area.insert(tk.END, "初始化失敗:資料結構不符預期。")
except Exception as e:
messagebox.showerror("初始化錯誤", f"應用程式初始化時發生未知錯誤:{e}")
text_area.insert(tk.END, "初始化失敗。")
else:
text_area.insert(tk.END, "未能載入天氣資料,請檢查網路連線或 API 金鑰。")
# --- Tkinter 介面設定 ---
root = tk.Tk()
root.title("今明36小時天氣預報")
root.geometry("800x700")
# 縣市按鈕框架
button_frame = tk.Frame(root, padx=10, pady=10, bg="#e0e0e0")
button_frame.pack(side="top", fill="x")
# 顯示天氣資訊的文字區域
text_area = tk.Text(root, wrap="word", font=("Arial", 12), padx=10, pady=10)
text_area.pack(expand=True, fill="both")
# 捲軸
scrollbar = tk.Scrollbar(text_area)
scrollbar.pack(side="right", fill="y")
text_area.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=text_area.yview)
# 應用程式啟動時,載入資料並建立按鈕
root.after(100, initialize_app) # 使用 after 延遲呼叫,確保 Tkinter 視窗已初始化
root.mainloop()







沒有留言:
張貼留言