@


之前用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(附源码)的更多相关文章

  1. 【转】.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 ...

  2. (原创)通用查询实现方案(可用于DDD)[附源码] -- 简介

    [声明] 写作不易,转载请注明出处(http://www.cnblogs.com/wiseant/p/3985353.html).   [系列文章] 通用查询实现方案(可用于DDD)[附源码] -- ...

  3. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(2)-easyui构建前端页面框架[附源码]

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(2)-easyui构建前端页面框架[附源码] 开始,我们有了一系列的解决方案,我们将动手搭建新系统吧. 用 ...

  4. 极限挑战—C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码)

    原文:极限挑战-C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码) 实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本实验将使用5中方法完成这个过程,并详细记录各种方 ...

  5. Matlab.NET混合编程技巧之——直接调用Matlab内置函数(附源码)

    原文:[原创]Matlab.NET混合编程技巧之--直接调用Matlab内置函数(附源码) 在我的上一篇文章[原创]Matlab.NET混编技巧之——找出Matlab内置函数中,已经大概的介绍了mat ...

  6. swfupload多文件上传[附源码]

    swfupload多文件上传[附源码] 文件上传这东西说到底有时候很痛,原来的asp.net服务器控件提供了很简单的上传,但是有回传,还没有进度条提示.这次我们演示利用swfupload多文件上传,项 ...

  7. 聊天系统Demo,增加Silverlight客户端(附源码)-- ESFramework 4.0 快速上手(09)

    在ESFramework 4.0 快速上手 -- 入门Demo,一个简单的IM系统(附源码)一文中,我们介绍了使用ESFramework的Rapid引擎开发的winform聊天程序,本文我们将在之前d ...

  8. openlayers4 入门开发系列之地图导航控件篇(附源码下载)

    前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...

  9. 超详细的php用户注册页面填写信息完整实例(附源码)

    这篇文章主要介绍了一个超详细的php用户注册页面填写信息完整实例,内容包括邮箱自动匹配.密码强度验证以及防止表单重复等,小编特别喜欢这篇文章,推荐给大家. 注册页面是大多数网站必备的页面,所以很有必要 ...

  10. 日志组件Log2Net的介绍和使用(附源码开源地址)

    Log2Net是一个用于收集日志到数据库或文件的组件,支持.NET和.NetCore平台. 此组件自动收集系统的运行日志(服务器运行情况.在线人数等).异常日志.程序员还可以添加自定义日志. 该组件支 ...

随机推荐

  1. Fortran笔记 派生类型-整理版

    以下为整理后的笔记,英文原文 Introduction to Modern Fortran for the Earth System Sciences, 英文翻译 https://www.cnblog ...

  2. SourceInsight中inc文件中的符号不能同步问题

    解决方法: 打开Options->Preferences->Syntax Formatting->File Types,然后选中对应的开发语言,如C/C++ Source File, ...

  3. C++ 函数类型和函数指针类型的自动推导、声明和赋值

    1.函数类型推导 #include <iostream> bool MyComp(int val1, int val2) { return val1 > val2; } int ma ...

  4. 一个MySQL双引号把我坑惨了!

    一.前言 最近经常碰到开发误删除误更新数据,这不,他们又给我找了个麻烦,我们来看下整个过程,把我坑得够惨. 二.过程 由于开发需要在生产环节中修复数据,需要执行120条SQL语句,需要将数据进行更新, ...

  5. Jmeter性能测试入门到项目实战03

    Jmeter性能测试入门到项目实战03 P35 项目业务介绍 1,之前已经对Jmeter所有的知识点做了一个介绍,主要的业务模式是币币交易,就像b2c 2, 3, 4,主要是把登陆和交易这一块做一个并 ...

  6. .Net Core WebApi AutoFac用法

    1. 安装Autofac.Extensions.DependencyInjection管理包 UI层安装 2.在Program里面配置服务提供工厂 3.在Startup里面添加一个配置容器的方法 使用 ...

  7. springboot集成es7(基于high level client)

    环境: ES: 7.12.0 1.springboot工程引入es相关jar <dependency> <groupId>org.elasticsearch</group ...

  8. vi 异常退出出现 E325:Attention的解决办法

    在linux系统下使用vi编辑程序的时候,没有保存退出,直接关闭了,出现了以下的情况: 打开就会显示filename.c.swap已经存在. 这是因为vi在编辑文件时会创建一个交换文件swap fil ...

  9. ping Hyper-V内虚拟机网络延迟

  10. plsql和instantclient版本都对,依然不能初始化oci.dll解决办法

    这里写到 "初始化错误,不能初始化 oci.dll, 请确认你安装的是64位的Oracle客户端 " ,这个描述还是非常的到位啊,我一检查,果然下载的客户端是32位的,在确保自己的 ...