Python Forum
Tkinter Weather App
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Tkinter Weather App
#1
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()
Axel_Erfurt likes this post
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#2
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()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  PyQt6 Version of weather app menator01 3 3,233 Jan-30-2022, 12:32 AM
Last Post: menator01
  Meteostat - Historical Weather and Climate Data clampr 1 3,698 May-25-2021, 04:32 PM
Last Post: Gribouillis
  Talking Weather b4iknew 0 2,123 Jan-31-2019, 08:42 PM
Last Post: b4iknew
  SCIKItlearn -Naive Bayes Accuracy (Weather Data) usman 0 3,314 Nov-07-2018, 05:25 PM
Last Post: usman
  A weather program. mcmxl22 0 2,859 Jul-30-2018, 03:19 AM
Last Post: mcmxl22

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020