From 9d69de92f2008fa23b29401e177ac15ad85fca54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=9F=E5=85=89-k?= <36470587+kxgx@users.noreply.github.com> Date: Thu, 23 Jan 2025 08:30:19 +0800 Subject: [PATCH] Update main.py --- bin/main.py | 505 +++++++++++----------------------------------------- 1 file changed, 105 insertions(+), 400 deletions(-) diff --git a/bin/main.py b/bin/main.py index 7fdcab7..0557c9d 100644 --- a/bin/main.py +++ b/bin/main.py @@ -1,24 +1,14 @@ #!/usr/bin/env python # -*- coding:utf-8 -*- -from flask import Flask, render_template_string, request, Response, redirect, url_for from PIL import Image, ImageDraw, ImageFont # 引入图片处理库 -import os, sys, re, json, time, datetime # 引入系统相关库 +import os, sys, json, time, datetime # 引入系统相关库 from borax.calendars.lunardate import LunarDate # 农历日期以及天干地支纪年法的 Python 库 import logging # 日志库 import subprocess -import io # 导入 io 模块 -from threading import Thread - -app = Flask(__name__) - -# 设置Flask日志级别为WARNING,减少日志输出 -log = logging.getLogger('werkzeug') -log.setLevel(logging.WARNING) white = 255 # 颜色 black = 0 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') - ################################引入配置文件开始################################################ picdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'pic') libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib') @@ -34,346 +24,104 @@ font05 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 12) # 字体文 font06 = ImageFont.truetype(os.path.join(picdir, '原神cn.ttf'), 13) # 字体文件 ################################引入配置文件结束################################################ -# 定义元素位置配置 -positions = { - "date": (2, 2), - "time": (5, 28), - "battery_icon_positions": [ - (126, 109), (154, 109), - (126, 110), (126, 119), - (127, 119), (154, 119), - (154, 110), (154, 118), - (155, 112), (157, 112), - (155, 116), (157, 116), - (157, 113), (157, 115) - ], - "power_str_position": (129, 108), - "clock_icon_position": [(192, 107), (207, 120)], - "clock_hand_positions": [ - (199, 109), (199, 114), - (200, 114), (204, 114) - ], - "ip_address_position": (10, 107), - "weather_prefix_position": { - "天气": (150, 25), - "温度": (150, 45), - "湿度": (150, 65), - "城市": (150, 85) - }, - "weather_value_position": { - "weather": (191, 25), - "temperature": (191, 45), - "humidity": (191, 65), - "cityname": (191, 85) - }, - "weather_update_position": (211, 107), - "bottom_edge_position": [(0, 105), (250, 122)] -} + +def Local_strong_brush(): # 局部强制刷新显示 + for _ in range(5): + epd.displayPartial(epd.getbuffer(info_image.rotate(180))) + def get_date(): # 返回当前年月日及星期几 date = datetime.datetime.now() today = LunarDate.today() week_day_dict = {0: '星期一', 1: '星期二', 2: '星期三', 3: '星期四', 4: '星期五', 5: '星期六', 6: '星期日'} day = date.weekday() - return time.strftime('%Y年%m月%d日') + '' + week_day_dict[day] + '' + today.strftime('农历%M月%D') + return f"{date.strftime('%Y年%m月%d日')}{week_day_dict[day]}{today.strftime('农历%M月%D')}" + + +def get_time(): + return time.strftime('%H:%M').upper() -def get_time(): # 返回当前时间,不到秒,大写 - return time.strftime('%H:%M') def Get_ipv4_address(): # 获取当前的IP地址 try: - # 执行命令获取IP地址,并处理输出以仅返回IPv4地址 ip_output = subprocess.check_output( "hostname -I | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}'", shell=True).decode('utf-8').strip() - # 分割输出以获取单个IP地址列表 ip_list = ip_output.split() - # 过滤掉以172开头的IP地址 filtered_ips = [ip for ip in ip_list if not ip.startswith("172.")] - # 如果有有效的IP地址,返回第一个,否则返回获取失败 - if filtered_ips: - return filtered_ips[0] - else: - return "地址获取失败" - except subprocess.CalledProcessError as e: + return filtered_ips[0] if filtered_ips else "地址获取失败" + except subprocess.CalledProcessError: return "获取失败" + def power_battery(): # 获取当前电池电量 return str(int(subprocess.check_output( u"echo \"get battery\" | nc -q 0 127.0.0.1 8423|awk -F':' '{print int($2)}'", shell=True).decode('gbk'))) + u'%' -def Bottom_edge(draw): # 在图片中添加底边内容 - draw.rectangle(positions["bottom_edge_position"], 'black', 'black') - '''电池图标画图''' - for pos in positions["battery_icon_positions"]: - if len(pos) == 4: - draw.line(pos, fill=255, width=1) - elif len(pos) == 2: - draw.point(pos, fill=255) +def Bottom_edge(): # 在图片中添加底边内容 + draw.rectangle((0, 105, 250, 122), 'black', 'black') + '''电池图标画图''' + draw.line((126, 109, 154, 109), fill=255, width=1) # 电池顶边 + draw.line((126, 110, 126, 119), fill=255, width=1) # 电池左边 + draw.line((127, 119, 154, 119), fill=255, width=1) # 电池下边 + draw.line((154, 110, 154, 118), fill=255, width=1) + draw.line((155, 112, 157, 112), fill=255, width=1) + draw.line((155, 116, 157, 116), fill=255, width=1) + draw.line((157, 113, 157, 115), fill=255, width=1) global power_str power_str = power_battery() - draw.text(positions["power_str_position"], power_str, font=font04, fill=255) # 显示当前电量百分比 - + draw.text((129, 108), power_str, font=font04, fill=255) # 显示当前电量百分比 '''电池图标画图''' - draw.ellipse(positions["clock_icon_position"], 0, 255) # 时钟图标 - for hand_pos in positions["clock_hand_positions"]: - draw.line(hand_pos, fill=255, width=1) - + draw.ellipse((192, 107, 207, 120), 0, 255) # 时钟图标 + draw.line((199, 109, 199, 114), fill=255, width=1) + draw.line((200, 114, 204, 114), fill=255, width=1) global local_addr # 获取当前IP地址 local_addr = Get_ipv4_address() # 获取当前IP地址 - draw.text(positions["ip_address_position"], "IP:" + local_addr, font=font05, fill=255) # 显示当前IP地址 + draw.text((10, 107), f"IP:{local_addr}", font=font05, fill=255) # 显示当前IP地址 -def Weather(draw): # 在图片中添加天气内容 - with open('/root/2.13-Ink-screen-clock/bin/weather.json', 'r') as f: - Weather_data = f.read() - Weather_text = json.loads(Weather_data) - global weather_position - global temperature - global weather - global wind_direction - global weather_update - global weather_date - global humidity - weather_position = Weather_text['cityname'] # 定位位置 - temperature = Weather_text['temp'] + u'°C' # 温度 - weather = Weather_text['weather'] # 天气情况 - wind_direction = Weather_text['WD'] # 风向 - weather_update = Weather_text['time'] # 天气更新时间 - weather_date = Weather_text['date'] # 日期 - humidity = Weather_text['SD'] # 湿度 - draw.text(positions["weather_prefix_position"]["天气"], "天气:", font=font06, fill=0) # 显示当前天气前缀 - draw.text(positions["weather_prefix_position"]["温度"], "温度:", font=font06, fill=0) # 显示当前温度前缀 - draw.text(positions["weather_prefix_position"]["湿度"], "湿度:", font=font06, fill=0) # 显示当前湿度前缀 - draw.text(positions["weather_prefix_position"]["城市"], "城市:", font=font06, fill=0) # 显示当前城市前缀 - draw.text(positions["weather_value_position"]["weather"], weather, font=font06, fill=0) - draw.text(positions["weather_value_position"]["temperature"], temperature, font=font06, fill=0) - draw.text(positions["weather_value_position"]["humidity"], humidity, font=font06, fill=0) - draw.text(positions["weather_value_position"]["cityname"], weather_position, font=font06, fill=0) - draw.text(positions["weather_update_position"], weather_update, font=font05, fill=255) # 显示天气更新时间 +def Weather(): # 在图片中添加天气内容 + with open('/root/2.13-Ink-screen-clock/bin/weather.json', 'r') as file: + weather_data = json.load(file) + global Weather_position, temperature, weather, wind_direction, weather_update, weather_date, humidity + Weather_position = weather_data['cityname'] # 定位位置 + temperature = weather_data['temp'] + u'°C' # 温度 + weather = weather_data['weather'] # 天气情况 + wind_direction = weather_data['WD'] # 风向 + weather_update = weather_data['time'] # 天气更新时间 + weather_date = weather_data['date'] # 日期 + humidity = weather_data['SD'] # 湿度 + draw.text((150, 25), "天气:", font=font06, fill=0) # 显示当前天气前缀 + draw.text((150, 45), "温度:", font=font06, fill=0) # 显示当前温度前缀 + draw.text((150, 65), "湿度:", font=font06, fill=0) # 显示当前湿度前缀 + draw.text((150, 85), "城市:", font=font06, fill=0) # 显示当前城市前缀 + draw.text((191, 25), weather, font=font06, fill=0) + draw.text((191, 45), temperature, font=font06, fill=0) + draw.text((191, 65), humidity, font=font06, fill=0) + draw.text((191, 85), Weather_position, font=font06, fill=0) + draw.text((211, 107), weather_update, font=font05, fill=255) # 显示天气更新时间 -def generate_image(): - width, height = 250, 122 # 设置画布大小 - info_image = Image.new('1', (width, height), white) # 创建白色背景的画布 - draw = ImageDraw.Draw(info_image) - date_var = get_date() # 记录开始数据 - draw.text(positions["date"], date_var, font=font02, fill=0) # 将日期及星期几显示到屏幕 - - local_time = get_time() - draw.text(positions["time"], local_time, font=font03, fill=0) # 显示当前时间 - - Bottom_edge(draw) # 添加底边内容 - Weather(draw) # 天气内容 - - img_byte_arr = io.BytesIO() - info_image.save(img_byte_arr, format='PNG') - img_byte_arr.seek(0) - return img_byte_arr.getvalue() - -@app.route('/') -def index(): - return render_template_string(''' - - - - 电子墨水屏内容 - - - - -

