今天我們做一個(gè)基于Python3 + PyQt5的一個(gè)天氣預(yù)報(bào),先來看一下效果圖:
整體界面比較簡陋充坑,但是這里我們主要把功能實(shí)現(xiàn)就行了捻爷。
工具準(zhǔn)備:
- Python3.x
運(yùn)行本例代碼還需要裝幾個(gè)Python3的模塊:
$ pip install pyqt5
$ pip install bs4
$ pip install lxml
- 開發(fā)環(huán)境呢份企,我這里用的VSCode,補(bǔ)一張效果圖(如果感興趣的話可以參看我的Python3開發(fā)環(huán)境配置):
正文開始
1.天氣信息爬取
在制作天氣預(yù)報(bào)信息顯示時(shí)甜紫,我們首先需要獲取預(yù)報(bào)信息囚霸,這里我們準(zhǔn)備從中國天氣網(wǎng)爬取我們所需要的天氣信息激才。具體思路是:
- 獲取所在城市碼和泌,構(gòu)造爬取鏈接
- 使用urllib獲取網(wǎng)站html源碼
- 分析html源碼,獲取我們所需的天氣信息
大概就上面三條傲绣,分析html源碼我們使用bs4包所提供的BeautifulSoup進(jìn)行揣云,關(guān)于BeautifulSoup的使用參見[腳本之家——Python中使用Beautiful Soup庫的超詳細(xì)教程]邓夕。
- 獲取城市碼
打開中國天氣網(wǎng),輸入你所在的城市名焚刚,打開后點(diǎn)擊今天,注意瀏覽器上的鏈接地址抢肛。我輸入的城市是西安碳柱,所以地址是:
http://www.weather.com.cn/weather1d/101110101.shtml
其中101110101就是西安的城市碼,其他城市以此類推福稳。 - 使用urllib獲取網(wǎng)站html源碼
from urllib import request
city_code = "101110101"
req = request("http://www.weather.com.cn/weather1d/" + city_code + ".shtml")
# 將網(wǎng)頁數(shù)據(jù)解碼為utf-8字符集
html = req.read().decode('utf-8')
# 為了節(jié)省調(diào)試時(shí)間的圆,我們這里直接將網(wǎng)頁源碼保存至本地文件tmp.html中
f = open("tmp.html", "w", encoding="utf-8")
f.write(html)
f.close()
- 分析html源碼半火,獲取我們所需的天氣信息
from bs4 import BeautifulSoup
# 我們使用lxml解析器
soup = BeautifulSoup(open("tmp.html", "r", encoding="utf-8"), "lxml")
# 使用字典保存我們所獲取的兩組數(shù)據(jù)
weather = {}
weather['day_wea'] = soup.select('div.t > ul.clearfix > li > p.wea')[0].text
weather['night_wea'] = soup.select('div.t > ul.clearfix > li > p.wea')[1].text
weather['day_tem'] = soup.select('div.t > ul.clearfix > li > p.tem > span')[0].text
weather['night_tem'] = soup.select('div.t > ul.clearfix > li > p.tem > span')[1].text
print(weather)
>>> {'day_wea': '陣雨', 'night_wea': '陣雨', 'day_tem': '30', 'night_tem': '22'}
我們所需要的天氣信息和溫度信息已經(jīng)獲取到了,原始資料準(zhǔn)備妥當(dāng)梅掠,開始構(gòu)建我們的圖形顯示界面
2. 構(gòu)造圖形界面
我們獲取的天氣信息有兩組店归,分別是當(dāng)日白天天氣信息、當(dāng)日夜間天氣信息挠蛉,我們準(zhǔn)備用3個(gè)QHBoxLayout肄满、1個(gè)QVBoxLayout、6個(gè)QLabel控件完成信息的顯示掰担。
布局如下:
白天 | 夜間 |
---|---|
天氣圖標(biāo) | 天氣圖標(biāo) |
預(yù)報(bào)+溫度 | 預(yù)報(bào)+溫度 |
窗口實(shí)現(xiàn)代碼如下:
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.img_label = QLabel()
self.wea_label = QLabel()
pal = QPalette()
pal.setColor(QPalette.WindowText, Qt.white)
font = QFont("微軟雅黑", 10)
# 標(biāo)題Layout
title_layout = QHBoxLayout()
self.day_title = QLabel('白天')
self.night_title = QLabel('夜間')
title_layout.addWidget(self.day_title)
title_layout.addWidget(self.night_title)
# 設(shè)置字體顏色為白色
self.day_title.setPalette(pal)
self.night_title.setPalette(pal)
# 設(shè)置對(duì)齊方式為居中對(duì)齊
self.day_title.setAlignment(Qt.AlignHCenter)
self.night_title.setAlignment(Qt.AlignHCenter)
# 設(shè)置顯示字體
self.day_title.setFont(font)
self.night_title.setFont(font)
# 天氣圖標(biāo)Layout
img_layout = QHBoxLayout()
self.day_img_label = QLabel()
self.night_img_label = QLabel()
img_layout.addWidget(self.day_img_label)
img_layout.addWidget(self.night_img_label)
# 天氣信息Layout
wea_layout = QHBoxLayout()
self.day_wea_label = QLabel()
self.night_wea_label = QLabel()
wea_layout.addWidget(self.day_wea_label)
wea_layout.addWidget(self.night_wea_label)
self.day_wea_label.setPalette(pal)
self.night_wea_label.setPalette(pal)
# 文本居中對(duì)齊
self.day_wea_label.setAlignment(Qt.AlignHCenter)
self.night_wea_label.setAlignment(Qt.AlignHCenter)
self.day_wea_label.setFont(font)
self.night_wea_label.setFont(font)
layout = QVBoxLayout(self)
layout.addLayout(title_layout)
layout.addLayout(img_layout)
layout.addLayout(wea_layout)
self.setLayout(layout)
# 設(shè)置窗口屬性带饱,去除系統(tǒng)標(biāo)題欄勺疼、隱藏狀態(tài)欄、置頂顯示
self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool | Qt.WindowStaysOnTopHint)
# 設(shè)置窗口背景透明
self.setAttribute(Qt.WA_TranslucentBackground)
self.setAutoFillBackground(False)
# 重新調(diào)整窗口大小
self.resize(300, 200)
# 用于移動(dòng)窗口
self.__x_offset = 0
self.__y_offset = 0
def mousePressEvent(self, event):
""" 父類方法重寫酪耕,用于實(shí)現(xiàn)窗口隨鼠標(biāo)拖動(dòng) """
self.__x_offset = event.globalX() - self.pos().x()
self.__y_offset = event.globalY() - self.pos().y()
def mouseMoveEvent(self, event):
""" 父類方法重寫轨淌,用于實(shí)現(xiàn)窗口隨鼠標(biāo)拖動(dòng) """
self.move(event.globalX() - self.__x_offset, event.globalY() - self.__y_offset)
def update_weather(self, wea):
""" 更新天氣顯示 """
pixmap = QPixmap(self.__get_images__('day_' + wea['day_wea']))
self.day_img_label.setPixmap(pixmap)
self.day_wea_label.setText('%s %s℃' % (wea['day_wea'], wea['day_tem']))
pixmap = QPixmap(self.__get_images__('night_' + wea['night_wea']))
self.night_img_label.setPixmap(pixmap)
self.night_img_label.resize(pixmap.size())
self.night_wea_label.setText('%s %s℃' % (wea['night_wea'], wea['night_tem']))
def __get_images__(self, wea_str):
imags = {"day_陣雨":':/images/day_shower.png', "night_陣雨":":/images/night_shower.png",
"day_多云":':/images/day_cloudy.png', "night_多云":":/images/night_cloudy.png",
"day_晴":':/images/day_sunny.png', "night_晴":":/images/night_sunny.png",
}
return imags[wea_str]
我們?cè)谏厦嫣砑恿藘蓚€(gè)新的方法update_weather(wea)递鹉,我們第一步爬取到的天氣信息輸入給這個(gè)方法,即可對(duì)界面顯示元素進(jìn)行更新却盘。還有一個(gè)是get_images(wea_str),當(dāng)獲取到具體的當(dāng)日天氣后傳參給這個(gè)函數(shù)將返回當(dāng)前天氣信息的圖標(biāo)名稱谷炸。PS:我太懶了禀挫,所以我只做了陣雨拓颓、多云、晴三種天氣信息砰左,剩下的按照需求自己完成就行场航。
- 為了使我們的天氣信息能夠自動(dòng)更新,我們將每隔1小時(shí)對(duì)中國天氣網(wǎng)進(jìn)行一次天氣信息解析僻造,為此我們寫了個(gè)線程用于完成這個(gè)功能。
# 城市碼
city_codes = {'西安':'101110101'}
def __init__(self, win):
super(WeatherThread, self).__init__()
self.__win = win
def run(self):
while True:
url = self.get_url('西安')
html = self.get_html(url)
wea = self.get_data(html)
self.__win.update_weather(wea)
print(wea)
# 等待1小時(shí)
time.sleep(3600)
def get_url(self, city):
url = 'http://www.weather.com.cn/weather1d/' + self.city_codes[city] + '.shtml'
return url
def get_html(self, url):
req = request.urlopen(url)
html = req.read().decode('utf-8')
return html
def get_data(self, html):
weather = {}
soup = BeautifulSoup(html, 'lxml')
weather['day_wea'] = soup.select('div.t > ul.clearfix > li > p.wea')[0].text
weather['night_wea'] = soup.select('div.t > ul.clearfix > li > p.wea')[1].text
weather['day_tem'] = soup.select('div.t > ul.clearfix > li > p.tem > span')[0].text
weather['night_tem'] = soup.select('div.t > ul.clearfix > li > p.tem > span')[1].text
return weather
城市碼我只弄了一種就是西安的髓削,剩下的自己按照我之前介紹的方法獲取就行了立膛。
- 圖片我是在懶人圖庫上找的梯码,圖片我使用Qt資源進(jìn)行存儲(chǔ)好啰,貼上qrc代碼:
<RCC version="1.0">
<qresource prefix="images">
<file alias="day_shower.png">./images/shower2.png</file>
<file alias="night_shower.png">./images/shower2_night.png</file>
<file alias="day_cloudy.png">./images/cloudy3.png</file>
<file alias="night_cloudy.png">./images/cloudy3_night.png</file>
<file alias="night_sunny.png">./images/sunny_night.png</file>
<file alias="day_sunny.png">./images/sunny.png</file>
</qresource>
</RCC>
使用pyrcc5進(jìn)行資源的處理:
$ pyrcc5 images.qrc -o images.py
最后添上我們的程序入口:
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
wea_thread = WeatherThread(win)
wea_thread.start()
sys.exit(app.exec())
哦對(duì)了坎怪,我列一下這個(gè)程序所用到的模塊:
import sys, time
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QHBoxLayout
from PyQt5.QtGui import QFont, QPixmap, QColor, QPalette
from PyQt5.QtCore import Qt, QThread
from bs4 import BeautifulSoup
from urllib import request
from images import *
本例所有代碼已上傳至github搅窿,有興趣的朋友可以pull下來。
https://github.com/xtinyd/weather.git
結(jié)束語
在PyQt5上我也只是個(gè)菜鳥男应,出這個(gè)系列教程的目的呢娱仔,還是想讓自己學(xué)到的東西能夠用上,不至于學(xué)起來那么盲目耐朴,另外也希望可以幫助想學(xué)習(xí)PyQt5的你盹憎。附上我的座右銘:
沒有什么是學(xué)習(xí)學(xué)不來的。
——skyloveraining