I. Preparations
No preparation required.
II. Preview
1. Activation
Automatically locate your city after startup, and display the weather of the located city.
2.Add city
3. Show multiple cities weather
After adding weather, you can display weather information for multiple cities.
III. Design Process
1. The process of obtaining urban weather information
Use this flowchart to show the process of locating city information to obtaining city weather information.
IV. Source Code
1.Weather_Tool-v1.
from tkinter import * from tkinter import ttk from PIL import Image,ImageTk from tkinter import messagebox from Weather_Spider import Weather_Get from threading import Thread import datetime import time ''' 5-1 1. open the home page to locate the current location to get the weather (ip locate+jieba split+city number+city weather) **5.11 realized ** 2.user manually select to view the weather of the currently selected city (Toplevel+Combobox) **Realized** 5-14 1.add notepad to display weather of multiple cities (Notebook Frame) **Implemented 2. Frequent refresh detection (thread timed for 10 seconds) **implemented 3. User selects the theme (Menu.add_radiobutton()) **implemented 4. A window multiple Combobox, how to handle the selection event **Has been implemented** **Has been implemented** **Has been implemented** **Has been implemented** **Has been implemented** 5-15 1. right click on the title of the notebook frame to appear "close" menu **have been cut **. 2. After user added city, label appeared in the last frame **not implemented ''' imgs=['./img/'] class App: def __init__(self): =Tk() ('Weather Forecast Widget-v1.0') width=600 height=282 left=(.winfo_screenwidth()-width)/2 top=(.winfo_screenheight()-height)/2 ('%dx%d+%d+%d'%(width,height,left,top)) ('') (False,False) self.cerate_widgets() self.first_launch() self.set_widgets() self.place_widgets() self.thread_it(self.show_local_weather) () def cerate_widgets(self): =() self.f1=Frame() =(self.f1) self.l1_var=StringVar() self.l1=(self.f1,textvariable=self.l1_var) =Menu() ['menu']= self.s1=Menu(,tearoff=False) self.s2=Menu(,tearoff=False) self.s3=Menu(,tearoff=False) def set_widgets(self): =[] style = () style.theme_use("default") columns=('rq','tq','flfx','zdqw','zgqw') (show='headings',columns=columns) (columns[0],anchor=CENTER,minwidth=95,width=110) (columns[1],anchor=CENTER,minwidth=60,width=70) (columns[2],anchor=CENTER,minwidth=90,width=100) (columns[3],anchor=CENTER,minwidth=90,width=100) (columns[4],anchor=CENTER,minwidth=90,width=100) ('rq', text='Date') ('tq', text='Weather') ('flfx', text='Wind direction wind force') ('zdqw', text='Minimum temperature') ('zgqw', text='Maximum temperature') .add_cascade(label='Start',menu=self.s1) self.s1.add_command(label='aaa',command='') self.s1.add_separator() self.s1.add_command(label='Exit',command=self.quit_window) .add_cascade(label='Operation',menu=self.s2) self.s2.add_command(label='Refresh',command=lambda:self.thread_it(self.refresh_weather)) self.s2.add_command(label='Add City',command=lambda:self.thread_it(self.select_city),state='disable') s2_sub = Menu(self.s2, tearoff=0) self.s2.add_separator() self.s2.add_cascade(label='Change theme',menu=s2_sub) .add_cascade(label='About',menu=self.s3) self.s3.add_command(label='About the Author',command=lambda :('About the Author','The author is mysterious and leaves nothing behind')) .tag_configure('evenColor',background='lightblue') ('WM_DELETE_WINDOW',self.quit_window) themes=[ 'default','clam', 'alt', 'classic'] =StringVar() for i,t in enumerate(themes): s2_sub.add_radiobutton(label=t,variable=,command=lambda:self.thread_it(self.change_theme),value=t) ('default') def place_widgets(self): (x=0,y=0,width=600,height=282) (x=0,y=0,width=600,height=150) self.(x=0,y=150,height=85,width=600) def first_launch(self): ''' First time startup, show loading image prompt message :return. ''' self.start_time=() paned = PanedWindow() = imgs img = ([0]) = (img) self.load_img = Label(, image=) self.load_lab = Label(, text='Loading...') self.load_img.pack() self.load_lab.pack() def show_local_weather(self): ''' Display location weather information :return. ''' self.l1_var.set('Refreshing the weather ......') items = .get_children() for item in items: (item) try: city,item=Weather_Get().get_local_weather() self.load_img.destroy() self.load_lab.destroy() self.('Add City', state='normal') (self.f1,text=city) i=0 for data in item['recent']: ('', i, values=( ('Date'), ('Weather'), ('Wind direction'), ('Minimum temperature'), ('Maximum temperature'))) i+=1 self.l1_var.set(f'at the present:{self.show_date()}\nCurrent Location:{city}\ncurrent temperature:{item["now"]}\nflu index:{item["ganmao"]}') except TypeError: ('Error','Weather information failed to load!') self.l1_var.set('Weather information failed to load!') self.('Add City', state='normal') def refresh_weather(self): """ Can't click refresh for 10 seconds after refreshing weather :return. """ self.('Refresh', state='disable') self.show_local_weather() self.thread_it(self.wait_time) def wait_time(self): ''' Thread timed at 10s, refresh button clickable after ten seconds :return. ''' (10) self.('Refresh', state='normal') def show_date(self): """ Display date information for weather display :return. """ date = str(()) year,month,day=('-') week_day_dict = { 0: 'Monday', 1: 'Tuesday', 2: 'Wednesday', 3: 'Thursday', 4: 'Friday', 5: 'Saturday', 6: 'Sunday ', } now=() date_index = () return f'{year}surname Nian{month}moon{day}date {week_day_dict[date_index]}' def select_city(self): ''' Toplevel lets the user select a city, and the backend gets the city number :return. ''' =Toplevel() (0,0) width=300 height=140 left=(.winfo_screenwidth()-width)/2 top=(.winfo_screenheight()-height)/2 ('%dx%d+%d+%d'%(width,height,left,top)) ('Select City') self.tl1=(,text='Please select a city:') self.() provinces=Weather_Get().get_provinces() self.tc1=(,justify='center',state='readonly',value=provinces) self.tc2=(,justify='center',state='readonly') self.() self.('<<ComboboxSelected>>',self.show_tc2_value) self.('<<ComboboxSelected>>',self.show_tc3_value) self.() self.tc3=(,justify='center',state='readonly') self.() self.tb1=(,text='Select',command=lambda :self.thread_it(self.ack_city)) self.(pady=10) #---- to be improved def ack_city(self): ''' Toplevel selected city, choose to use the notebook to create a frame to display the selected city information :return. ''' cityno=self.get_city_no() weather_item=Weather_Get().get_weather(cityno) location=self.province_name+self.city_name+ if location in : ('Warning','This city has been added, please don't add it again!') else: (location) self.f2= Frame(takefocus=True) (self.f2, text=location) self.tree2 = (self.f2) columns = ('rq', 'tq', 'flfx', 'zdqw', 'zgqw') self.(show='headings', columns=columns) self.(columns[0], anchor=CENTER, minwidth=95, width=110) self.(columns[1], anchor=CENTER, minwidth=60, width=70) self.(columns[2], anchor=CENTER, minwidth=90, width=100) self.(columns[3], anchor=CENTER, minwidth=90, width=100) self.(columns[4], anchor=CENTER, minwidth=90, width=100) self.('rq', text='Date') self.('tq', text='Weather') self.('flfx', text='Wind direction wind force') self.('zdqw', text='Minimum temperature') self.('zgqw', text='Maximum temperature') self.(x=0,y=0,width=600,height=150) # label_='label'+str(self.click_no) # label_var='label'+str(self.click_no)+'_var' self.fl1_var=StringVar() self.fl1=(self.f2,textvariable=self.fl1_var) self.(x=0,y=150,height=85,width=600) items = self.tree2.get_children() for item in items: self.(item) try: item = weather_item city=location i = 0 for data in item['recent']: self.('', i, values=( ('Date'), ('Weather'), ('Wind direction'), ('Minimum temperature'), ('Maximum temperature'))) i += 1 self.fl1_var.set(f'at the present:{self.show_date()}\nCurrent Location:{city}\ncurrent temperature:{item["now"]}\nflu index:{item["ganmao"]}') except TypeError: ('Error','Weather information failed to load!') self.fl1_var.set(f'{city}Weather information failed to load!') () def change_tab(self,*args): pass def show_tc2_value(self,event): ''' Display "City..." level information. :param event: :return: :return. ''' self.(value=[]) self.(value=[]) self.province_name=self.() cities=Weather_Get().get_cities(self.province_name) self.(value=cities) def show_tc3_value(self,event): ''' Display "District/county" level information. :param event: :return. :return. ''' self.city_name=self.() regions=Weather_Get().get_regions(self.province_name,self.city_name) self.(value=regions) def get_city_no(self): """ Get city number by province, city, district, county :return: city number """ =self.() city_no=Weather_Get().get_city_id_by_add(self.province_name,self.city_name,) return city_no def change_theme(self,): ''' Change Thread :return. ''' theme=() style = () style.theme_use(theme) def quit_window(self): ret=('Exit','Do you want to quit?') if ret: () def thread_it(self,func,*args): ''' Preventing thread conflicts :param func. :param args. :return. ''' t=Thread(target=func,args=args) (True) () if __name__ == '__main__': a=App()
2.Weather_Spider.py
#coding:utf-8 import requests import json from lxml import etree import jieba class Weather_Get(): def __init__(self): self.base_ip_url='/json' self.location_url='/' # Get China's domestic cities--number interface self.city_number_url='http://static./sktq/common/city_China.json' #Weather Checker Interface self.base_weather_url='/weather_mini?citykey={}' ={ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36', } =self.get_city_item() def request(self,url,headers): """ Request url with customizable request header :param url: the request url :param headers: customizable request headers :return: web page text data """ s=() s.keep_alive=False try: r=(url,headers=headers) ='utf-8' if r.status_code==200: = r.apparent_encoding return else: return None except : return None def get_city(self): """ Locate current city by ip :return:Location information of your province and city """ my_headers={ 'Connection': 'keep-alive', 'Host': '', 'sec-ch-ua': '"Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36', 'Upgrade-Insecure-Requests': '1' } res = ((self.location_url,headers=my_headers)) location = ('//div[@class="WhoIpWrap jspu"]//span[@class="Whwtdhalf w30-0 lh24 tl ml80"]/em/text()') # Stuttering and splitting words is so time-consuming jieba_cut_result = (''.join(location)) try: # of countries and types of networks removed from the top del jieba_cut_result[0] del jieba_cut_result[-1] item = {} # If the result is similar to Shijiazhuang Yuhua, then automatically add the city. if jieba_cut_result[0]!=jieba_cut_result[1]: item['province'] = jieba_cut_result[0] + 'City' item['city'] = jieba_cut_result[1] + "District." return item else: # If the result is similar to Beijing Beijing is automatically added to the city item['province'] = jieba_cut_result[0] + 'City' item['city'] = jieba_cut_result[1] + "City." return item except IndexError: return False def get_city_item(self): res =(self.city_number_url,headers=) item=eval("{'cities':"+res+"}") return item def get_provinces(self): province=[p for p in ['cities']] #print(province) return province def get_cities(self,province): cities_=['cities'][province] cities=[city for city in cities_.keys()] return cities def get_regions(self,province,city): regions_=['cities'][province][city] regions=[region for region in regions_.keys()] #print(province,city,regions) return regions def get_city_id_by_add(self,province,city,region=''): if region=='': city_no=['cities'][province][city][city].replace('CN','') else: city_no=['cities'][province][city][region].replace('CN','') return city_no def get_cityid(self,province,city): """ Look up the corresponding city number in the dictionary by province and city :param province: province :param city: city :return: city """ if province in ['cities'].keys(): try: # Tangshan City, Tangshan City, Hebei Province (usual province and city) number=['cities'][province].get(city).get(city).replace('CN','') return number except AttributeError: number=['cities'][province].get(province).get(city).replace('CN','') return number else: print('No information was retrieved on{}{}such information!'.format(province,city)) def get_weather(self,number): weather_data = ((self.base_weather_url.format(number),)) # (weather_data) data=weather_data['data'] item={} yesterday={} item_list=[] yesterday['Date']=data['yesterday']['date']+'(Yesterday)' item['now']=data['wendu']+'℃' item['ganmao']=data['ganmao'] yesterday['Weather']=data['yesterday']['type'] yesterday['Wind direction']=data['yesterday']['fx']+data['yesterday']['fl'].replace('<![CDATA[','').replace(']]>','') yesterday['minimum temperature']=data['yesterday']['low'].replace('Cold temperatures ','') yesterday['maximum temperature']=data['yesterday']['high'].replace('High Temperature ','') item_list.append(yesterday) count=0 for weateher in data['forecast']: item2={} if count==0: date=weateher['date']+'(Today)' elif count==1: date=weateher['date']+'(Tomorrow)' elif count==2: date=weateher['date']+'(The day after tomorrow)' else: date=weateher['date']+f'({count-1}Tin Hau (* area around the MTR station with same name))' item2['Date']=date item2['Weather'] = weateher['type'] item2['Wind direction']=weateher['fengxiang']+weateher['fengli'].replace('<![CDATA[','').replace(']]>','') item2['minimum temperature'] = weateher['low'].replace('Cold temperatures ', '') item2['maximum temperature'] = weateher['high'].replace('High Temperature ', '') item_list.append(item2) count+=1 item['recent']=item_list return item def get_local_weather(self): item=Weather_Get().get_city() if item: p=item['province'] c=item['city'] number=Weather_Get().get_cityid(p,c) weather=Weather_Get().get_weather(number) return p+c,weather else: return False
V. Summarizing
This time using Tkinter to write a weather forecast gadget, basically support every province and city of the country's weather forecast, support for historical weather (yesterday) to view, although the basic functions can be realized, but theTwo small problems remain:
1. After adding more than two cities weather, the specific city information will be displayed in the latest added Freame.
Positioning inaccuracies.
There are two other features of this program:
1. Support changing themes.
2. The first start of the program added a loading transition.
Discover the rest of the eggs for yourself!
The program is placed in theblue cloud (geology). Any shortcomings in ideas and code are welcome to be corrected and criticized!
Above is the python made weather forecast widget (gui interface) of the details, more information about python weather forecast tool please pay attention to my other related articles!