电子墨水屏内容

-
- - 设置参数 -
- - -''') - -@app.route('/image') -def image(): - img = generate_image() - return Response(img, mimetype='image/png') - -@app.route('/settings', methods=['GET', 'POST']) -def settings(): - global positions, font01, font02, font03, font04, font05, font06 - - if request.method == 'POST': - # Update positions - positions["date"] = tuple(map(int, request.form.get('date_position').split(','))) - positions["time"] = tuple(map(int, request.form.get('time_position').split(','))) - positions["battery_icon_positions"] = list(map(lambda x: tuple(map(int, x.split(','))), request.form.getlist('battery_icon_positions'))) - positions["power_str_position"] = tuple(map(int, request.form.get('power_str_position').split(','))) - positions["clock_icon_position"] = list(map(lambda x: tuple(map(int, x.split(','))), request.form.getlist('clock_icon_position'))) - positions["clock_hand_positions"] = list(map(lambda x: tuple(map(int, x.split(','))), request.form.getlist('clock_hand_positions'))) - positions["ip_address_position"] = tuple(map(int, request.form.get('ip_address_position').split(','))) - positions["weather_prefix_position"]["天气"] = tuple(map(int, request.form.get('weather_prefix_天气').split(','))) - positions["weather_prefix_position"]["温度"] = tuple(map(int, request.form.get('weather_prefix_温度').split(','))) - positions["weather_prefix_position"]["湿度"] = tuple(map(int, request.form.get('weather_prefix_湿度').split(','))) - positions["weather_prefix_position"]["城市"] = tuple(map(int, request.form.get('weather_prefix_城市').split(','))) - positions["weather_value_position"]["weather"] = tuple(map(int, request.form.get('weather_value_weather').split(','))) - positions["weather_value_position"]["temperature"] = tuple(map(int, request.form.get('weather_value_temperature').split(','))) - positions["weather_value_position"]["humidity"] = tuple(map(int, request.form.get('weather_value_humidity').split(','))) - positions["weather_value_position"]["cityname"] = tuple(map(int, request.form.get('weather_value_cityname').split(','))) - positions["weather_update_position"] = tuple(map(int, request.form.get('weather_update_position').split(','))) - positions["bottom_edge_position"] = list(map(lambda x: tuple(map(int, x.split(','))), request.form.getlist('bottom_edge_position'))) - - # Update fonts - font01 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), int(request.form.get('font01_size'))) - font02 = ImageFont.truetype(os.path.join(picdir, 'GB2312.ttf'), int(request.form.get('font02_size'))) - font03 = ImageFont.truetype(os.path.join(picdir, 'Fonttt.ttf'), int(request.form.get('font03_size'))) - font04 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), int(request.form.get('font04_size'))) - font05 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), int(request.form.get('font05_size'))) - font06 = ImageFont.truetype(os.path.join(picdir, '原神cn.ttf'), int(request.form.get('font06_size'))) - - return redirect(url_for('index')) - - # Flatten positions dictionary for rendering - flat_positions = {} - for key, value in positions.items(): - if isinstance(value, dict): - for sub_key, sub_value in value.items(): - flat_positions[f"{key}_{sub_key}"] = sub_value - elif isinstance(value, list): - flat_positions[key] = ';'.join([','.join(map(str, item)) for item in value]) - else: - flat_positions[key] = value - - return render_template_string(''' - - - - 设置参数 - - -

