Python Forum

Full Version: Tkinter weather app
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Redone the tkinter weather app
This one should work in whatever part of the world your in. (If you have access google) The first was just in US.
import tkinter as tk
from datetime import datetime, timedelta
from bs4 import BeautifulSoup
import requests
import urllib
import base64
from PIL import Image, ImageTk
from io import BytesIO


class Data:
    pass

    def clock(self):
            ''' Return clock data '''
            current = datetime.now()
            month = current.strftime('%B')
            weekday = current.strftime('%A')
            day = current.strftime('%d')
            year = current.strftime('%Y')
            hour = current.strftime('%I')
            minute = current.strftime('%M')
            second = current.strftime('%S')
            ampm = current.strftime('%p')

            return f'{weekday} {month} {day}, {year}  {hour}:{minute}:{second} {ampm}'

    def get_weather(self):
        ''' method for getting weather data from google '''

        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"
        
        # US english
        LANGUAGE = "en-US,en;q=0.5"

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

        url = 'https://www.google.com/search?q=weather'

        html = session.get(url)

        soup = BeautifulSoup(html.text, 'html.parser')

        # Create a dict to hold the data and get wanted data
        result = {}
        result['Region'] = soup.find('span', attrs={'class': 'BBwThe'}).text
        result['Temperature'] = soup.find('span', attrs={'id': 'wob_tm'}).text + f'\u00b0 F'
        result['Conditions'] = soup.find('span', attrs={'id': 'wob_dc'}).text
        result['Humidity'] = soup.find('span', attrs={'id': 'wob_hm'}).text
        result['Percipitation'] = soup.find('span', attrs={'id': 'wob_pp'}).text
        result['Wind'] = soup.find('span', attrs={'id': 'wob_ws'}).text
        result['img'] = soup.find('img', id='wob_tci', src=True)['src']
        
        return result

    def getimg(self):
        ''' Method for getting and sizing image '''
        url = 'http:'+self.get_weather()['img']
        u = urllib.request.urlopen(url)
        raw = u.read()
        u.close()
        dat = base64.b64encode(bytes(raw))
        buff = BytesIO(base64.b64decode(dat))

        image = Image.open(buff)
        image = image.resize((160,160))
        return image


class Window:
    ''' Class for window display '''
    def __init__(self, parent):
        self.parent = parent
        self.parent.rowconfigure(0, weight=1)
        self.parent.columnconfigure(0, weight=1)

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

        self.clock = tk.Label(container, text='clock', bg='#777777', fg='black')
        self.clock.grid(column=0, row=0, sticky='new', padx=4, pady=4, ipadx=4, ipady=4)
        self.clock.configure(font=(None, 14, 'normal'))

        row1 = tk.Frame(container)
        row1.grid(column=0, row=1, sticky='news')
        for i in range(2):
            row1.grid_columnconfigure(i, weight=3, uniform='columns')

        self.left = tk.Frame(row1)
        self.left['highlightbackground'] = '#555555'
        self.left['highlightcolor'] = '#555555'
        self.left['highlightthickness'] = 1
        self.left.grid(column=0, row=0, sticky='news', pady=4, padx=(4,0))
        for i in range(2):
            self.left.grid_columnconfigure(i, weight=3, uniform='columns')

        right = tk.Frame(row1)
        right.grid(column=1, row=0, sticky='news')
        right.grid_columnconfigure(0, weight=3)

        self.img_label = tk.Label(right, bg='#555555')
        self.img_label.grid(column=0, row=0, sticky='news', padx=4, pady=(4,1))

        self.condition_label = tk.Label(right, anchor='w', bg='#555555', fg='white', padx=4)
        self.condition_label.grid(column=0, row=1, sticky='news', padx=4, pady=(1,4))


class Controller:
    ''' Controller handles communications between classes '''
    def __init__(self, data, window):
        self.data = data
        self.window = window

        # Display clock
        self.window.clock.configure(text=self.data.clock())

        # Create labels
        self.labels = []
        self.info = []
        index = 0

        for key, value in self.data.get_weather().items():
            if key != 'img':
                self.labels.append(tk.Label(self.window.left, text=f'{key}:', anchor='w'))
                self.labels[index].grid(column=0, row=index, sticky='news', padx=4, pady=1)
                self.labels[index]['font'] = None, 10, 'bold'

                self.info.append(tk.Label(self.window.left, text=value, anchor='w',font=(None, 10, 'normal')))
                self.info[index].grid(column=1, row=index, sticky='news', padx=4, pady=1)
                index += 1

        last_label = tk.Label(self.window.left, text='Last updated:', anchor='w')
        last_label['font'] = None, 10, 'bold'
        last_label.grid(column=0, row=len(self.data.get_weather())-1, sticky='new', padx=4, pady=1)
        
        # Get last update time
        self.last_data = tk.Label(self.window.left, anchor='w')
        self.last_data.grid(column=1, row=len(self.data.get_weather())-1, sticky='new', padx=4, pady=1)
        self.last_data.configure(text=datetime.now().strftime('%I:%M:%S %p'))

        next_update = tk.Label(self.window.left, text='Next update:', anchor='w', font=(None, 10, 'bold'))
        next_update.grid(column=0, row=len(self.data.get_weather()), sticky='new', padx=4, pady=1)

        # Get next update time
        update_time = datetime.now() + timedelta(minutes=5)
        self.next_data = tk.Label(self.window.left, anchor='w')
        self.next_data['text'] = update_time.strftime('%I:%M:%S %p')
        self.next_data.grid(column=1, row=len(self.data.get_weather()), sticky='new', padx=4, pady=1)
        
        # Get the image
        image = self.data.getimg()
        image = ImageTk.PhotoImage(image)
        image.bakup = image

        # Get and display image and conditions text under image
        self.window.img_label.configure(image=image)
        self.window.condition_label.configure(text=self.data.get_weather()['Conditions'])
                
        #Call the updates
        self.update()
        self.update_data()

    def update(self):
        ''' Methos updates the clock '''
        self.window.clock.configure(text=self.data.clock())
        self.window.parent.after(1000, self.update)
        

    def update_data(self):
        ''' Method updates all other information '''
        data = self.data.get_weather()
        index = 0
        for key, value in data.items():
            if key != 'img':
                self.info[index].configure(text=value)
                index += 1
                
        image = self.data.getimg()
        image = ImageTk.PhotoImage(image)
        image.bakup = image
        self.window.img_label.configure(image=image)

        self.window.condition_label.configure(text=self.data.get_weather()['Conditions'])
        
        self.last_data.configure(text=datetime.now().strftime('%I:%M:%S %p'))
        time = datetime.now() + timedelta(minutes=5)
        self.next_data.configure(text=time.strftime('%I:%M:%S %p'))

        self.window.parent.after(300000, self.update_data)


if __name__ == '__main__':
    root = tk.Tk()
    root.resizable(False, False)
    controller = Controller(Data(), Window(root))
    root.mainloop()