import threading import requests from bs4 import BeautifulSoup from tkinter import Frame, Label, PhotoImage, Tk from app.widgets.abc import ThreadedTab import cairosvg from io import BytesIO class WeatherTab(ThreadedTab): def __init__(self, root: Frame | Tk, city: str, **kwargs): self.city = city self.weather_info = {} self.weather_image = None self.weather_frame = None self.weather_label = None self.weather_image_label = None self.city_label = None self.real_feel_label = None self.wind_label = None self.wind_gusts_label = None self.air_quality_label = None super().__init__(root, **kwargs) def build(self): self.weather_frame = Frame(self) self.weather_frame.pack(fill="both", expand=True) self.city_label = Label(self.weather_frame, text=f"Weather in {self.city}", font=("Helvetica", 16)) self.city_label.grid(row=0, column=0, columnspan=2, pady=10, sticky="ew") self.weather_image_label = Label(self.weather_frame) self.weather_image_label.grid(row=1, column=0, padx=10, sticky="ew") self.weather_label = Label(self.weather_frame, text="", font=("Helvetica", 14)) self.weather_label.grid(row=1, column=1, padx=10, sticky="ew") self.real_feel_label = Label(self.weather_frame, text="", font=("Helvetica", 12)) self.real_feel_label.grid(row=2, column=1, padx=10, sticky="ew") self.wind_label = Label(self.weather_frame, text="", font=("Helvetica", 12)) self.wind_label.grid(row=3, column=1, padx=10, sticky="ew") self.wind_gusts_label = Label(self.weather_frame, text="", font=("Helvetica", 12)) self.wind_gusts_label.grid(row=4, column=1, padx=10, sticky="ew") self.air_quality_label = Label(self.weather_frame, text="", font=("Helvetica", 12)) self.air_quality_label.grid(row=5, column=1, padx=10, sticky="ew") self.weather_frame.columnconfigure(0, weight=1) self.weather_frame.columnconfigure(1, weight=1) # Ensure the frame fills the entire parent space self.grid(row=0, column=0, sticky="nsew") self.master.rowconfigure(0, weight=1) self.master.columnconfigure(0, weight=1) def task(self, *args): self.fetch_weather_data() self.update_ui() def fetch_weather_data(self): try: headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' } search_url = f"https://www.accuweather.com/en/search-locations?query={self.city}" search_response = requests.get(search_url, headers=headers) search_soup = BeautifulSoup(search_response.text, 'html.parser') location_list = search_soup.find('div', class_='locations-list content-module') if not location_list: print("Location list not found") return location_link = location_list.find('a')['href'] weather_url = f"https://www.accuweather.com{location_link}" weather_response = requests.get(weather_url, headers=headers) weather_soup = BeautifulSoup(weather_response.text, 'html.parser') weather_icon_path = weather_soup.find('svg', class_='weather-icon')['data-src'] weather_icon_url = f"https://www.accuweather.com{weather_icon_path}" weather_icon_response = requests.get(weather_icon_url, headers=headers) weather_icon_svg = weather_icon_response.content # Convert SVG to PNG and resize to 50x50 weather_icon_png = cairosvg.svg2png(bytestring=weather_icon_svg, output_width=50, output_height=50) weather_icon_image = PhotoImage(data=BytesIO(weather_icon_png).getvalue()) temperature = weather_soup.find('div', class_='temp').text real_feel = weather_soup.find('div', class_='real-feel').text.strip().replace('RealFeelĀ®', '').strip() details_container = weather_soup.find('div', class_='details-container') wind = details_container.find('span', text='Wind').find_next('span', class_='value').text.strip() if details_container else 'N/A' wind_gusts = details_container.find('span', text='Wind Gusts').find_next('span', class_='value').text.strip() if details_container else 'N/A' air_quality = details_container.find('span', text='Air Quality').find_next('span', class_='value').text.strip() if details_container else 'N/A' self.weather_info = { 'icon': weather_icon_image, 'temperature': temperature, 'real_feel': real_feel, 'wind': wind, 'wind_gusts': wind_gusts, 'air_quality': air_quality } except Exception as e: print(f"Error fetching weather data: {e}") def update_ui(self): if self.weather_info: weather_text = f"{self.weather_info['temperature']}" self.weather_label.config(text=weather_text) self.weather_image_label.config(image=self.weather_info['icon']) self.weather_image_label.image = self.weather_info['icon'] self.real_feel_label.config(text=f"RealFeel: {self.weather_info['real_feel']}") self.wind_label.config(text=f"Wind: {self.weather_info['wind']}") self.wind_gusts_label.config(text=f"Wind Gusts: {self.weather_info['wind_gusts']}") self.air_quality_label.config(text=f"Air Quality: {self.weather_info['air_quality']}") def changeCity(self, city): self.city = city self.city_label.config(text=f"Weather in {self.city}") threading.Thread(target=self.task).start()