设置参数

-
- -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -

- - -
- 返回主页 - - -''', flat_positions=flat_positions) - -def screen_updater(): - while True: - try: - epd.init() - Basic_refresh(epd) - Partial_refresh(epd) - epd.sleep() - except Exception as e: - logging.error(f"Screen update failed: {e}") - finally: - time.sleep(60) # 更新频率为每分钟一次 - -def Basic_refresh(epd): # 全刷函数 +def Basic_refresh(): # 全刷函数 logging.info("在启动画布之前,刷新并准备基本内容") # 开始画布前刷新准备基础内容 global get_date_var get_date_var = get_date() # 记录开始数据 - draw.text(positions["date"], get_date_var, font=font02, fill=0) # 将日期及星期几显示到屏幕 + draw.text((2, 2), get_date_var, font=font02, fill=0) # 将日期及星期几显示到屏幕 global local_time local_time = get_time() - draw.text(positions["time"], local_time, font=font03, fill=0) # 显示当前时间 - Bottom_edge(draw) # 添加底边内容 - Weather(draw) # 天气内容 + draw.text((5, 28), local_time, font=font03, fill=0) # 显示当前时间 + Bottom_edge() # 添加底边内容 + Weather() # 天气内容 epd.display(epd.getbuffer(info_image.rotate(180))) -def Partial_refresh(epd): # 局刷函数 + +def Partial_full_brush(): # 局部定时全刷函数 + Basic_refresh() # 全局刷新 + logging.debug("局部定时全局刷新") + + +def Partial_refresh(): # 局刷函数 logging.info("部分内容更新,此更新建议与分钟同步,以节省墨水屏的使用寿命") # 局部内容更新,此更新建议与分钟同步,以节省墨水屏寿命 epd.displayPartBaseImage(epd.getbuffer(info_image.rotate(180))) epd.init() @@ -382,133 +130,90 @@ def Partial_refresh(epd): # 局刷函数 local_time1 = get_time() if local_time1 != local_time: draw.rectangle((5, 28, 149, 82), fill=255) # 时间局刷区域 - draw.text(positions["time"], local_time1, font=font03, fill=0) # 刷新当前时间 + draw.text((5, 28), local_time1, font=font03, fill=0) # 刷新当前时间 local_time = local_time1 - Local_strong_brush(epd) # 局部强刷 - + Local_strong_brush() # 局部强刷 get_date_var1 = get_date() # 局刷判断,如果时间与前一次不一致说明内容变化,需要刷新显示 global get_date_var # 再次声明这个是全局变量 if get_date_var1 != get_date_var: draw.rectangle((2, 2, 250, 16), fill=255) # 设置头部刷新区域 - draw.text(positions["date"], get_date_var1, font=font02, fill=0) # 将日期及星期几刷新显示到屏幕 + draw.text((2, 2), get_date_var1, font=font02, fill=0) # 将日期及星期几刷新显示到屏幕 get_date_var = get_date_var1 # 将更新的值保存到初始变量,直到下一次变化时执行该刷新操作 logging.debug("头部日期部位发生刷新变化.") - Local_strong_brush(epd) # 局部强刷 - + Local_strong_brush() # 局部强刷 global local_addr # 当前IP地址 local_addr1 = Get_ipv4_address() if local_addr1 != local_addr: draw.rectangle((1, 107, 123, 120), fill=0) # 设置头部刷新区域 - draw.text(positions["ip_address_position"], "IP:" + local_addr1, font=font05, fill=255) # 显示当前IP地址 + draw.text((10, 107), f"IP:{local_addr1}", font=font05, fill=255) # 显示当前IP地址 local_addr = local_addr1 - Local_strong_brush(epd) # 局部强刷 - + Local_strong_brush() # 局部强刷 '''天气局部更新函数''' - with open('/root/2.13-Ink-screen-clock/bin/weather.json', 'r') as f: - Weather_data = f.read() - Weather_text = json.loads(Weather_data) - global weather_position - global temperature - global weather - global wind_direction - global weather_update - global weather_date - global humidity - weather_position1 = Weather_text['cityname'] # 定位位置 - temperature1 = Weather_text['temp'] + u'°C' # 温度 - weather11 = Weather_text['weather'] # 天气情况 - wind_direction1 = Weather_text['WD'] # 风向 - weather_update1 = Weather_text['time'] # 天气更新时间 - weather_date1 = Weather_text['date'] # 日期 - humidity1 = Weather_text['SD'] # 湿度 - + with open('/root/2.13-Ink-screen-clock/bin/weather.json', 'r') as file: + weather_data = json.load(file) + global Weather_position, temperature, weather, wind_direction, weather_update, weather_date, humidity + Weather_position1 = weather_data['cityname'] # 定位位置 + temperature1 = weather_data['temp'] + u'°C' # 温度 + weather11 = weather_data['weather'] # 天气情况 + humidity1 = weather_data['SD'] # 湿度 + weather_update1 = weather_data['time'] # 天气更新时间 if weather11 != weather: draw.rectangle((191, 25, 249, 38), fill=255) # 天气局刷区域 - draw.text(positions["weather_value_position"]["weather"], weather11, font=font06, fill=0) + draw.text((191, 25), weather11, font=font06, fill=0) weather = weather11 logging.info("天气局部刷新") - Local_strong_brush(epd) # 局部强刷 - + Local_strong_brush() # 局部强刷 if temperature1 != temperature: draw.rectangle((191, 45, 249, 57), fill=255) # 局刷区域 - draw.text(positions["weather_value_position"]["temperature"], temperature1, font=font06, fill=0) + draw.text((191, 45), temperature1, font=font06, fill=0) temperature = temperature1 logging.info("温度局部刷新") - Local_strong_brush(epd) # 局部强刷 - + Local_strong_brush() # 局部强刷 if humidity1 != humidity: draw.rectangle((191, 65, 249, 77), fill=255) # 局刷区域 - draw.text(positions["weather_value_position"]["humidity"], humidity1, font=font06, fill=0) + draw.text((191, 65), humidity1, font=font06, fill=0) humidity = humidity1 logging.info("湿度局部刷新") - Local_strong_brush(epd) # 局部强刷 - - if weather_position1 != weather_position: + Local_strong_brush() # 局部强刷 + if Weather_position1 != Weather_position: draw.rectangle((191, 85, 249, 98), fill=255) # 局刷区域 - draw.text(positions["weather_value_position"]["cityname"], weather_position1, font=font06, fill=0) - weather_position = weather_position1 + draw.text((191, 85), Weather_position1, font=font06, fill=0) + Weather_position = Weather_position1 logging.info("城市局部刷新") - Local_strong_brush(epd) # 局部强刷 - + Local_strong_brush() # 局部强刷 if weather_update1 != weather_update: draw.rectangle((211, 107, 248, 118), fill=0) # 设置更新时间刷新区域 - draw.text(positions["weather_update_position"], weather_update1, font=font05, fill=255) # 显示天气更新时间 + draw.text((211, 107), weather_update1, font=font05, fill=255) # 显示天气更新时间 weather_update = weather_update1 logging.info("天气更新时间局部刷新") - Local_strong_brush(epd) # 局部强刷 - + Local_strong_brush() # 局部强刷 '''天气局部更新函数''' global power_str power_str1 = power_battery() if power_str1 != power_str: draw.rectangle((128, 110, 153, 117), fill=0) # 设置更新时间刷新区域 - draw.text(positions["power_str_position"], power_str1, font=font04, fill=255) # 显示当前电量百分比 + draw.text((129, 108), power_str1, font=font04, fill=255) # 显示当前电量百分比 power_str = power_str1 - Local_strong_brush(epd) # 局部强刷 + Local_strong_brush() # 局部强刷 -def Local_strong_brush(epd): # 局部强制刷新显示 - i = 0 - while i < 5: - epd.displayPartial(epd.getbuffer(info_image.rotate(180))) # 局刷开始 - i = i + 1 -if __name__ == '__main__': - try: - epd = epd2in13_V4.EPD() # 初始化 - epd.init() # 设定屏幕刷新模式 - logging.info("Width = %s, Height = %s", format(epd.width), format(epd.height)) # 打印屏幕高度及宽度 - logging.info("初始化并清空显示屏") # 屏幕开始准备相关展示 - info_image = Image.new('1', (epd.height, epd.width), 255) # 画布创建准备 - draw = ImageDraw.Draw(info_image) - - # 启动屏幕更新线程 - screen_thread = Thread(target=screen_updater) - screen_thread.daemon = True - screen_thread.start() - - # 启动Flask应用 - app.run(host='0.0.0.0', port=5000) - except OSError as e: - logging.info(e) - except KeyboardInterrupt: - logging.info("检测到键盘中断,正在清理并退出") - epd.init() - epd.Clear(0xFF) # 清除屏幕内容 - epd.sleep() # 使屏幕进入休眠状态 - epd2in13_V4.epdconfig.module_exit() # 清理资源 - exit() - - except Exception as e: - logging.error("发生了意外的错误: %s", e) - epd.init() - epd.Clear(0xFF) # 清除屏幕内容 - epd.sleep() # 使屏幕进入休眠状态 - epd2in13_V4.epdconfig.module_exit() # 清理资源 - exit() - - # 脚本正常结束后的清理操作 +try: + ##################屏幕初始化######################### + epd = epd2in13_V4.EPD() # 初始化 + epd.init() # 设定屏幕刷新模式 + logging.info("Width = %s, Height = %s", format(epd.width), format(epd.height)) # 打印屏幕高度及宽度 + logging.info("初始化并清空显示屏") # 屏幕开始准备相关展示 + info_image = Image.new('1', (epd.height, epd.width), 255) # 画布创建准备 + draw = ImageDraw.Draw(info_image) + Basic_refresh() # 全局刷新 + Partial_refresh() # 局部刷新 +except OSError as e: + logging.info(e) +except KeyboardInterrupt: + logging.info("检测到键盘中断,正在清理并退出") +finally: epd.init() epd.Clear(0xFF) # 清除屏幕内容 epd.sleep() # 使屏幕进入休眠状态 epd2in13_V4.epdconfig.module_exit() # 清理资源 - exit() \ No newline at end of file + exit()