本文主要是介绍python编写朋克风格的天气查询程序,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《python编写朋克风格的天气查询程序》这篇文章主要为大家详细介绍了一个基于Python的桌面应用程序,使用了tkinter库来创建图形用户界面并通过requests库调用Open-MeteoAPI...
工具介绍
这个天气查询工具是一个基于 Python 的桌面应用程序,使用了tkinter库来创建图形用户界面(GUI),并通过requests库调用 Open - Meteo API 获取天气数据。它具有赛博朋克风格的界面设计,提供了当前天气信息、15 天天气预报以及详细的天气数据展示,同时还包含温度趋势图表。
工具使用说明
1.界面布局
顶部标题栏:显示应用名称 “赛博朋克风格天气查询”。
2.搜索区域
- 输入框:可以输入要查询的城市名称,默认显示为 “北京”。
- 查询按钮:点击该按钮可以查询输入城市的天气信息。
3.标签页
- 当前天气:显示当前城市的天气信息,包括城市名称、日期、天气状况、温度、体感温度以及一些基本的天气数据(如湿度、风速等)。
- 15 天预报:包含未来 15 天的温度趋势图表和每天的天气预报卡片,卡片显示日期、星期、天气状况、最高温度和最低温度。
- 详细信息:显示更详细的天气数据,如日出时间、日落时间、日照时长、风向等。
4.查询天气
- 在输入框中输入要查询的城市名称。
- 点击 “查询天气” 按钮。
- 应用会显示加载中指示器,同时在后台线程中获取天气数据。
- 获取数据成功后,更新各个标签页的内容,显示该城市的天气信息;如果获取数据失败,会弹出错误提示框。
5.功能特点
- 多信息展示:提供当前天气、15 天预报和详细天气信息,满足用户对不同时间尺度天气数据的需求。
- 图表可视化:通过matplotlib库绘制未来 15 天的温度趋势图表,直观展示温度变化。
- 赛博朋克风格:使用赛博朋克风格的颜色和字体,界面设计独特。
- 多线程处理:在新线程中获取天气数据,避免阻塞 UI,保证用户交互的流畅性。
- 错误处理:在获取数据或更新界面失败时,会弹出错误提示框,告知用户具体的错误信息。
python脚本内容
import tkinter as tk from tkinter import ttk, messagebox import requests from datetime import datetime, timedelta import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import numpy as np import threading # 设置中文字体 plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"] class WeatherApp: def __init__(self, root): self.root = root self.root.title("赛博天气 - 未来气象预测") self.root.geometry("800x700") self.root.configure(bg="#0A0E17") # 赛博朋克风格颜色 self.colors = { 'neon_blue': '#00F0FF', 'neon_purple': '#BF00FF', 'neon_pink': '#FF00FF', 'neon_green': '#00FF9C', 'dark_900': '#0A0E17', 'dark_800': '#141E30', 'dark_700': '#1A2940' } # 创建加载中指示器 self.loading_frame = None self.loading_label = None # 创建UI self.create_widgets() # 初始加载北京天气 self.get_weather("北京") def create_widgets(self): # 顶部标题栏 title_frame = tk.Frame(self.root, bg=self.colors['dark_900'], bd=2, relief=tk.SOLID) title_frame.pack(fill=tk.X, pady=(0, 10)) title_label = tk.Label( title_frame, text="赛博朋克风格天气查询", font=("SimHei", 20, "bold"), bg=self.colors['dark_900'], fg=self.colors['neon_blue'], bd=0, highlightthickness=0 ) title_label.pack(pady=10) # 搜索区域 search_frame = tk.Frame(self.root, bg=self.colors['dark_900']) search_frame.pack(fill=tk.X, padx=20, pady=10) self.city_entry = ttk.Entry( search_frame, font=("SimHei", 12), width=30, background=self.colors['dark_800'], foreground=self.colors['neon_blue'] ) self.city_entry.pack(side=tk.LEFT, padx=(0, 10), ipady=5) self.city_entry.insert(0, "北京") search_btn = ttk.Button( search_frame, text="查询天气", command=self.search_weather, style='Neon.TButton' ) search_btn.pack(side=tk.LEFT, ipady=5) # 创建标签页 notebook = ttk.Notebook(self.root) notebook.pack(fill=tk.BOTH, expand=True, padx=20, pady=10) # 当前天气标签页 chttp://www.chinasem.cnurrent_frame = ttk.Frame(notebook) notebook.add(current_frame, text="当前天气") # 15天预报标签页 forecast_frame = ttk.Frame(notebook) notebook.add(forecast_frame, text="15天预报") # 详细信息标签页 details_frame = ttk.Frame(notebook) notebook.add(details_frame, text="详细信息") # 构建各标签页内容 self.build_current_weather_frame(current_frame) self.build_forecast_frame(forecast_frame) self.build_details_frame(details_frame) # 配置自定义样式 self.configure_styles() def configure_styles(self): style = ttk.Style() # 配置TButton样式 style.configure('TButton', font=("SimHei", 10), background=self.colors['dark_800'], foreground=self.colors['neon_blue'], borderwidth=1, relief=tk.RAISED ) # 赛博朋克风格按钮 style.configure('Neon.TButton', font=("SimHei", 10, "bold"), background=self.colors['neon_blue'], foreground=self.colors['dark_900'], borderwidth=0, relief=tk.FLAT ) style.map('Neon.TButton', background=[('active', self.colors['neon_pink'])] ) # 配置标签样式 style.configure('TLabel', font=("SimHei", 10), background=self.colors['dark_900'], foreground="white" ) # 标题样式 style.configure('Title.TLabel', font=("SimHei", 16, "bold"), foreground=self.colors['neon_blue'] ) # 子标题样式 style.configure('Subtitle.TLabel', font=("SimHei", 12, "bold"), foreground=self.colors['neon_purple'] ) # 数据标签样式 style.configure('Data.TLabel', font=("SimHei", 14, "bold"), foreground="white" ) # 数据值样式 style.configure('Value.TLabel', font=("SimHei", 16, "bold"), foreground=self.colors['neon_green'] ) # 天气卡片样式 style.configure('WeatherCard.TFrame', background=self.colors['dark_800'] ) # 详细信息卡片样式 style.configure('DetailCard.TFrame', background=self.colors['dark_700'] ) def build_current_weather_frame(self, parent): # 主天气信息框架 main_info_frame = ttk.Frame(parent, style='WeatherCard.TFrame') main_info_frame.pack(fill=tk.X, padx=10, pady=10, ipady=10) # 左侧信息 left_frame = ttk.Frame(main_info_frame, style='WeatherCard.TFrame') left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10) self.location_label = ttk.Label(left_frame, text="北京市", style='Title.TLabel') self.location_label.pack(anchor=tk.W, pady=(5, 0)) self.date_label = ttk.Label(left_frame, text="2023年10月15日 星期日", style='TLabel') self.date_label.pack(anchor=tk.W) self.condition_label = ttk.Label(left_frame, text="晴朗", style='Subtitle.TLabel') self.condition_label.pack(anchor=tk.W, pady=(10, 0)) # 右侧温度 right_frame = ttk.Frame(main_info_frame, style='WeatherCard.TFrame') right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10) self.temp_label = ttk.Label(right_frame, text="22C", style='Value.TLabel') self.temp_label.pack(anchor=tk.E, pady=(5, 0)) self.feels_like_label = ttk.Label(right_frame, text="体感温度: 24C", style='TLabel') self.feels_like_label.pack(anchor=tk.E) # 基本信息网格 grid_frame = ttk.Frame(parent, style='WeatherCard.TFrame') grid_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10)) # 创建信息标签变量列表 self.info_labels = [] for i in range(8): row = i // 2 col = i % 2 card_frame = ttk.Frame(grid_frame, style='DetailCard.TFrame') card_frame.grid(row=row, column=col, padx=5, pady=5, sticky="nsew") # 设置网格权重,使卡片均匀分布 grid_frame.grid_rowconfigure(row, weight=1) grid_frame.grid_columnconfigure(col, weight=1) title_label = ttk.Label(card_frame, text="", style='Subtitle.TLabel') title_label.pack(pady=(5, 0)) value_label = ttk.Label(card_frame, text="", font=("SimHei", 12, "bold")) value_label.pack(pady=(0, 5)) self.info_labels.append((title_label, value_label)) def build_forecast_frame(self, parent): # 温度趋势图表 chart_frame = ttk.Frame(parent, style='WeatherCard.TFrame') chart_frame.pack(fill=tk.X, padx=10, pady=10, ipady=10) ttk.Label(chart_frame, text="未来15天温度趋势", style='Title.TLabel').pack(pady=(0, 10)) # 创建图表 self.fig, self.ax = plt.subplots(figsize=(7, 3), dpi=100) self.canvas = FigureCanvasTkAgg(self.fig, master=chart_frame) self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) # 15天预报网格 forecast_frame = ttk.Frame(parent, style='WeatherCard.TFrame') forecast_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10)) # 创建滚动条 canvas = tk.Canvas(forecast_frame, bg=self.colors['dark_800'], highlightthickness=0) scrollbar = ttk.Scrollbar(forecast_frame, orient="vertical", command=canvas.yview) self.forecast_content = ttk.Frame(canvas, style='WeatherCard.TFrame') self.forecast_content.bind( "<Configure>", lambda e: canvas.configure( scrollregion=canvas.bbox("all") ) ) canvas.create_window((0, 0), window=self.forecast_content, anchor="nw") canvas.configure(yscrollcommand=scrollbar.set) canvas.pack(side="left", fill="both", expand=True) scrollbar.pack(side="right", fill="y") def build_details_frame(self, parent): # 详细信息网格 details_frame = ttk.Frame(parent, style='WeatherCard.TFrame') details_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 创建信息标签变量列表 self.detail_labels = [] for i in range(9): row = i // 3 col = i % 3 card_frame = ttk.Frame(details_frame, style='DetailCard.TFrame') card_frame.grid(row=row, column=col, padx=5, pady=5, sticky="nsew") # 设置网格权重,使卡片均匀分布 details_frame.grid_rowconfigure(row, weight=1) details_frame.grid_columnconfigure(col, weight=1) title_label = ttk.Label(card_frame, text="", style='Subtitle.TLabel') title_label.pack(pady=(5, 0)) value_label = ttk.Label(card_frame, text="", font=("SimHei", 12, "bold")) value_label.pack(pady=(0, 5)) self.detail_labels.append((title_label, value_label)) dehttp://www.chinasem.cnf show_loading(self, show=True): if show: # 创建加载中指示器 if self.loading_frame is None: self.loading_frame = tk.Frame(self.root, bg=self.colors['dark_900'], bd=2, relief=tk.SOLID) self.loading_frame.place(relx=0.5, rely=0.5, anchor="center", relwidth=0.4, relheight=0.2) self.loading_label = ttk.Label( self.loading_frame, text="正在获取天气数据...", font=("SimHei", 14), foreground=self.colors['neon_blue'] ) self.loading_label.place(relx=0.5, rely=0.5, anchor="cenwww.chinasem.cnter") else: # 移除加载中指示器 if self.loading_frame is not None: self.loading_frame.destroy() self.loading_frame = None self.loading_label = NoneChina编程 def search_weather(self): city = self.city_entry.get().strip() if city: self.show_loading(True) # 在新线程中获取天气数据,避免阻塞UI threading.Thread(target=self.get_weather, args=(city,), daemon=True).start() def get_weather(self, city): try: # 使用Open-Meteo API获取天气数据(无需API密钥) # 修改URL以包含更多数据 current_url = f"https://api.open-meteo.com/v1/forecast?latitude=39.91&longitude=116.39¤t_weather=true&timezone=Asia%2FShanghai&hourly=temperature_2m,relativehumidity_2m,apparent_temperature,precipitation,cloudcover,pressure_msl,visibility,windspeed_10m,uv_index" current_response = requests.get(current_url) current_data = current_response.json() # 获取15天预报 forecast_url = f"https://api.open-meteo.com/v1/forecast?latitude=39.91&longitude=116.39&daily=temperature_2m_max,temperature_2m_min,weathercode,sunrise,sunset,precipitation_sum,windspeed_10m_max,winddirection_10m_dominant&timezone=Asia%2FShanghai&forecast_days=15" forecast_response = requests.get(forecast_url) forecast_data = forecast_response.json() # 更新UI self.root.after(0, lambda: self.update_ui(city, current_data, forecast_data)) except Exception as e: self.root.after(0, lambda: messagebox.showerror("错误", f"获取天气数据失败: {str(e)}")) finally: # 隐藏加载指示器 self.root.after(0, lambda: self.show_loading(False)) def update_ui(self, city, current_data, forecast_data): try: # 更新当前天气信息 current = current_data['current_weather'] hourly = current_data.get('hourly', {}) daily = forecast_data.get('daily', {}) # 获取当前时间索引 current_time = current['time'] time_list = hourly.get('time', []) if current_time in time_list: time_index = time_list.index(current_time) else: time_index = 0 # 获取当前详细数据 humidity = hourly.get('relativehumidity_2m', [None])[time_index] pressure = hourly.get('pressure_msl', [None])[time_index] visibility = hourly.get('visibility', [None])[time_index] cloudcover = hourly.get('cloudcover', [None])[time_index] windspeed = hourly.get('windspeed_10m', [None])[time_index] uv_index = hourly.get('uv_index', [None])[time_index] # 更新UI元素 self.location_label.config(text=city) self.date_label.config(text=datetime.now().strftime("%Y年%m月%d日 %A")) self.temp_label.config(text=f"{current['temperature']}C") self.condition_label.config(text=self.get_weather_description(current['weathercode'])) self.feels_like_label.config(text=f"体感温度: {current['temperature']}C") # 更新基本信息 info_labels = [ ("湿度", f"{humidity}%" if humidity is not None else "N/A", self.colors['neon_pink']), ("风速", f"{wwww.chinasem.cnindspeed} km/h" if windspeed is not None else "N/A", self.colors['neon_blue']), ("气压", f"{pressure} hPa" if pressure is not None else "N/A", self.colors['neon_purple']), ("能见度", f"{visibility/1000:.1f} km" if visibility is not None else "N/A", self.colors['neon_green']), ("云量", f"{cloudcover}%" if cloudcover is not None else "N/A", self.colors['neon_blue']), ("露点", "15C", self.colors['neon_pink']), ("紫外线指数", f"{uv_index}" if uv_index is not None else "N/A", self.colors['neon_purple']), ("降水概率", "10%", self.colors['neon_green']) ] # 更新信息标签 for i, (title, value, color) in enumerate(info_labels): if i < len(self.info_labels): title_label, value_label = self.info_labels[i] title_label.config(text=title) value_label.config(text=value, foreground=color) # 更新详细信息 sunrise_time = daily.get('sunrise', [None])[0] sunset_time = daily.get('sunset', [None])[0] precipitation = daily.get('precipitation_sum', [None])[0] wind_direction = daily.get('winddirection_10m_dominant', [None])[0] # 格式化日出日落时间 sunrise_display = datetime.fromisoformat(sunrise_time).strftime('%H:%M') if sunrise_time else "N/A" sunset_display = datetime.fromisoformat(sunset_time).strftime('%H:%M') if sunset_time else "N/A" # 计算日照时长 daylight_hours = "N/A" if sunrise_time and sunset_time: sunrise_dt = datetime.fromisoformat(sunrise_time) sunset_dt = datetime.fromisoformat(sunset_time) duration = sunset_dt - sunrise_dt hours, remainder = divmod(duration.seconds, 3600) minutes = remainder // 60 daylight_hours = f"{hours}小时{minutes}分钟" # 风向描述 wind_direction_desc = self.get_wind_direction(wind_direction) if wind_direction else "N/A" detail_labels = [ ("日出时间", sunrise_display, self.colors['neon_blue']), ("日落时间", sunset_display, self.colors['neon_pink']), ("日照时长", daylight_hours, self.colors['neon_green']), ("风向", wind_direction_desc, self.colors['neon_purple']), ("雪量", "0 mm", self.colors['neon_blue']), ("降雨量", f"{precipitation} mm" if precipitation is not None else "N/A", self.colors['neon_pink']), ("天气代码", f"{current['weathercode']}", self.colors['neon_green']), ("气压趋势", "稳定", self.colors['neon_purple']), ("体感描述", "舒适", self.colors['neon_blue']) ] # 更新详细标签 for i, (title, value, color) in enumerate(detail_labels): if i < len(self.detail_labels): title_label, value_label = self.detail_labels[i] title_label.config(text=title) value_label.config(text=value, foreground=color) # 更新图表 self.update_chart(daily) # 更新15天预报 self.update_forecast_cards(daily) except Exception as e: messagebox.showerror("错误", f"更新界面失败: {str(e)}") def update_chart(self, daily_data): try: self.ax.clear() # 提取数据 dates = [datetime.fromisoformat(date).strftime('%m-%d') for date in daily_data.get('time', [])] max_temps = daily_data.get('temperature_2m_max', []) min_temps = daily_data.get('temperature_2m_min', []) if not dates or not max_temps or not min_temps: self.ax.set_title("无法获取温度数据") self.canvas.draw() return # 绘制图表 x = np.arange(len(dates)) width = 0.35 self.ax.bar(x - width/2, min_temps, width, label='最低温度', color=self.colors['neon_purple']) self.ax.bar(x + width/2, max_temps, width, label='最高温度', color=self.colors['neon_blue']) self.ax.set_xlabel('日期') self.ax.set_ylabel('温度 (C)') self.ax.set_title('未来15天温度趋势') self.ax.set_xticks(x) self.ax.set_xticklabels(dates, rotation=45) self.ax.legend() self.fig.tight_layout() self.canvas.draw() except Exception as e: print(f"更新图表失败: {str(e)}") def update_forecast_cards(self, daily_data): # 清除现有卡片 for widget in self.forecast_content.winfo_children(): widget.destroy() # 创建新卡片 times = daily_data.get('time', []) max_temps = daily_data.get('temperature_2m_max', []) min_temps = daily_data.get('temperature_2m_min', []) weather_codes = daily_data.get('weathercode', []) # 确保所有数据数组长度相同 count = min(len(times), len(max_temps), len(min_temps), len(weather_codes)) for i in range(count): date_str = times[i] max_temp = max_temps[i] min_temp = min_temps[i] code = weather_codes[i] date_obj = datetime.fromisoformat(date_str) day_of_week = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][date_obj.weekday()] date_display = date_obj.strftime('%m月%d日') card_frame = ttk.Frame(self.forecast_content, style='DetailCard.TFrame') card_frame.grid(row=i, column=0, padx=5, pady=5, sticky="nsew") # 设置网格权重 self.forecast_content.grid_rowconfigure(i, weight=1) self.forecast_content.grid_columnconfigure(0, weight=1) # 左侧日期和星期 left_frame = ttk.Frame(card_frame, style='DetailCard.TFrame') left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=5) ttk.Label(left_frame, text=date_display, style='Subtitle.TLabel').pack(anchor=tk.W) ttk.Label(left_frame, text=day_of_week, foreground=self.colors['neon_blue']).pack(anchor=tk.W) # 中间天气状况 mid_frame = ttk.Frame(card_frame, style='DetailCard.TFrame') mid_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=5) weather_desc = self.get_weather_description(code) ttk.Label(mid_frame, text=weather_desc, foreground=self.colors['neon_pink']).pack(anchor=tk.CENTER) # 右侧温度 right_frame = ttk.Frame(card_frame, style='DetailCard.TFrame') right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10, pady=5) ttk.Label(right_frame, text=f"高: {max_temp}C", foreground=self.colors['neon_blue']).pack(anchor=tk.E) ttk.Label(right_frame, text=f"低: {min_temp}C", foreground=self.colors['neon_purple']).pack(anchor=tk.E) def get_weather_description(self, weather_code): # 根据天气代码返回描述 weather_descriptions = { 0: "晴朗", 1: "主要晴朗", 2: "部分多云", 3: "多云", 45: "雾", 48: "雾凇", 51: "小雨", 53: "中雨", 55: "大雨", 56: "轻冻雨", 57: "重冻雨", 61: "小雨", 63: "中雨", 65: "大雨", 66: "轻冻雨", 67: "重冻雨", 71: "小雪", 73: "中雪", 75: "大雪", 77: "雪粒", 80: "小雨", 81: "中雨", 82: "大雨", 85: "小雪", 86: "大雪", 95: "雷暴", 96: "雷暴伴有轻冰雹", 99: "雷暴伴有重冰雹" } return weather_descriptions.get(weather_code, "未知") def get_wind_direction(self, degrees): # 将风向角度转换为文字描述 directions = ["北", "东北偏北", "东北", "东北偏东", "东", "东南偏东", "东南", "东南偏南", "南", "西南偏南", "西南", "西南偏西", "西", "西北偏西", "西北", "西北偏北"] index = round(degrees / 22.5) % 16 return directions[index] if __name__ == "__main__": root = tk.Tk() app = WeatherApp(root) root.mainloop()
如何运行脚本
将脚本内容放在文件名后缀为.py的文件中保存。
使用Windows的python命令运行此文件即可。
python版本为:Python 3.12.5
到此这篇关于python编写朋克风格的天气查询程序的文章就介绍到这了,更多相关python天气查询内容请搜索编程China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!
这篇关于python编写朋克风格的天气查询程序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!