python3GUI--天气预报小工具By:PyQt5(附源码)
@
之前用tk写过一款python3GUI--天气预报小工具实现了所在天气定位,以及指定城市天气预报的查询,这次使用PyQt5在之前tk的基础上加以改进,虽然功能没有新的增加,但是软件整体速度上有明显的变化,开整吧。
一.准备工作
基于PyQt5的QT设计师,安装、配置详见:
二.预览
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(附源码)的更多相关文章
- 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生
[转].NET(C#):浅谈程序集清单资源和RESX资源 目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...
- (原创)通用查询实现方案(可用于DDD)[附源码] -- 简介
[声明] 写作不易,转载请注明出处(http://www.cnblogs.com/wiseant/p/3985353.html). [系列文章] 通用查询实现方案(可用于DDD)[附源码] -- ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(2)-easyui构建前端页面框架[附源码]
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(2)-easyui构建前端页面框架[附源码] 开始,我们有了一系列的解决方案,我们将动手搭建新系统吧. 用 ...
- 极限挑战—C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码)
原文:极限挑战-C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码) 实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本实验将使用5中方法完成这个过程,并详细记录各种方 ...
- Matlab.NET混合编程技巧之——直接调用Matlab内置函数(附源码)
原文:[原创]Matlab.NET混合编程技巧之--直接调用Matlab内置函数(附源码) 在我的上一篇文章[原创]Matlab.NET混编技巧之——找出Matlab内置函数中,已经大概的介绍了mat ...
- swfupload多文件上传[附源码]
swfupload多文件上传[附源码] 文件上传这东西说到底有时候很痛,原来的asp.net服务器控件提供了很简单的上传,但是有回传,还没有进度条提示.这次我们演示利用swfupload多文件上传,项 ...
- 聊天系统Demo,增加Silverlight客户端(附源码)-- ESFramework 4.0 快速上手(09)
在ESFramework 4.0 快速上手 -- 入门Demo,一个简单的IM系统(附源码)一文中,我们介绍了使用ESFramework的Rapid引擎开发的winform聊天程序,本文我们将在之前d ...
- openlayers4 入门开发系列之地图导航控件篇(附源码下载)
前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...
- 超详细的php用户注册页面填写信息完整实例(附源码)
这篇文章主要介绍了一个超详细的php用户注册页面填写信息完整实例,内容包括邮箱自动匹配.密码强度验证以及防止表单重复等,小编特别喜欢这篇文章,推荐给大家. 注册页面是大多数网站必备的页面,所以很有必要 ...
- 日志组件Log2Net的介绍和使用(附源码开源地址)
Log2Net是一个用于收集日志到数据库或文件的组件,支持.NET和.NetCore平台. 此组件自动收集系统的运行日志(服务器运行情况.在线人数等).异常日志.程序员还可以添加自定义日志. 该组件支 ...
随机推荐
- mysql表关联更新
UPDATE enterprise_test t1, enterprise_development_relation t2 SET t1.development_area_id = t2.develo ...
- 思科IPsecVPN建立
实验拓扑 实验目标: 1.不配置中间的三个路由器的路由实现router0和router2的vpn隧道 2.PC0能够ping通PC1 实验IP预定: PC0 10.1.1.1/24 PC1 20.1. ...
- wsl 的 tail -f 不好使
windows 上的sub linux ubuntu 的tail -f 不好使 在/etc/profile 中加入alias alias tailf='tail -f ---disable-inoti ...
- 使用Swagger和OpenAPI 3规范定义API接口并集成到SpringBoot
1. OpenAPI 3 规范介绍及属性定义 参考官方定义:https://swagger.io/specification/ 2. 使用OpenAPI 3规范定义API接口 官方样例参考:https ...
- 服务器做bond,交换机做port-channel
端口绑定的种类(具体介绍网上很多参考) balance-rr or 0 active-backup or 1 balance-xor or 2 broadcast or 3 802.3ad or 4 ...
- HTML学习笔记3----制作一个简易网站
随笔记录方便自己和同路人查阅. #------------------------------------------------我是可耻的分割线--------------------------- ...
- Chrome(谷歌浏览器)安装Vue插件vue-devtools
安装步骤如下:1.首先给大家提供一个git地址,虽然官网也有地址(https://github.com/vuejs/vue-devtools.git),我认为不太好用给大家提供另一个git地址: ht ...
- laravel 软删除的使用
1.模型层 引用类use Illuminate\Database\Eloquent\SoftDeletes;class类中引用软删除use SoftDeletes;然后执行正常的删除,列表已经不显示, ...
- 每日一抄 Go语言等待组
package main import ( "fmt" "net/http" "sync" ) /* Go语言除了可以使用通道(channe ...
- idea引入ojdbc包报错
网上下载或者让同事传两个jar过来,ojdbc6-11.2.0.7.0.jar 以及jconn3.0.jar 放入同一个文件夹中: 在此文件夹中cmd如下:回车,进入cmd控制台. 输入如下两个命令: ...