Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!
  • Guest, before posting your code please take these rules into consideration:
    • It is required to use our BBCode feature to display your code. While within the editor click < / > or >_ and place your code within the BB Code prompt. This helps others with finding a solution by making it easier to read and easier to copy.
    • You can also use markdown to share your code. When using markdown your code will be automatically converted to BBCode. For help with markdown check out the markdown guide.
    • Don't share a wall of code. All we want is the problem area, the code related to your issue.


    To learn more about how to use our BBCode feature, please click here.

    Thank you, Code Forum.

pyqt6 weather app

menator01

Gold Coder
A weather app I wrote some time ago. Think it will only work in US as it gets the data from weather.gov. Could change the weather region and it will work but, not sure. I welcome any feedback.

Python:
#! /usr/bin/env python3

from bs4 import BeautifulSoup as bs
import requests, json, re, sys
from time import strftime
from PyQt6.QtCore import (Qt, QTimer, QTime)
from PyQt6.QtGui import (QImage, QPixmap)
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QGridLayout,
QVBoxLayout, QHBoxLayout, QLabel, QFrame, QPushButton)


class Weather:
    def __init__(self):
        # Get location - lat and long
        location = json.loads(requests.get('http://ipinfo.io/json').text)['loc']
        lat, lon = location.split(',')

        # Get weather page from forecast.weather.gov
        page = requests.get(f'https://forecast.weather.gov/MapClick.php?lat={lat}&lon={lon}')

        # Create the soup
        soup = bs(page.text, 'html.parser')

        # Create some dicts for storing data
        self.data = {}
        summary = {}
        current_conditions = {}

        # Find the header we want and store in the data dict
        self.data['header'] = f"{soup.find('h2', attrs={'class':'panel-title'}).text}"

        # Find current condition summary and data
        weather = soup.find('div', attrs={'id': 'current_conditions-summary'})
        summary['img'] = f"https://forecast.weather.gov/{weather.find('img')['src']}"
        summary['condition'] = weather.find('p', attrs={'class': 'myforecast-current'}).text
        summary['temp f'] = weather.find('p', attrs={'class': 'myforecast-current-lrg'}).text
        summary['temp c'] = weather.find('p', attrs={'class': 'myforecast-current-sm'}).text

        # Find detail data from soup
        table = soup.find('div', attrs={'id': 'current_conditions_detail'})

        # Find the detail header/left td in table and add to dict as keys
        for text in table.findAll('b'):
            current_conditions[text.text] = None

        # Find the data on the right td and add to current_conditions dict
        for key in current_conditions.keys():
            for text in table.findAll('tr'):

                # Using this to get rid of excessive white space in the last dict entry
                if key in text.text.strip():
                    text = re.sub(key, '', text.text.strip())
                    current_conditions[key] = text.replace('\n', '').strip()

        # Add everything to the data dict
        self.data['summary'] = summary
        self.data['details'] = current_conditions


