You've already forked QWeatherReporter
Compare commits
53 Commits
v3.1.0b
...
v4.0.0pre-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6db23a3e28 | ||
|
|
5bed020e65 | ||
|
|
506bb81f44 | ||
|
|
3a0902509a | ||
|
|
704158bc64 | ||
|
|
f6f97a0785 | ||
|
|
b41be502f2 | ||
|
|
cc8bed1890 | ||
|
|
9174e11ecd | ||
|
|
31ea638770 | ||
|
|
11244734fd | ||
|
|
9cbd3b5063 | ||
|
|
3622b094fd | ||
|
|
4c73240fe9 | ||
|
|
451aed59d0 | ||
|
|
0b41c9c1f5 | ||
|
|
b205533ae7 | ||
|
|
b493c06387 | ||
|
|
936dd14a87 | ||
|
|
21a7f9140d | ||
|
|
23f44c3f78 | ||
|
|
9477c33952 | ||
|
|
7fdd0d56a1 | ||
|
|
2117771518 | ||
|
|
ea4d04a71c | ||
|
|
235e252bab | ||
|
|
278913b2c3 | ||
|
|
3cc8afa1f4 | ||
|
|
3034346c73 | ||
|
|
7b4a0dd033 | ||
|
|
c9ec788cd3 | ||
|
|
30240033cf | ||
|
|
f986a037a8 | ||
|
|
1b96caf719 | ||
|
|
df1d5a13b4 | ||
|
|
2c111536c7 | ||
|
|
1bedb05ac1 | ||
|
|
fd10791baa | ||
|
|
98b4b9305b | ||
|
|
62d08096b3 | ||
|
|
c32efe4b44 | ||
|
|
9e3f866fb8 | ||
|
|
e8b1a910ae | ||
|
|
dd1dc2e3e6 | ||
|
|
bac4565289 | ||
|
|
25f755fd27 | ||
|
|
09ebfc1a76 | ||
|
|
ee9e4a4c0f | ||
|
|
c2bc49b0d4 | ||
|
|
fe8d850422 | ||
|
|
51023efc76 | ||
|
|
0c6a267967 | ||
|
|
28e1319ee3 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
test.py
|
||||
*.yml
|
||||
!config.yml
|
||||
*.log
|
||||
|
||||
34
QWeather.py
34
QWeather.py
@@ -6,9 +6,39 @@
|
||||
# @File Name: QWeather.py
|
||||
|
||||
|
||||
import sys
|
||||
import threading
|
||||
from core import qweather
|
||||
from tkinter import messagebox
|
||||
from lib.buildGUIClass import tk
|
||||
from lib.buildGUIClass import root
|
||||
from lib.buildGUIClass import Features
|
||||
|
||||
|
||||
def on_closing():
|
||||
if messagebox.askokcancel('Exit', 'Confirm exit?'):
|
||||
Features().quit_()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(qweather.main())
|
||||
b1 = tk.Button(root,
|
||||
text='Click to run QWeather',
|
||||
command=threading.Thread(target=qweather.main, name='main').start)
|
||||
b1.place(x=45, y=300)
|
||||
b2 = tk.Button(root,
|
||||
text='Exit the main program',
|
||||
command=on_closing)
|
||||
b2.place(x=275, y=300)
|
||||
l1 = tk.Label(root,
|
||||
text='更多功能敬请期待\nEnjoy this ~',)
|
||||
l1.place(x=530, y=70)
|
||||
information = """
|
||||
已知问题:
|
||||
1.开启QWeather主程序后退出只会退出界面主程序并不会退出
|
||||
2.webservice.py 的输出并不会被插入到GUI界面里
|
||||
* 更多问题待发现...
|
||||
"""
|
||||
l2 = tk.Label(root,
|
||||
text=information)
|
||||
l2.place(x=140, y=350)
|
||||
root.protocol('WM_DELETE_WINDOW', on_closing)
|
||||
root.mainloop()
|
||||
|
||||
34
README.md
34
README.md
@@ -1,51 +1,58 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/MarkusJoe/QWeather">
|
||||
<img src="https://img.shields.io/badge/Python-3.10.x-blue.svg" alt="PythonVersion">
|
||||
<img src="https://img.shields.io/badge/release-3.1.0b-green.svg" alt="QWeatherVersion">
|
||||
<img src="https://img.shields.io/badge/release-4.0.0-green.svg" alt="QWeatherVersion">
|
||||
<img src="https://img.shields.io/badge/LINCESE-Apache2.0-orange.svg" alt="LICENSE">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
## *QWeather*
|
||||
## *QWeatherReporter*
|
||||
|
||||
<i style="text-align: center;"><a href="https://www.qweather.com/">QWeather Official Website</a></i>\
|
||||
<i style="text-align: center;"><a href="https://dev.qweather.com/">QWeather Development Platform</a></i>
|
||||
|
||||
> GUI问题: 无法填写城市位置, 清手动打开./res/china_city_list.xlsx 进行手动查询
|
||||
> 并填写至`requests-settings` > `location`
|
||||
> `only-view-settings` > `city-name` 也需手动填写
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<b><i>当前分支: GUI, 为界面分支, 仍存在bug以及一些问题<br></i></b>
|
||||
<b><i>当前分支版本为重构后的版本重构前版本请在before分支中查看<br>(重构前版本不再更新,最新版本为v2.9.0)</i></b><br>
|
||||
<b><i><a href="https://markusjoe.github.io/" target="_blank">点击跳转到帮助文档</a></i></b>
|
||||
</div>
|
||||
|
||||
## 声明
|
||||
- > 程序作者: **RTAkland (markushammered@gmail.com)**
|
||||
- > 和风天气开发者平台:https://dev.qweather.com
|
||||
- > 和风天气官网: https://qweather.com
|
||||
|
||||
|
||||
### 开源
|
||||
- 本项目以[Apache-2.0](./LICENSE)许可开源, 即:
|
||||
- 你可以直接使用该项目提供的功能, 无需任何授权
|
||||
- 你可以在**注明来源版权信息**的情况下对源代码进行任意分发和修改以及衍生
|
||||
|
||||
### 已实现功能
|
||||
- [x] 发送免费版&开发板右键
|
||||
- [x] 发送免费版&开发版天气预报功能
|
||||
- [x] 间隔10分钟请求一次自然灾害预警信息
|
||||
- [x] 在网页上快速查看天气
|
||||
- [x] 在邮件html内附带一句一言
|
||||
- [ ] ~~推送到QQ(咕咕咕)~~
|
||||
- [x] GUI界面 (无法填写城市位置, 清手动打开./res/china_city_list.xlsx 进行手动查询)
|
||||
|
||||
### 问题汇总
|
||||
#### Python 版本:
|
||||
> 程序使用了Python3.10中的match-case语句
|
||||
> 请使用Python3.10版本运行
|
||||
> 程序使用了*Python3.10.x*中的match-case语句
|
||||
> 请使用*Python3.10.x*版本运行
|
||||
#### 主题选择:
|
||||
> 在2.9.0之后的版本不支持自定义天气图标 只能使用最新的图标
|
||||
#### 配置填写问题:
|
||||
> `location`项和`only-view-settings`类不需要用户填写, 只需要打开`QWeather.py`进行自助填写
|
||||
|
||||
|
||||
## 声明
|
||||
- > 程序作者: **RTAkland (markushammered@gmail.com)**
|
||||
- > 和风天气开发者平台:https://dev.qweather.com
|
||||
- > 和风天气官网: https://qweather.com
|
||||
|
||||
## 如何使用
|
||||
- 程序基于python3.10开发 务必使用python3.10版本运行
|
||||
- 将config.yml正确填写完成
|
||||
@@ -54,8 +61,9 @@
|
||||
|
||||
### 网页上查看天气
|
||||
- 将所有准备工作完成(能正常运行QWeather.py)
|
||||
- 运行QWeather.py
|
||||
- 打开浏览器输入***127.0.0.1***:7898 (127.0.0.1可以更改为部署本项目的服务器ip)
|
||||
- 运行`QWeather.py`
|
||||
- 打开浏览器输入**127.0.0.1:7898**
|
||||
>127.0.0.1可以更改为部署本项目的服务器ip, 7898端口不能被其他程序占用或不开放此端口
|
||||
|
||||
|
||||
## 联系方式
|
||||
|
||||
12
config.yml
12
config.yml
@@ -103,7 +103,15 @@ client-settings:
|
||||
# [选填/只能一个] 开启本地网页快速查看天气
|
||||
# 默认: False
|
||||
# 可选: "True" "False"
|
||||
webserver: False
|
||||
webservice: false
|
||||
|
||||
# [必填/只能一个] 是否使用SSL 连接到邮箱服务器
|
||||
# 默认 True
|
||||
# 可选: "True" "False"
|
||||
SSL: True
|
||||
|
||||
# [必填/只能一个] enable GUI or disable GUI
|
||||
enable-GUI: True
|
||||
|
||||
|
||||
# [标识/不填] 仅作用户读取标识
|
||||
@@ -111,7 +119,7 @@ only-view-settings:
|
||||
# 城市名
|
||||
city-name: 黄岩-台州-台州
|
||||
# 修改时间
|
||||
time: Sun Dec 12 2021 14:49:38
|
||||
time: Mon Dec 20 2021 21:08:22
|
||||
# 修改用户
|
||||
user: rtakland
|
||||
|
||||
|
||||
@@ -5,31 +5,8 @@
|
||||
# @Create Time: 2021/12/15
|
||||
# @File Name: __init__.py
|
||||
|
||||
"""
|
||||
Check the configuration file
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from core.logger import Logger
|
||||
from core.language import Language
|
||||
from core.read_config import read_config
|
||||
|
||||
|
||||
settings = read_config()
|
||||
|
||||
if not os.path.exists('./logs'):
|
||||
os.mkdir('./logs')
|
||||
|
||||
for mail in settings[0].values():
|
||||
if not mail:
|
||||
Logger.critical('mail-settings 有未填写项目')
|
||||
sys.exit(1)
|
||||
for request in settings[1].values():
|
||||
if not request:
|
||||
Logger.critical('request-settings 有未填写项目')
|
||||
sys.exit(1)
|
||||
for other in settings[1].values():
|
||||
if not other:
|
||||
Logger.critical('client-settings 有未填写项目')
|
||||
sys.exit(1)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# @File Name: information.py
|
||||
|
||||
import requests
|
||||
from core import read_config
|
||||
from core.read_config import read_config
|
||||
|
||||
|
||||
class WeatherInfo:
|
||||
|
||||
@@ -10,6 +10,10 @@ import json
|
||||
|
||||
|
||||
def Language():
|
||||
"""
|
||||
读取配置文件中的语言选项并返回相对因的语言文件的读取结果
|
||||
:return:
|
||||
"""
|
||||
with open('./config.yml', 'r', encoding='utf-8') as lang:
|
||||
config = YAML().load(lang.read())
|
||||
language_sel = config['client-settings']['language']
|
||||
|
||||
@@ -13,7 +13,7 @@ import time
|
||||
level = read_config()[2]['level']
|
||||
|
||||
date_format = '%H:%M:%S'
|
||||
info_format_console = '%(log_color)s[%(asctime)s] |%(filename)s[ %(lineno)-3s] |%(levelname)-8s |%(message)s'
|
||||
info_format_console = '%(log_color)s[%(asctime)s] |%(filename)s[%(lineno)-3s] |%(levelname)-8s |%(message)s'
|
||||
info_format_file = '[%(asctime)s] |%(filename)s[%(funcName)sline:%(lineno)d] |%(levelname)-8s |%(message)s'
|
||||
formatter = ColoredFormatter(fmt=info_format_console,
|
||||
datefmt=date_format,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
# @File Name: qweather.py
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import argparse
|
||||
@@ -13,14 +14,21 @@ from concurrent.futures import ProcessPoolExecutor
|
||||
from core.logger import Logger
|
||||
from core.language import Language
|
||||
from core.settings import change_settings
|
||||
from lib import webserver
|
||||
from core import read_config
|
||||
from core.read_config import read_config
|
||||
from core.sendmail import Mail
|
||||
from lib.webservice import accept_requests
|
||||
from lib.buildGUIClass import InsertLog
|
||||
|
||||
language = Language()
|
||||
settings = read_config()
|
||||
processes = ProcessPoolExecutor(max_workers=3)
|
||||
gui_log = InsertLog()
|
||||
|
||||
|
||||
def check_time():
|
||||
"""
|
||||
if the local time is the time in config, send a mail
|
||||
通过多进程让函数和主程序并行,
|
||||
并持续检测本地计算机时间是否和配置文件内填写的发送时间一致
|
||||
:return:
|
||||
"""
|
||||
mode = settings[1]['mode']
|
||||
@@ -33,7 +41,10 @@ def check_time():
|
||||
if local_time in time_list:
|
||||
Mail().dev_version()
|
||||
Logger.info(f'{language["mail_succeed"]}')
|
||||
gui_log.insert(f'{language["wait_seconds"]}')
|
||||
Logger.info(f'{language["wait_seconds"]}')
|
||||
gui_log.insert(f'{language["mail_succeed"]}')
|
||||
|
||||
time.sleep(60)
|
||||
case 'free':
|
||||
while True:
|
||||
@@ -42,11 +53,58 @@ def check_time():
|
||||
if local_time in time_list:
|
||||
Mail().free_version()
|
||||
Logger.info(f'{language["mail_succeed"]}')
|
||||
gui_log.insert(f'{language["wait_seconds"]}')
|
||||
Logger.info(f'{language["wait_seconds"]}')
|
||||
gui_log.insert(f'{language["mail_succeed"]}')
|
||||
time.sleep(60)
|
||||
|
||||
|
||||
def check_config():
|
||||
"""
|
||||
返回配置文件中的location项是否填写
|
||||
:return: True or False
|
||||
"""
|
||||
location = settings[1]['location']
|
||||
if location:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def setting():
|
||||
"""
|
||||
检查配置文件是否填写完成
|
||||
:return:
|
||||
"""
|
||||
for mail in settings[0].values():
|
||||
if not mail:
|
||||
Logger.critical('mail-settings 有未填写项目')
|
||||
sys.exit(1)
|
||||
for request in settings[1].values():
|
||||
if not request:
|
||||
Logger.critical('request-settings 有未填写项目')
|
||||
sys.exit(1)
|
||||
for other in settings[1].values():
|
||||
if not other:
|
||||
Logger.critical('client-settings 有未填写项目')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
主程序
|
||||
:return:
|
||||
"""
|
||||
|
||||
Logger.info(f'{language["statement_1"]}')
|
||||
gui_log.insert(f'{language["statement_1"]}')
|
||||
Logger.info(f'{language["statement_2"]}')
|
||||
gui_log.insert(f'{language["statement_2"]}')
|
||||
Logger.info(f'{language["statement_3"]}')
|
||||
gui_log.insert(f'{language["statement_3"]}')
|
||||
Logger.info(f'{language["statement_4"]}')
|
||||
gui_log.insert(f'{language["statement_4"]}')
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
arg_keywords = ['free', 'dev', 'warning', 'setting']
|
||||
parser.add_argument('-t',
|
||||
@@ -59,23 +117,33 @@ def main():
|
||||
case 'free':
|
||||
Mail().free_version()
|
||||
Logger.debug(f'{language["debug_done"]}')
|
||||
gui_log.insert(f'{language["debug_done"]}')
|
||||
sys.exit(0)
|
||||
case 'dev':
|
||||
Mail().dev_version()
|
||||
Logger.debug(f'{language["debug_done"]}')
|
||||
gui_log.insert(f'{language["debug_done"]}')
|
||||
sys.exit(0)
|
||||
case 'warning':
|
||||
Mail().warning_()
|
||||
Logger.debug(f'{language["debug_done"]}')
|
||||
gui_log.insert(f'{language["debug_done"]}')
|
||||
sys.exit(0)
|
||||
case 'setting':
|
||||
change_settings(False)
|
||||
change_settings()
|
||||
Logger.debug(f'{language["debug_done"]}')
|
||||
gui_log.insert(f'{language["debug_done"]}')
|
||||
case _:
|
||||
pass
|
||||
if check_config():
|
||||
setting()
|
||||
else:
|
||||
change_settings()
|
||||
|
||||
processes.submit(check_time)
|
||||
if settings[2]['webserver']:
|
||||
processes.submit(webserver.process_request())
|
||||
if settings[2]['webservice']:
|
||||
Logger.info(f'{language["webservice_ip"]}:127.0.0.1:7898')
|
||||
gui_log.insert(f'{language["webservice_ip"]}:127.0.0.1:7898')
|
||||
processes.submit(accept_requests)
|
||||
|
||||
time_count = 0
|
||||
while True:
|
||||
@@ -84,14 +152,3 @@ def main():
|
||||
if time_count == 600:
|
||||
Mail().warning_()
|
||||
time_count = 0
|
||||
|
||||
|
||||
if __name__ != '__main__':
|
||||
language = Language()
|
||||
settings = read_config()
|
||||
processes = ProcessPoolExecutor(max_workers=3)
|
||||
|
||||
Logger.info(f'{language["statement_1"]}')
|
||||
Logger.info(f'{language["statement_2"]}')
|
||||
Logger.info(f'{language["statement_3"]}')
|
||||
Logger.info(f'{language["statement_4"]}')
|
||||
|
||||
@@ -10,10 +10,11 @@ from ruamel.yaml import YAML
|
||||
|
||||
def read_config():
|
||||
"""
|
||||
|
||||
读取配置文件并返回读取到的内容同
|
||||
:return: mail-settings, request-settings, client-settings, only-view-settings --> 0, 1, 2, 3
|
||||
"""
|
||||
with open('./config.yml', 'r') as conf:
|
||||
config_file = 'config.yml'
|
||||
with open(f'./{config_file}', 'r') as conf:
|
||||
config = YAML().load(conf.read())
|
||||
mail_settings = config['mail-settings']
|
||||
request_settings = config['request-settings']
|
||||
|
||||
@@ -8,11 +8,14 @@
|
||||
import pandas
|
||||
from core.logger import Logger
|
||||
from core.language import Language
|
||||
from lib.buildGUIClass import InsertLog
|
||||
|
||||
gui_log = InsertLog()
|
||||
|
||||
|
||||
def read_excel(kw: str):
|
||||
"""
|
||||
Read xlsx file and return searched results
|
||||
读取china_city_list.xlsx并搜索匹配关键字的结果并输出到终端
|
||||
:param kw: keyword
|
||||
:return: city_list
|
||||
"""
|
||||
@@ -21,12 +24,14 @@ def read_excel(kw: str):
|
||||
index_count = 0
|
||||
city_list = []
|
||||
Logger.info(f'[Search]{language["reading_the_file"]}')
|
||||
gui_log.insert(f'[Search]{language["reading_the_file"]}')
|
||||
df = pandas.read_excel(f'./res/china_city_list.xlsx')
|
||||
pandas.set_option('max_rows', None) # 读取xlsx文件不折叠
|
||||
data_records = df.to_dict(orient='split')
|
||||
for i in data_records['data']:
|
||||
if kw in str(i):
|
||||
Logger.info(f' {index_count} | {i[2]}-{i[4]}-{i[6]}')
|
||||
gui_log.insert(f' {index_count} | {i[2]}-{i[4]}-{i[6]}')
|
||||
city = [index_count, i[0], i[1], i[2], i[3], i[4], i[5], i[6], i[7], i[8], i[9]]
|
||||
index_count += 1
|
||||
city_list.append(city)
|
||||
|
||||
@@ -16,6 +16,10 @@ from core.logger import Logger
|
||||
from core.language import Language
|
||||
from core.information import WeatherInfo
|
||||
from core.read_config import read_config
|
||||
from lib.hitokoto import hitokoto
|
||||
from lib.buildGUIClass import InsertLog
|
||||
|
||||
gui_log = InsertLog()
|
||||
|
||||
|
||||
class Mail:
|
||||
@@ -33,12 +37,18 @@ class Mail:
|
||||
self.message['From'] = Header('QWeather') # 发件人名称
|
||||
self.message['To'] = Header('All allowed User') # 收件人显示名称
|
||||
|
||||
self.hitokoto = hitokoto() # 一言
|
||||
|
||||
if self.enableSSL:
|
||||
self.smtp = smtplib.SMTP_SSL(self.server, self.port) # 登录服务器 使用SSL连接
|
||||
else:
|
||||
self.smtp = smtplib.SMTP(self.server, self.port) # 登录邮箱服务器 不使用SSL连接
|
||||
|
||||
def dev_version(self):
|
||||
"""
|
||||
开发者版本
|
||||
:return:
|
||||
"""
|
||||
dev_weather = WeatherInfo().dev_version()
|
||||
dates = dev_weather[0]
|
||||
day_weathers = dev_weather[1]
|
||||
@@ -124,8 +134,8 @@ class Mail:
|
||||
<th> </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>风速/风级/风向</th>
|
||||
<th>相对湿度</th>
|
||||
<th>风速/级/向</th>
|
||||
<th>湿度</th>
|
||||
<th>紫外线</th>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -138,8 +148,8 @@ class Mail:
|
||||
</tr>
|
||||
<tr>
|
||||
<th>能见度</th>
|
||||
<th>大气压强</th>
|
||||
<th>相对云量</th>
|
||||
<th>压强</th>
|
||||
<th>云量</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{vis}km </td>
|
||||
@@ -162,9 +172,13 @@ class Mail:
|
||||
<td> {sunset} </td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<div style="text-align: center" id="hitokoto">
|
||||
<p>
|
||||
{self.hitokoto}
|
||||
</p>
|
||||
</div>
|
||||
<div style="text-align: center;" id="About">
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<i>
|
||||
<b>
|
||||
@@ -198,11 +212,17 @@ class Mail:
|
||||
self.smtp.login(self.sender, self.password) # 登录
|
||||
self.smtp.sendmail(self.sender, self.receiver, self.message.as_string()) # 发送
|
||||
Logger.info(f'{self.language["mail_succeed"]}')
|
||||
gui_log.insert(f'{self.language["mail_succeed"]}')
|
||||
except smtplib.SMTPException as e: # 处理错误
|
||||
Logger.critical(f'{self.language["mail_error"]}: {e}')
|
||||
gui_log.insert(f'{self.language["mail_error"]}: {e}')
|
||||
sys.exit(1)
|
||||
|
||||
def free_version(self):
|
||||
"""
|
||||
免费版本
|
||||
:return:
|
||||
"""
|
||||
free_weather = WeatherInfo().free_version()
|
||||
dates = free_weather[0]
|
||||
day_weathers = free_weather[1]
|
||||
@@ -263,8 +283,8 @@ class Mail:
|
||||
<th> </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>风速/风级/风向</th>
|
||||
<th>相对湿度</th>
|
||||
<th>风速/级/向</th>
|
||||
<th>湿度</th>
|
||||
<th>紫外线</th>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -277,8 +297,8 @@ class Mail:
|
||||
</tr>
|
||||
<tr>
|
||||
<th>能见度</th>
|
||||
<th>大气压强</th>
|
||||
<th>相对云量</th>
|
||||
<th>压强</th>
|
||||
<th>云量</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{vis}km </td>
|
||||
@@ -301,9 +321,13 @@ class Mail:
|
||||
<td> {sunset} </td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<div style="text-align: center" id="hitokoto">
|
||||
<p>
|
||||
{self.hitokoto}
|
||||
</p>
|
||||
</div>
|
||||
<div style="text-align: center;" id="About">
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<i>
|
||||
<b>
|
||||
@@ -337,11 +361,17 @@ class Mail:
|
||||
self.smtp.login(self.sender, self.password) # 登录
|
||||
self.smtp.sendmail(self.sender, self.receiver, self.message.as_string()) # 发送
|
||||
Logger.info(f'{self.language["mail_succeed"]}')
|
||||
Logger.info(f'{self.language["mail_succeed"]}')
|
||||
except smtplib.SMTPException as e: # 处理错误
|
||||
Logger.critical(f'{self.language["mail_error"]}: {e}')
|
||||
gui_log.insert(f'{self.language["mail_error"]}: {e}')
|
||||
sys.exit(1)
|
||||
|
||||
def warning_(self):
|
||||
"""
|
||||
自然灾害预警
|
||||
:return:
|
||||
"""
|
||||
info = WeatherInfo().warning_()
|
||||
release_time = info[0]
|
||||
title = info[1]
|
||||
@@ -356,11 +386,14 @@ class Mail:
|
||||
case 'update':
|
||||
status = '预警更新'
|
||||
Logger.info(f'{self.language["new_warning"]}')
|
||||
gui_log.insert(f'{self.language["new_warning"]}')
|
||||
case 'active':
|
||||
status = '已有灾害'
|
||||
Logger.info(f'{self.language["warning_updated"]}')
|
||||
gui_log.insert(f'{self.language["warning_updated"]}')
|
||||
case 'cancel':
|
||||
Logger.info(f'{self.language["warning_canceled"]}')
|
||||
gui_log.insert(f'{self.language["warning_canceled"]}')
|
||||
|
||||
mail_html = f"""
|
||||
<!DOCTYPE html>
|
||||
@@ -411,6 +444,8 @@ class Mail:
|
||||
self.smtp.login(self.sender, self.password) # 登录
|
||||
self.smtp.sendmail(self.sender, self.receiver, self.message.as_string()) # 发送
|
||||
Logger.info(f'{self.language["mail_succeed"]}')
|
||||
gui_log.insert(f'{self.language["mail_succeed"]}')
|
||||
except smtplib.SMTPException as e: # 处理错误
|
||||
Logger.critical(f'{self.language["mail_error"]}: {e}')
|
||||
gui_log.insert(f'{self.language["mail_error"]}: {e}')
|
||||
sys.exit(1)
|
||||
|
||||
116
core/settings.py
116
core/settings.py
@@ -13,60 +13,78 @@ from core.language import Language
|
||||
from core.read_excel import read_excel
|
||||
from core.read_config import read_config
|
||||
from ruamel.yaml import YAML
|
||||
from lib.buildGUIClass import InsertLog
|
||||
from lib.buildGUIClass import var_command
|
||||
|
||||
gui_log = InsertLog()
|
||||
gui = read_config()[2]['enable-GUI']
|
||||
|
||||
|
||||
def change_settings(flag: bool = True):
|
||||
def change_settings():
|
||||
"""
|
||||
|
||||
:return: None
|
||||
使用read_excel.py搜索到的结果再次进行二次选择, 并写入文件
|
||||
:return:
|
||||
"""
|
||||
language = Language()
|
||||
settings = read_config()
|
||||
|
||||
if not settings[1]['mode'] or not flag:
|
||||
Logger.info(f'[Modify]{language["fill_the_config"]}')
|
||||
Logger.info(f'[Modify]{language["input_a_city_name"]}')
|
||||
while True:
|
||||
time.sleep(0.3)
|
||||
city_name = input('-->')
|
||||
match city_name:
|
||||
case 'q':
|
||||
Logger.info(f'[Modify]User quit.')
|
||||
sys.exit(0)
|
||||
case '':
|
||||
Logger.critical(f'[Modify]{language["null_value"]}')
|
||||
continue
|
||||
case _:
|
||||
break
|
||||
searched_city = read_excel(city_name)
|
||||
Logger.info(f'[Modify]{language["user_input"]}:[{city_name}]')
|
||||
Logger.info(f'[Modify]{language["select_a_index"]}')
|
||||
if not searched_city:
|
||||
Logger.error(f'[Modify]{language["no_result"]}')
|
||||
sys.exit(1)
|
||||
Logger.info(f'[Modify]{language["change_setting"]}')
|
||||
gui_log.insert(f'[Modify]{language["change_setting"]}')
|
||||
Logger.info(f'[Modify]{language["fill_the_config"]}')
|
||||
gui_log.insert(f'[Modify]{language["fill_the_config"]}')
|
||||
Logger.info(f'[Modify]{language["input_a_city_name"]}')
|
||||
gui_log.insert(f'[Modify]{language["input_a_city_name"]}')
|
||||
while True:
|
||||
time.sleep(0.3)
|
||||
while True:
|
||||
try:
|
||||
time.sleep(0.3)
|
||||
user_input = input('-->')
|
||||
if user_input == 'q':
|
||||
Logger.info('[Quit]User quit')
|
||||
sys.exit(1)
|
||||
index = searched_city[int(user_input)]
|
||||
with open('./config.yml', 'r', encoding='utf-8') as of:
|
||||
data = YAML().load(of)
|
||||
data['request-settings']['location'] = index[1]
|
||||
data['only-view-settings']['city-name'] = f'{index[3]}-{index[7]}-{index[7]}'
|
||||
data['only-view-settings']['time'] = time.strftime("%a %b %d %Y %H:%M:%S", time.localtime())
|
||||
data['only-view-settings']['user'] = getpass.getuser()
|
||||
with open('./config.yml', 'w', encoding='utf-8') as wf:
|
||||
YAML().dump(data, wf)
|
||||
Logger.info(f'[Write]{language["write_successfully"]}:config.yml')
|
||||
break
|
||||
except (IndexError, ValueError) as e:
|
||||
Logger.info(e)
|
||||
Logger.error(f'[Write]{language["input_type_error"]}')
|
||||
continue
|
||||
finally:
|
||||
Logger.info('[Done]Program has done.')
|
||||
city_name = input('-->')
|
||||
match city_name:
|
||||
case 'q':
|
||||
Logger.info(f'[Exit]{language["exit"]}')
|
||||
sys.exit(0)
|
||||
case '':
|
||||
Logger.error(f'[Modify]{language["null_value"]}')
|
||||
continue
|
||||
case _:
|
||||
break
|
||||
searched_city = read_excel(city_name)
|
||||
Logger.info(f'[Modify]{language["user_input"]}:[{city_name}]')
|
||||
gui_log.insert(f'[Modify]{language["user_input"]}:[{city_name}]')
|
||||
Logger.info(f'[Modify]{language["select_a_index"]}')
|
||||
gui_log.insert(f'[Modify]{language["select_a_index"]}')
|
||||
if not searched_city:
|
||||
Logger.error(f'[Modify]{language["no_result"]}')
|
||||
gui_log.insert(f'[Modify]{language["no_result"]}')
|
||||
sys.exit(1)
|
||||
time.sleep(0.3)
|
||||
while True:
|
||||
try:
|
||||
time.sleep(0.3)
|
||||
# if not gui:
|
||||
user_input = input('-->')
|
||||
# else:
|
||||
# user_input = var_command.get()
|
||||
if user_input == 'q':
|
||||
Logger.info(f'[Exit]{language["exit"]}')
|
||||
gui_log.insert(f'[Exit]{language["exit"]}')
|
||||
sys.exit(1)
|
||||
index = searched_city[int(user_input)]
|
||||
with open('./config.yml', 'r', encoding='utf-8') as of:
|
||||
data = YAML().load(of)
|
||||
data['request-settings']['location'] = index[1]
|
||||
data['only-view-settings']['city-name'] = f'{index[3]}-{index[7]}-{index[7]}'
|
||||
data['only-view-settings']['time'] = time.strftime("%a %b %d %Y %H:%M:%S", time.localtime())
|
||||
data['only-view-settings']['user'] = getpass.getuser()
|
||||
with open('./config.yml', 'w', encoding='utf-8') as wf:
|
||||
YAML().dump(data, wf)
|
||||
Logger.info(f'[Write]{language["write_successfully"]}:config.yml')
|
||||
gui_log.insert(f'[Write]{language["write_successfully"]}:config.yml')
|
||||
break
|
||||
except (IndexError, ValueError) as e:
|
||||
Logger.info(e)
|
||||
gui_log.insert(e)
|
||||
Logger.error(f'[Write]{language["input_type_error"]}')
|
||||
gui_log.insert(f'[Write]{language["input_type_error"]}')
|
||||
continue
|
||||
finally:
|
||||
Logger.info(f'[Exit]{language["exit"]}')
|
||||
gui_log.insert(f'[Exit]{language["exit"]}')
|
||||
sys.exit(0)
|
||||
|
||||
58
lib/buildGUIClass.py
Normal file
58
lib/buildGUIClass.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/1/9
|
||||
# @File Name: buildGUIClass.py
|
||||
import tkinter
|
||||
import tkinter as tk
|
||||
|
||||
root = tk.Tk()
|
||||
root.title('QWeather client')
|
||||
sw = root.winfo_screenwidth()
|
||||
sh = root.winfo_screenheight()
|
||||
ww = 700
|
||||
wh = 490
|
||||
x = (sw - ww) / 2
|
||||
y = (sh - wh) / 2
|
||||
root.geometry("%dx%d+%d+%d" % (ww, wh, x, y))
|
||||
root.minsize(700, 490)
|
||||
root.maxsize(700, 490)
|
||||
|
||||
log_label = tk.Label(root, text='Log information')
|
||||
log_label.place(x=170, y=4)
|
||||
log_text = tk.Text(root, width=55, height=15)
|
||||
log_text.place(x=30, y=30)
|
||||
var_command = tk.StringVar()
|
||||
|
||||
|
||||
class InsertLog:
|
||||
def __init__(self):
|
||||
self.log_text = log_text
|
||||
|
||||
def insert(self, msg: str):
|
||||
"""
|
||||
insert the information to gui(Text)
|
||||
:param msg: information
|
||||
:return:
|
||||
"""
|
||||
if '\n' not in msg:
|
||||
msg = msg + '\n'
|
||||
self.log_text.configure(state=tkinter.NORMAL) # writable
|
||||
self.log_text.insert('insert', msg) # insert information
|
||||
self.log_text.configure(state=tkinter.DISABLED) # readable
|
||||
|
||||
|
||||
class Features:
|
||||
def __init__(self):
|
||||
self.root = root
|
||||
self.tk = tk
|
||||
self.log_text = log_text
|
||||
|
||||
def quit_(self):
|
||||
"""
|
||||
quit gui
|
||||
:return:
|
||||
"""
|
||||
self.root.destroy()
|
||||
self.root.quit()
|
||||
@@ -16,6 +16,10 @@ import json
|
||||
|
||||
|
||||
def hourly_weather():
|
||||
"""
|
||||
获取24小时的天气
|
||||
:return:
|
||||
"""
|
||||
settings = read_config()
|
||||
location = settings[1]['location']
|
||||
key = settings[1]['key']
|
||||
|
||||
@@ -17,6 +17,10 @@ import json
|
||||
|
||||
|
||||
def indices():
|
||||
"""
|
||||
获取生活建议
|
||||
:return:
|
||||
"""
|
||||
settings = read_config()
|
||||
location = settings[1]['location']
|
||||
key = settings[1]['key']
|
||||
|
||||
@@ -16,6 +16,10 @@ import json
|
||||
|
||||
|
||||
def real_time_air_quality():
|
||||
"""
|
||||
获取实时的空气质量数据
|
||||
:return:
|
||||
"""
|
||||
settings = read_config()
|
||||
location = settings[1]['location']
|
||||
key = settings[1]['key']
|
||||
|
||||
@@ -16,6 +16,11 @@ import json
|
||||
|
||||
|
||||
def get_warning_list(_range='cn'):
|
||||
"""
|
||||
获取当前正在发送自然灾害的城市id列表
|
||||
:param _range: Range
|
||||
:return:
|
||||
"""
|
||||
settings = read_config()
|
||||
key = settings[1]['key']
|
||||
r = requests.get(f'https://devapi.qweather.com/v7/warning/list?range={_range}&key={key}')
|
||||
|
||||
23
lib/hitokoto.py
Normal file
23
lib/hitokoto.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2021/12/24
|
||||
# @File Name: hitokoto.py
|
||||
|
||||
import requests
|
||||
from core.language import Language
|
||||
from core.logger import Logger
|
||||
|
||||
|
||||
def hitokoto():
|
||||
language = Language()
|
||||
url = 'https://v1.hitokoto.cn/'
|
||||
Logger.info(f'{language["hitokoto"]}')
|
||||
res = requests.get(url).json()
|
||||
speaker = res['from_who']
|
||||
text = res['hitokoto']
|
||||
if not speaker:
|
||||
speaker = language['noname']
|
||||
full_text = f'{text} --{speaker}'
|
||||
|
||||
return full_text
|
||||
@@ -3,22 +3,36 @@
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2021/12/18
|
||||
# @File Name: webserver.py
|
||||
# @File Name: webservice.py
|
||||
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
from core.logger import Logger
|
||||
from core.read_config import read_config
|
||||
from core.information import WeatherInfo
|
||||
from core.language import Language
|
||||
from lib.buildGUIClass import InsertLog
|
||||
|
||||
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
server.bind(('127.0.0.1', 7898))
|
||||
server.listen(5)
|
||||
gui_log = InsertLog()
|
||||
|
||||
try:
|
||||
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
server.bind(('0.0.0.0', 7898))
|
||||
server.listen(5)
|
||||
except OSError as e:
|
||||
Logger.critical(e)
|
||||
gui_log.insert(e)
|
||||
|
||||
language = Language()
|
||||
|
||||
|
||||
def build_html():
|
||||
"""
|
||||
构建html主体
|
||||
:return:
|
||||
"""
|
||||
settings = read_config()
|
||||
city = settings[3]['city-name']
|
||||
mode = settings[1]['mode']
|
||||
@@ -47,7 +61,7 @@ def build_html():
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<title>QWeather|网页查看天气服务</title>
|
||||
</head>
|
||||
<body>
|
||||
<p style="text-align: center"><i><b>地区:{city}</b></i></p>
|
||||
@@ -115,8 +129,8 @@ def build_html():
|
||||
<th> </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>风速/风级/风向</th>
|
||||
<th>相对湿度</th>
|
||||
<th>风速/级/向</th>
|
||||
<th>湿度</th>
|
||||
<th>紫外线</th>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -129,8 +143,8 @@ def build_html():
|
||||
</tr>
|
||||
<tr>
|
||||
<th>能见度</th>
|
||||
<th>大气压强</th>
|
||||
<th>相对云量</th>
|
||||
<th>压强</th>
|
||||
<th>云量</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{vis}km </td>
|
||||
@@ -192,7 +206,7 @@ def build_html():
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<title>QWeather|网页查看天气服务</title>
|
||||
</head>
|
||||
<body>
|
||||
<p style="text-align: center"><i><b>地区:{city}</b></i></p>
|
||||
@@ -245,8 +259,8 @@ def build_html():
|
||||
</tr>
|
||||
<tr>
|
||||
<th>能见度</th>
|
||||
<th>大气压强</th>
|
||||
<th>相对云量</th>
|
||||
<th>压强</th>
|
||||
<th>云量</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{vis}km </td>
|
||||
@@ -288,25 +302,43 @@ def build_html():
|
||||
return "You hadn't selected a mode"
|
||||
|
||||
|
||||
def process_request():
|
||||
def process_requests(c, a):
|
||||
"""
|
||||
处理请求
|
||||
:param c: connection
|
||||
:param a: address
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
while True:
|
||||
c, a = server.accept()
|
||||
data = str(c.recv(1024)).split(':')[0][6:][:-17]
|
||||
html = build_html()
|
||||
if data == '/':
|
||||
c.send('HTTP1.1/ 200 OK\r\n\r\n'.encode('utf-8'))
|
||||
c.send(html.encode('utf-8'))
|
||||
Logger.info(f'{a}: Get {data} --by browser')
|
||||
else:
|
||||
try:
|
||||
with open(f'.{data}', 'rb') as f:
|
||||
c.send('HTTP1.1/ 200 OK\r\n\r\n'.encode('utf-8'))
|
||||
c.send(f.read())
|
||||
Logger.info(f'{a}: Get {data} --by browser')
|
||||
except FileNotFoundError:
|
||||
c.send(f'HTTP1.1/ 404 Not Found\r\n\r\n{html}'.encode('utf-8'))
|
||||
c.close()
|
||||
data = str(c.recv(1024)).split(':')[0][6:][:-17]
|
||||
html = build_html()
|
||||
if data == '/': # 判断用户请求的目标是否为根目录, 如果是则返回html; 如果不是则继续判断
|
||||
c.send('HTTP/1.1 200 OK\r\n\r\n'.encode('utf-8'))
|
||||
c.send(html.encode('utf-8'))
|
||||
Logger.info(f'{language["get_resource"]} {data} {language["get_resource_from"]} {a[0]}:{a[1]}')
|
||||
gui_log.insert(f'{language["get_resource"]} {data} {language["get_resource_from"]} {a[0]}:{a[1]}')
|
||||
try:
|
||||
with open(f'./{data}', 'rb') as f:
|
||||
c.send('HTTP/1.1 200 OK\r\n\r\n'.encode('utf-8'))
|
||||
c.send(f.read())
|
||||
Logger.info(f'{language["get_resource"]} {data} {language["get_resource_from"]} {a[0]}:{a[1]}')
|
||||
gui_log.insert(f'{language["get_resource"]} {data} {language["get_resource_from"]} {a[0]}:{a[1]}')
|
||||
except FileNotFoundError:
|
||||
with open('./res/basic-resources/404.html', 'r') as not_found:
|
||||
c.send(f'HTTP/1.1 404 Not Found\r\n\r\n{not_found.read()}'.encode('utf-8'))
|
||||
except BrokenPipeError:
|
||||
Logger.critical('Link speed was too fast! Subprocess:webserver exited')
|
||||
sys.exit(1)
|
||||
Logger.error(f'{language["connection_speed_too_fast"]}')
|
||||
gui_log.insert(f'{language["connection_speed_too_fast"]}')
|
||||
finally:
|
||||
c.close()
|
||||
return
|
||||
|
||||
|
||||
def accept_requests():
|
||||
"""
|
||||
接受请求
|
||||
:return:
|
||||
"""
|
||||
while True:
|
||||
c, a = server.accept()
|
||||
threading.Thread(target=process_requests, args=(c, a,)).start()
|
||||
@@ -1,4 +1,5 @@
|
||||
requests==2.26.0
|
||||
ruamel.yaml==0.17.17
|
||||
pandas==1.3.4
|
||||
colorlog==6.5.0
|
||||
requests>=2.26.0
|
||||
ruamel.yaml>=0.17.17
|
||||
pandas>=1.3.4
|
||||
colorlog>=6.5.0
|
||||
openpyxl>=3.0.9
|
||||
|
||||
10
res/basic-resources/404.html
Normal file
10
res/basic-resources/404.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>QWeather|网页查看天气服务</title>
|
||||
</head>
|
||||
<body>
|
||||
<i><b><p style="text-align: center; color: red">404 File Not Found</p></b></i>
|
||||
</body>
|
||||
</html>
|
||||
@@ -28,5 +28,14 @@
|
||||
"no_result": "Nothing has searched.",
|
||||
"config_file_read_successfully": "Read config file successfully.",
|
||||
"language_file_read_successfully": "Multi-language module load successfully",
|
||||
"language_not_filled": "Language hasn't filled, default: zh_cn"
|
||||
"language_not_filled": "Language hasn't filled, default: zh_cn.",
|
||||
"no_location": "You haven't fill the location in the config file.",
|
||||
"change_setting": "You are changing setting.",
|
||||
"exit": "Exited.",
|
||||
"webservice_ip": "Local webservice ip",
|
||||
"connection_speed_too_fast": "Connection speed is too fast",
|
||||
"noname": "No name.",
|
||||
"hitokoto": "Get hitokoto successfully.",
|
||||
"get_resource": "Get resource",
|
||||
"get_resource_from": "from"
|
||||
}
|
||||
|
||||
@@ -28,5 +28,13 @@
|
||||
"no_result": "没有搜索到相关结果",
|
||||
"config_file_read_successfully": "配置文件读取成功",
|
||||
"language_file_read_successfully": "多语言模块加载成功",
|
||||
"language_not_filled": "语言选项为填写完成或填写错误, 默认zh_cn"
|
||||
}
|
||||
"language_not_filled": "语言选项为填写完成或填写错误, 默认zh_cn",
|
||||
"change_setting": "更改设置",
|
||||
"exit": "已退出",
|
||||
"webservice_ip": "webservice地址",
|
||||
"connection_speed_too_fast": "连接速度过快",
|
||||
"noname": "佚名",
|
||||
"hitokoto": "一言获取成功",
|
||||
"get_resource": "获取资源",
|
||||
"get_resource_from": "来自"
|
||||
}
|
||||
Reference in New Issue
Block a user