python3GUI--天气预报小工具By:PyQt5(附源码)

2023-03-18,,

@

目录
一.准备工作
二.预览
1.启动
2.添加城市
三.设计流程
1.UI设计(草图)
2.UI设计(QT设计师)
3.解释
四.源代码
五.总结


之前用tk写过一款python3GUI--天气预报小工具实现了所在天气定位,以及指定城市天气预报的查询,这次使用PyQt5在之前tk的基础上加以改进,虽然功能没有新的增加,但是软件整体速度上有明显的变化,开整吧。

一.准备工作

基于PyQt5的QT设计师,安装、配置详见:

PyCharm安装PyQt5及其工具(Qt Designer、PyUIC、PyRcc)详细教程

二.预览

1.启动

启动后,会自动定位当前所在城市,展示所在城市前后五天的天气信息。

2.添加城市

点击“添加城市”,向主界面添加城市,遂展示所选城市天气信息,每个选项卡是能够关闭的,工具栏可以自由移动。

三.设计流程

天气数据还是基于spider,重点在于界面的设计以及信号和槽的使用。

1.UI设计(草图)

整体由QToolBar、QTabWidget、QTableWidget、Qlabel组成

2.UI设计(QT设计师)

3.解释

这里解释一下,为什么有些组件最后没有展示:首次打开软件时,软件进行定位,会显示一张图片一个进度条以及一个带loading的标签,这时候QTableWidget是隐藏的,整体布局为垂直布局,当定位完成后,将QTableWidget设为可见并载入数据,loading结束,隐藏图片、进度条、以及加载提示,整体仍为垂直布局。

四.源代码

这里放的是UI与爬虫的交互代码

