Python Forum
Tkinter Weather App - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: General (https://python-forum.io/forum-1.html)
+--- Forum: Code sharing (https://python-forum.io/forum-5.html)
+--- Thread: Tkinter Weather App (/thread-36091.html)



Tkinter Weather App - menator01 - Jan-16-2022

I was intrigued by this post and thought I would make a tkinter weather app.

#! /usr/bin/env python3

# Do the imports
import tkinter as tk
import tkinter.font as tkfont
from bs4 import BeautifulSoup as bs
import requests
import urllib
import base64

# Create weather class to get info
class Weather:
    def __init__(self):
        # Set some vars
        LANGUAGE = 'en-US, en;q=0.5'
        USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"

        # session vars
        session = requests.Session()
        session.headers['User-Agent'] = USER_AGENT
        session.headers['Accept-Language'] = LANGUAGE
        session.headers['Content-Language'] = LANGUAGE

        # Url to google weather, get the data, use soup to parse
        url = 'https://www.google.com/search?lr=lang_en&ie=UTF-8&q=weather'
        data = session.get(url)
        soup = bs(data.text, 'html.parser')

        # Set an empty dict and grab the data we want
        self.results = {}
        self.results['region'] = soup.find('div', attrs={'id': 'wob_loc'}).text
        self.results['current temperature'] = soup.find('span', attrs={'id': 'wob_tm'}).text
        self.results['day and hour'] = soup.find('div', attrs={'id': 'wob_dts'}).text
        self.results['current weather'] = soup.find('span', attrs={'id': 'wob_dc'}).text
        self.results['precipitation'] = soup.find('span', attrs={'id': 'wob_pp'}).text
        self.results['humidity'] = soup.find('span', attrs={'id': 'wob_hm'}).text
        self.results['wind speed'] = soup.find('span', attrs={'id': 'wob_ws'}).text
        self.results['img'] = soup.find('img', attrs={'class': 'wob_tci', 'id': 'wob_tci'})['src']

# Create the tkinter window class
class Window:
    def __init__(self, parent):
        # set the window parent and configure rows and columns
        self.parent = parent
        self.parent.columnconfigure(0, weight=1)
        self.parent.rowconfigure(0, weight=1)

        # Initiate the weather class
        self.weather = Weather()

        # Create the main container
        container = tk.Frame(self.parent)
        container.grid(column=0, row=0, sticky='news')
        container.grid_columnconfigure(0, weight=3)

        # Container to hold the data in our header
        header_container = tk.Frame(container)
        header_container.grid(column=0, row=0, sticky='new')
        header_container.grid_columnconfigure(1, weight=3)

        # Container for holding weather info
        self.info_container = tk.Frame(container)
        self.info_container.grid(column=0, row=1, sticky='news')
        for i in range(2):
            self.info_container.grid_columnconfigure(i, weight=3, uniform='info')

        # Label for the image
        self.img_label = tk.Label(header_container)
        self.img_label['image'] = self.get_image()
        self.img_label['relief'] = 'groove'
        self.img_label.grid(column=0, row=0, sticky='new')

        # Label for header text
        header = tk.Label(header_container)
        header['text'] = self.weather.results['region']
        header['font'] = tkfont.Font(size=18, weight='bold')
        header['relief'] = 'groove'
        header['padx'] = 8
        header['fg'] = 'blue'
        header.grid(column=1, row=0, sticky='news')


        # Create empty list for labels and set a counter
        self.labels = []
        i = 0

        # Loop through the dict and assign to labels
        for key, value in self.weather.results.items():
            if key != 'region' and key != 'img':
                if key == 'current temperature':
                    text = value.title() + '\u00b0' + ' F'
                else:
                    text = value.title()
                self.labels.append([tk.Label(self.info_container, text=key.title(), anchor='w'),
                tk.Label(self.info_container, text=text, anchor='w')])
                self.labels[i][0]['font'] = tkfont.Font(weight='bold', size=10)
                self.labels[i][0]['relief'] = 'groove'
                self.labels[i][1]['relief'] = 'groove'
                self.labels[i][0].grid(column=0, row=i, sticky='new', ipadx=5)
                self.labels[i][1].grid(column=1, row=i, sticky='new', ipadx=5)
                i += 1

        # Call self.update to update the information
        self.update()

    # function/method for getting and returning the image
    def get_image(self):
        url = 'https:' + self.weather.results['img']
        img = urllib.request.urlopen(url)
        raw = img.read()
        img.close()

        image = tk.PhotoImage(data=raw)
        image.img = image
        return image

    # Create the update function/method
    def update(self):
        self.img_label['image'] = self.get_image()
        i = 0
        for key, value in self.weather.results.items():
            if key != 'region' and key != 'img':
                if key == 'current temperature':
                    text = value.title() + '\u00b0' + ' F'
                else:
                    text = value.title()
                self.labels[i-1][1]['text'] = text
            i += 1

        self.info_container.after(5000, self.update)

def main():
    root = tk.Tk()
    root.title('Tkinter Weather App')
    root['padx'] = 5
    root['pady'] = 4
    root.resizable(False, False)
    Window(root)
    root.mainloop()

main()



RE: Tkinter Weather App - menator01 - Jan-16-2022

Added a seven day forecast
#! /usr/bin/env python3

# Do the imports
import tkinter as tk
import tkinter.font as tkfont
from bs4 import BeautifulSoup as bs
import requests
import urllib
import base64

# Create weather class to get info
class Weather:
    def __init__(self):
        # Set some vars
        LANGUAGE = 'en-US, en;q=0.5'
        USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"

        # session vars
        session = requests.Session()
        session.headers['User-Agent'] = USER_AGENT
        session.headers['Accept-Language'] = LANGUAGE
        session.headers['Content-Language'] = LANGUAGE

        # Url to google weather, get the data, use soup to parse
        url = 'https://www.google.com/search?lr=lang_en&ie=UTF-8&q=weather'
        data = session.get(url)
        soup = bs(data.text, 'html.parser')

        # Set an empty dict and grab the data we want
        self.results = {}
        self.results['region'] = soup.find('div', attrs={'id': 'wob_loc'}).text
        self.results['current temperature'] = soup.find('span', attrs={'id': 'wob_tm'}).text
        self.results['last updated'] = soup.find('div', attrs={'id': 'wob_dts'}).text
        self.results['current weather'] = soup.find('span', attrs={'id': 'wob_dc'}).text
        self.results['precipitation'] = soup.find('span', attrs={'id': 'wob_pp'}).text
        self.results['humidity'] = soup.find('span', attrs={'id': 'wob_hm'}).text
        self.results['wind speed'] = soup.find('span', attrs={'id': 'wob_ws'}).text
        self.results['img'] = soup.find('img', attrs={'class': 'wob_tci', 'id': 'wob_tci'})['src']

        # Create empty list to hold seven day data
        # Get the wanted data
        seven_day = []
        days = soup.find('div', attrs={'id': 'wob_dp'})
        for day in days.find_all('div', attrs={'class': 'wob_df'}):
            name = day.find_all('div')[0].attrs['aria-label']
            image = day.find('img')
            temp = day.find_all('span', {'class': 'wob_t'})
            high_tmp = temp[0].text
            low_tmp = temp[2].text
            seven_day.append({'name': name, 'image': image, 'high': high_tmp, 'low': low_tmp})

        # Pop off the first data as we already displaying todays forecast
        seven_day.pop(0)

        # Add the list to our dict
        self.results['seven day'] = seven_day

# Create the tkinter window class
class Window:
    def __init__(self, parent):
        # set the window parent and configure rows and columns
        self.parent = parent
        self.parent.columnconfigure(0, weight=1)
        self.parent.rowconfigure(0, weight=1)

        # Initiate the weather class
        self.weather = Weather()

        # Create the main container
        container = tk.Frame(self.parent)
        container.grid(column=0, row=0, sticky='news')
        container.grid_columnconfigure(0, weight=3)

        # Container to hold the data in our header
        header_container = tk.Frame(container)
        header_container.grid(column=0, row=0, sticky='new')
        header_container.grid_columnconfigure(1, weight=3)

        # Container for holding weather info
        self.info_container = tk.Frame(container)
        self.info_container.grid(column=0, row=1, sticky='news')
        for i in range(2):
            self.info_container.grid_columnconfigure(i, weight=3, uniform='info')

        # Container for extended forecast
        self.seven_day_container = tk.Frame(container)
        self.seven_day_container['relief'] = 'groove'
        self.seven_day_container['highlightbackground'] = 'gray'
        self.seven_day_container['highlightcolor'] = 'gray'
        self.seven_day_container['highlightthickness'] = 1
        self.seven_day_container.grid(column=0, row=2, sticky='new')
        for i in range(len(self.weather.results['seven day'])):
            self.seven_day_container.grid_columnconfigure(i, weight=3, uniform='days')


        # Label for the image
        self.img_label = tk.Label(header_container)
        self.img_label['image'] = self.get_image(self.weather.results['img'])
        self.img_label['relief'] = 'groove'
        self.img_label.grid(column=0, row=0, sticky='new')

        # Label for header text
        header = tk.Label(header_container)
        header['text'] = self.weather.results['region']
        header['font'] = tkfont.Font(size=18, weight='bold')
        header['relief'] = 'groove'
        header['padx'] = 8
        header['fg'] = 'blue'
        header.grid(column=1, row=0, sticky='news')


        # Create empty list for labels and set a counter
        self.labels = []
        i = 0

        # Loop through the dict and assign to labels
        for key, value in self.weather.results.items():
            if key != 'region' and key != 'img' and key != 'seven day':
                if key == 'current temperature':
                    text = value.title() + '\u00b0' + ' F'
                else:
                    text = value.title()
                self.labels.append([tk.Label(self.info_container, text=key.title(), anchor='w'),
                tk.Label(self.info_container, text=text, anchor='w')])
                self.labels[i][0]['font'] = tkfont.Font(weight='bold', size=10)
                self.labels[i][0]['relief'] = 'groove'
                self.labels[i][1]['relief'] = 'groove'
                self.labels[i][0].grid(column=0, row=i, sticky='new', ipadx=5)
                self.labels[i][1].grid(column=1, row=i, sticky='new', ipadx=5)
                i += 1

        # Create a header label for the extended forecast
        label = tk.Label(self.seven_day_container)
        label['text'] = 'Extended Forecast'
        label['font'] = tkfont.Font(weight='bold', size=14)
        label['bg'] = 'lightgray'
        label.grid(column=0, columnspan=len(self.weather.results['seven day']), row=0, sticky='new')

        # Create empty list to hold days
        self.days = []
        i = 0
        # Loop through the seven day forecast info
        for data in self.weather.results['seven day']:
            self.days.append(tk.Label(self.seven_day_container))
            self.days[i]['compound'] = 'bottom'
            self.days[i]['text'] = f'{data["name"]}\n H: {data["high"]}\u00b0 F / L: {data["low"]}\u00b0 F'
            self.days[i]['font'] = tkfont.Font(weight='bold', size= 8)
            self.days[i]['image'] = self.get_image(data['image']['src'])
            self.days[i]['relief'] = 'groove'
            self.days[i].grid(column=i, row=1, sticky='new')
            i += 1

        # Call self.update to update the information
        self.update()

    # function/method for getting and returning the image
    def get_image(self, src):
        url = 'https:' + src
        img = urllib.request.urlopen(url)
        raw = img.read()
        img.close()

        image = tk.PhotoImage(data=raw)
        image.img = image
        return image

    # Create the update function/method
    def update(self):
        self.img_label['image'] = self.get_image(self.weather.results['img'])
        i = 0
        for key, value in self.weather.results.items():
            if key != 'region' and key != 'img' and key != 'seven day':
                if key == 'current temperature':
                    text = value.title() + '\u00b0' + ' F'
                else:
                    text = value.title()
                self.labels[i-1][1]['text'] = text
            i += 1

        i = 0
        for img in self.weather.results['seven day']:
            self.days[i]['image'] = self.get_image(img['image']['src'])
            i += 1

        self.info_container.after(300000, self.update)

def main():
    root = tk.Tk()
    root.title('Tkinter Weather App')
    root['padx'] = 5
    root['pady'] = 4
    root.resizable(False, False)
    Window(root)
    root.mainloop()

main()