class Window(QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setFixedSize(500,270)

        # Iniate the Weather class
        weather = Weather()
        self.summary = weather.data['summary']
        self.details = weather.data['details']

        self.setWindowTitle('PyQt6 Weather App')

        # Create main container
        container = QGridLayout()

        cbox = QHBoxLayout()

        # Create data container
        dgrid = QGridLayout()
        dgrid.setSpacing(1)
        dgrid.setContentsMargins(2, 2, 2, 2)
        dframe = QFrame()
        dframe.setFrameStyle(1)
        dframe.setFrameShadow(QFrame.Shadow.Sunken)
        dframe.setLayout(dgrid)

        # Create the header
        header = QLabel('PyQt6 Weather App')
        header.setAlignment(Qt.AlignmentFlag.AlignCenter)
        header.setStyleSheet('font-size: 18px; background-color: lightgray; padding: 5 3 5 3; \
        font-weight: bold; border: 1px solid gray; color:steelblue;')

        # Create local header
        self.loc_header = QLabel(f'Current Conditions at {weather.data["header"]}')
        self.loc_header.setStyleSheet('font-size: 12px; color: navy; background-color: lightgray; \
        padding: 5, 3, 5, 3; border: 1px solid gray; font-weight: bold;')

        # Get first init image
        img_url = self.summary['img']
        image = QImage()
        image.loadFromData(requests.get(img_url).content)

        # Create image label
        self.img_label = QLabel()
        self.img_label.setStyleSheet('padding: 3 3 3 3;')
        self.img_label.setPixmap(QPixmap(image))


        # Create detail labels
        i = 0
        self.myvlabels = []
        hlabels = []
        for key, value in self.details.items():
            self.myvlabels.append(value)
            hlabels.append(key)
            hlabels[i] = QLabel(f'<b>{key}</b>:')
            hlabels[i].setStyleSheet('border: 1px solid lightgray; padding: 0 0 0 8;')
            self.myvlabels[i] = QLabel(value)
            self.myvlabels[i].setStyleSheet('border: 1px solid lightgray; padding 0 0 0 0;')
            dgrid.addWidget(hlabels[i], i, 1, 1, 1)
            dgrid.addWidget(self.myvlabels[i], i, 2, 1, 1)
            i += 1

        # Create current conditions label
        text = f'<b>Currently</b>: {self.summary["condition"]} {self.summary["temp f"]} / {self.summary["temp c"]}'
        self.current_label = QLabel(text)
        self.current_label.setFrameStyle(1)
        self.current_label.setFrameShadow(QFrame.Shadow.Sunken)

        # Create a clock
        self.clock = QLabel(f'Current Time: <font color="navy">{strftime("%I:%M:%S %p")}</font>')
        self.clock.setFrameStyle(1)
        self.clock.setFrameShadow(QFrame.Shadow.Sunken)

        # Compact current data and clock
        cbox.addWidget(self.current_label)
        cbox.addWidget(self.clock)

        # Add data widget to data container
        dgrid.addWidget(self.img_label, 0, 0, len(self.details), 1)

        # Add widgets to main container grid
        container.addWidget(header, 0, 0, 1, 1, Qt.AlignmentFlag.AlignTop)
        container.addWidget(self.loc_header, 1, 0, 1, 1, Qt.AlignmentFlag.AlignTop)
        container.addWidget(dframe, 2, 0, 1, 1, Qt.AlignmentFlag.AlignTop)
        container.addLayout(cbox, 3, 0, 1, 1, Qt.AlignmentFlag.AlignTop)

        # Setup the clock timer
        clock_timer = QTimer(self)
        clock_timer.timeout.connect(self.clock_update)
        clock_timer.start(1000)

        # Setup the update timer
        update_timer = QTimer(self)
        update_timer.timeout.connect(self.update)
        update_timer.start(3000000)




        widget = QWidget()
        widget.setLayout(container)
        self.setCentralWidget(widget)

    def clock_update(self):
        self.clock.setText(f'<b>Current Time</b>: <font color="navy">{strftime("%I:%M:%S %p")}</font>')

    def update(self):
        # Iniate the Weather class
        weather = Weather()

        # Set some variables
        summary = weather.data['summary']
        details = weather.data['details']

        # Update the labels
        img_url = self.summary['img']
        image = QImage()
        image.loadFromData(requests.get(img_url).content)
        self.img_label.setPixmap(QPixmap(image))

        i = 0
        for val in details.values():
            self.myvlabels[i].setText(val)
            i += 1

        self.current_label.setText(f'<b>Currently</b>: {summary["condition"]} {summary["temp f"]} / {summary["temp c"]}')

def main():
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec())

if __name__ == '__main__':
    main()
 
Tkinter version of the weather app. Pulls data from google weather

Python:
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()
 
Last edited:

New Threads

Buy us a coffee!

Back
Top Bottom