#-*-coding:utf-8-*-
import sys
import datetime
import threading
import webbrowser
from PyQt5.uic import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt as qqt
from Weather_Spider import Weather_Get
import add_city
import weather """
天气信息刷新时,label不能更新**已解决**
“添加城市”窗口,关闭后,主窗口不可用setEnable(True)**未解决** **0914已解决** 使用自定义信号槽
#天气信息待加载,与label不能对应______________________**已解决** 热键注册
"""
class add_city_window(QWidget):
Signal_parp = pyqtSignal(bool)
def __init__(self):
# 信号的定义
super().__init__()
#这里有些不解,为什么调用时,要用两侧add_ui
self.add_ui=add_city.Ui_add_city_window()
self.add_ui.setupUi(self) def closeEvent(self, event):
self.close()
self.Signal_parp.emit(True) class Weather_Report(QMainWindow):
Signal_parp = pyqtSignal(str)
def __init__(self):
super().__init__()
self.first_start_flag=True
self.tab_index=0
self.label_widget_list=[]
self.table_widget_list=[]
self.ui =weather.Ui_MainWindow()
self.ui.setupUi(self)
self.setFixedSize(self.width(), self.height())#禁止最大化
self.setFixedSize(695, 445) # 天气信息加载成功之后,窗口的大小
self.adjustSize()
self.ui.tabWidget.hide()
self.ui.action_open_China_weather.setEnabled(False)
self.ui.action_add_city.setEnabled(False)
self.W=Weather_Get()
self.current_china_weather_url=''
self.city_number_list=[]
self.city_list=[]
self.ui.action_open_China_weather.triggered.connect(self.open_china_weather_web)
self.ui.actiont_quit_window.triggered.connect(self.close)
self.ui.action_refresh.triggered.connect(self.refreash_weather_infos)
self.ui.action_about_author.triggered.connect(self.show_about_author)
self.ui.action_add_city.triggered.connect(self.do_select_city)
self.ui.tabWidget.tabCloseRequested.connect(self.close_tab)
self.ui.tabWidget.currentChanged.connect(self.change_index)
self.thread_it(self.show_local_weather) def change_main_ui_status(self, status):
self.setEnabled(status) def show_local_weather(self):
'''
展示定位天气信息
:return:
'''
self.ui.label_weather_infos.setText('正在刷新......')
self.ui.tableWidget.clearContents()
try:
if self.first_start_flag:
city,item,number=self.W.get_local_weather()
self.local_city_number=number
self.local_city_=city
else:
item=self.W.get_weather(self.local_city_number)
city=self.local_city_
number=self.local_city_number
self.ui.tabWidget.setTabText(0,city) #将默认的定位更改为当前所在城市名
datas = item['recent']
self.ui.action_open_China_weather.setEnabled(True)
self.ui.action_add_city.setEnabled(True)
self.ui.label_loading_pic.hide()
self.ui.label_loading_now.hide()
self.ui.BlueProgressBar.hide()
self.ui.tabWidget.setVisible(True)
self.ui.label_weather_infos.setVisible(True)
for index,data in enumerate(datas):
newItem = QTableWidgetItem(data["日期"])
newItem.setTextAlignment(Qt.AlignCenter )
self.ui.tableWidget.setItem(index, 0, newItem)
newItem = QTableWidgetItem(data["天气"])
newItem.setTextAlignment(Qt.AlignCenter )
self.ui.tableWidget.setItem(index, 1, newItem)
newItem = QTableWidgetItem(data["风力风向"])
newItem.setTextAlignment(Qt.AlignCenter)
self.ui.tableWidget.setItem(index, 2, newItem)
newItem = QTableWidgetItem(data["最低气温"])
newItem.setTextAlignment(Qt.AlignCenter )
self.ui.tableWidget.setItem(index, 3, newItem)
newItem = QTableWidgetItem(data["最高气温"])
newItem.setTextAlignment(Qt.AlignCenter)
self.ui.tableWidget.setItem(index, 4, newItem)
self.ui.tableWidget.setColumnWidth(0, 160)
self.ui.tableWidget.setColumnWidth(4, 125)
now_time = str(datetime.datetime.now()).split('.')[0].split(' ')[-1]
self.ui.label_weather_infos.setText(f'今天:{self.show_date()}\n当前所在地区:{city}\n当前气温:{item["now"]}({now_time}更新)\n感冒指数:{item["ganmao"]}')
#将定位城市加入 已展示城市列表self.location中
self.current_china_weather_url= f'http://www.weather.com.cn/weather/{number}.shtml'
if self.first_start_flag:
self.city_number_list.append(number)
self.city_list.append(city)
self.first_start_flag=False
except TypeError:
QMessageBox.warning(self,'错误','天气信息加载失败!')
self.statusBar().showMessage('天气信息加载失败!', 3000)
self.s2.entryconfig('添加城市', state='normal') def show_date(self):
"""
展示日期信息,便于天气展示
:return:
"""
date = str(datetime.date.today())
year,month,day=date.split('-')
week_day_dict = {
0: '星期一',
1: '星期二',
2: '星期三',
3: '星期四',
4: '星期五',
5: '星期六',
6: '星期日 ',
}
now=datetime.datetime.now()
date_index = now.weekday()
date_time=f'{year}年{month}月{day}日 {week_day_dict[date_index]}'
return date_time def do_select_city(self):
#选择省份 城市 所在地
self.setEnabled(False)
self.add_ui=add_city_window()
self.add_ui.Signal_parp.connect(self.change_main_ui_status)
self.add_ui.setFixedSize(self.add_ui.width(), self.add_ui.height())#禁止最大化
provences=self.W.get_provinces()
self.add_ui.add_ui.comboBox_provence.addItem('--请选择--')
self.add_ui.add_ui.comboBox_provence.addItems(provences)
self.add_ui.add_ui.comboBox_provence.currentIndexChanged.connect(self.get_citys)
self.add_ui.add_ui.comboBox_city.currentIndexChanged.connect(self.get_regions)
self.add_ui.add_ui.pushButton_add_the_city.clicked.connect(self.do_add_city)
self.add_ui.show() def closeEvent(self,event):
reply = QMessageBox.question(self, '关闭', "确定要退出吗?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
# 判断返回值,如果点击的是Yes按钮,我们就关闭组件和应用,否则就忽略关闭事件
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore() def get_citys(self):
self.add_ui.add_ui.comboBox_city.clear()
self.add_ui.add_ui.comboBox_region.clear()
self.curr_provence=self.add_ui.add_ui.comboBox_provence.currentText()
ciyies=self.W.get_cities(self.curr_provence)
self.add_ui.add_ui.comboBox_city.addItems(ciyies) def get_regions(self):
try:
self.add_ui.add_ui.comboBox_region.clear()
self.curr_city=self.add_ui.add_ui.comboBox_city.currentText()
ciyies=self.W.get_regions(self.curr_provence,self.curr_city)
self.add_ui.add_ui.comboBox_region.addItems(ciyies)
except KeyError:
pass def do_add_city(self):
if self.add_ui.add_ui.comboBox_provence.currentText()=='--请选择--':
QMessageBox.warning(self,'警告','请选择城市!')
else:
self.curr_region=self.add_ui.add_ui.comboBox_region.currentText()
self.curr_city_no=0
if self.curr_region!='':
self.curr_city_data=self.curr_provence+self.curr_city+self.curr_region
self.curr_city_no=self.W.get_city_id_by_add(self.curr_provence,self.curr_city,self.curr_region)
else:
self.curr_city_data=self.curr_provence+self.curr_city
self.curr_city_no=self.W.get_city_id_by_add(self.curr_provence,self.curr_city)
if self.curr_city_no==0:
QMessageBox.information(self,"提示",'未找到相关城市天气信息,请尝试更换城市!')
else:
if self.curr_city_no in self.city_number_list:
QMessageBox.warning(self, "警告", '此城市已经添加,请勿重复添加!')
else:
self.tab=QWidget(self)
self.ui.tabWidget.addTab(self.tab,self.curr_city_data)
tble_widget_new=QTableWidget(self.ui.tabWidget)
tble_widget_new.setEnabled(True)
tble_widget_new.setContextMenuPolicy(qqt.NoContextMenu)#没有右键菜单
tble_widget_new.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContentsOnFirstShow)#自动添加滚动条
tble_widget_new.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)#不可编辑
tble_widget_new.setAlternatingRowColors(True)
tble_widget_new.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)#选择模式:单选
tble_widget_new.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)#选择行为:整行选择
tble_widget_new.setTextElideMode(qqt.ElideMiddle)#省略号出现在长文本中间
tble_widget_new.setShowGrid(True)#显示网格
tble_widget_new.setGridStyle(qqt.SolidLine)#网格风格
tble_widget_new.setWordWrap(True)
tble_widget_new.setRowCount(5)
tble_widget_new.setColumnCount(5)
tble_widget_new.horizontalHeader().setCascadingSectionResizes(False)
tble_widget_new.horizontalHeader().setDefaultSectionSize(95)
tble_widget_new.horizontalHeader().setMinimumSectionSize(30)
tble_widget_new.verticalHeader().setVisible(False)
tble_widget_new.verticalHeader().setCascadingSectionResizes(False)
tble_widget_new.verticalHeader().setDefaultSectionSize(36)
tble_widget_new.verticalHeader().setMinimumSectionSize(30)
tble_widget_new.verticalHeader().setSortIndicatorShown(False)
tble_widget_new.verticalHeader().setStretchLastSection(True)
tble_widget_new.horizontalHeader().setStretchLastSection(True)
tble_widget_new.setColumnWidth(0, 160)
tble_widget_new.setColumnWidth(4, 125)
tble_widget_new.setHorizontalHeaderLabels(['日期', '天气', '风向风力', '最低气温', '最高气温'])
new_label=QLabel(self)
# 渲染到页面
Layout = QVBoxLayout(self.tab)
Layout.setContentsMargins(0, 0, 0, 0)
self.setEnabled(True)
self.add_ui.close()#关闭“添加城市”窗口
weather_infos=self.W.get_weather(self.curr_city_no)
datas = weather_infos['recent']
for index, data in enumerate(datas):
newItem = QTableWidgetItem(data["日期"])
newItem.setTextAlignment(Qt.AlignCenter)
tble_widget_new.setItem(index, 0, newItem)
newItem = QTableWidgetItem(data["天气"])
newItem.setTextAlignment(Qt.AlignCenter)
tble_widget_new.setItem(index, 1, newItem)
newItem = QTableWidgetItem(data["风力风向"])
newItem.setTextAlignment(Qt.AlignCenter)
tble_widget_new.setItem(index, 2, newItem)
newItem = QTableWidgetItem(data["最低气温"])
newItem.setTextAlignment(Qt.AlignCenter)
tble_widget_new.setItem(index, 3, newItem)
newItem = QTableWidgetItem(data["最高气温"])
newItem.setTextAlignment(Qt.AlignCenter)
tble_widget_new.setItem(index, 4, newItem)
tble_widget_new.setColumnWidth(0, 160)
tble_widget_new.setColumnWidth(4, 162)
now_time = str(datetime.datetime.now()).split('.')[0].split(' ')[-1]
new_label.setText(
f'今天:{self.show_date()}\n当前所选地区:{self.curr_city_data}\n当前气温:{weather_infos["now"]}({now_time}更新)\n感冒指数:{weather_infos["ganmao"]}')
# 将定位城市加入 已展示城市列表self.location中
Layout.addWidget(tble_widget_new)
Layout.addWidget(new_label)
self.label_widget_list.append(new_label)
self.table_widget_list.append(tble_widget_new)
self.city_number_list.append(self.curr_city_no)
self.city_list.append(self.curr_city) def thread_it(self,func,*args):
'''
防止线程冲突
:param func:
:param args:
:return:
'''
t=threading.Thread(target=func,args=args)
t.setDaemon(True)
t.start() def refreash_weather_infos(self):
if self.tab_index==0:
self.ui.label_weather_infos.setText('正在刷新天气信息......')
self.thread_it(self.show_local_weather)
else:
curr_city_no=self.city_number_list[self.tab_index]
table_widget=self.table_widget_list[self.tab_index-1]
new_label=self.label_widget_list[self.tab_index-1]
table_widget.clearContents()
weather_infos = self.W.get_weather(curr_city_no)
weather_data=weather_infos['recent']
for index, data in enumerate(weather_data):
newItem = QTableWidgetItem(data["日期"])
newItem.setTextAlignment(Qt.AlignCenter)
table_widget.setItem(index, 0, newItem)
newItem = QTableWidgetItem(data["天气"])
newItem.setTextAlignment(Qt.AlignCenter)
table_widget.setItem(index, 1, newItem)
newItem = QTableWidgetItem(data["风力风向"])
newItem.setTextAlignment(Qt.AlignCenter)
table_widget.setItem(index, 2, newItem)
newItem = QTableWidgetItem(data["最低气温"])
newItem.setTextAlignment(Qt.AlignCenter)
table_widget.setItem(index, 3, newItem)
newItem = QTableWidgetItem(data["最高气温"])
newItem.setTextAlignment(Qt.AlignCenter)
table_widget.setItem(index, 4, newItem)
now_time = str(datetime.datetime.now()).split('.')[0].split(' ')[-1]
new_label.setText(
f'今天:{self.show_date()}\n当前所选地区:{self.curr_city_data}\n当前气温:{weather_infos["now"]}({now_time}更新)\n感冒指数:{weather_infos["ganmao"]}') def open_china_weather_web(self):
webbrowser.open(self.current_china_weather_url) def close_tab(self,index):
if self.ui.tabWidget.count()>1:
self.ui.tabWidget.removeTab(index)
#同步更新两个列表
self.city_number_list.pop(index)
self.city_list.pop(index)
else:
self.close() def change_index(self,index):
"""
tabwidget 索引发生改变触发的事件,
改变当前中国天气URL地址
:param index: 当前tab所选索引
:return:
"""
self.current_china_weather_url= f'http://www.weather.com.cn/weather/{self.city_number_list[index]}.shtml'
self.tab_index=index def show_about_author(self):
QMessageBox.information(self,'关于','作者:懷淰メ\nBy:PyQT5') if __name__ == '__main__':
app=QApplication(sys.argv)
ui=Weather_Report()
ui.show()
sys.exit(app.exec_())

五.总结

QT设计师是真的好用,帮助我少写了很多的代码,算了一下,这个界面大概少写了300行代码,大部分时间都花在了界面的设计以及界面交互槽函数的实现,对比tk,QT确实强大!今后我还要多加练习,实现更多复杂的功能!

软件打包好,放在了蓝奏云。思路、代码方面有什么不足欢迎各位大佬指正、批评!能点个赞给我个鼓励吗?

python3GUI--天气预报小工具By:PyQt5(附源码)的相关教程结束。

《python3GUI--天气预报小工具By:PyQt5(附源码).doc》

下载本文的Word格式文档,以方便收藏与打印。