diff --git a/README.md b/README.md index 0bab08e3..94d97a00 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,14 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能 ## 更新 +### 2022/11/19 + +* 修改优化帮助图片生成逻辑 + +### 2022/11/18 + +* poetry添加适配器依赖,更新支持py3.10 [@pull/1176](https://github.com/HibiKier/zhenxun_bot/pull/1176) + ### 2022/11/13 * 更新天气api diff --git a/basic_plugins/help/data_source.py b/basic_plugins/help/data_source.py index 69d0149c..3876531e 100755 --- a/basic_plugins/help/data_source.py +++ b/basic_plugins/help/data_source.py @@ -1,3 +1,4 @@ +from .utils import group_image, build_help_image from utils.image_utils import BuildImage from configs.path_config import IMAGE_PATH from utils.manager import ( @@ -6,14 +7,11 @@ from utils.manager import ( plugins_manager, group_manager, ) -from typing import Optional +from typing import Optional, List, Tuple from services.log import logger from pathlib import Path from utils.utils import get_matchers -import random -import asyncio import nonebot -import os random_bk_path = IMAGE_PATH / "background" / "help" / "simple_help" @@ -30,12 +28,10 @@ async def create_help_img( :param help_image: 图片路径 :param simple_help_image: 简易帮助图片路径 """ - return await asyncio.get_event_loop().run_in_executor( - None, _create_help_img, group_id, help_image, simple_help_image - ) + return await _create_help_img(group_id, help_image, simple_help_image) -def _create_help_img( +async def _create_help_img( group_id: Optional[int], help_image: Path, simple_help_image: Path ): """ @@ -257,23 +253,8 @@ def _create_help_img( if img.h > height: height = img.h width += img.w + 10 - B = BuildImage(width + 100, height + 250, font_size=24) - width, _ = get_max_width_or_paste(simple_help_img_list, B) - width = width if width > 870 else 870 - bk = None - random_bk = os.listdir(random_bk_path) - if random_bk: - bk = random.choice(random_bk) - x = max(width + 50, height + 250) - B = BuildImage( - x, - x, - font_size=24, - color="#FFEFD5", - background=random_bk_path / bk, - ) - B.filter("GaussianBlur", 10) - _, B = get_max_width_or_paste(simple_help_img_list, B, True) + image_group, h = group_image(simple_help_img_list) + B = await build_help_image(image_group, h) w = 10 h = 10 for msg in ["目前支持的功能列表:", "可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法", "或者使用 ‘详细帮助’ 来获取所有功能方法"]: @@ -304,8 +285,8 @@ def _create_help_img( def get_max_width_or_paste( - simple_help_img_list: list, B: BuildImage = None, is_paste: bool = False -) -> "int, BuildImage": + simple_help_img_list: List[BuildImage], B: BuildImage = None, is_paste: bool = False +) -> Tuple[int, BuildImage]: """ 获取最大宽度,或直接贴图 :param simple_help_img_list: 简单帮助图片列表 diff --git a/basic_plugins/help/utils.py b/basic_plugins/help/utils.py new file mode 100644 index 00000000..989756f4 --- /dev/null +++ b/basic_plugins/help/utils.py @@ -0,0 +1,105 @@ +from typing import List, Tuple +from configs.path_config import IMAGE_PATH +from utils.image_utils import BuildImage +import os +import random + + +background_path = IMAGE_PATH / "background" / "help" / "simple_help" + + +async def build_help_image(image_group: List[List[BuildImage]], h: int): + bk = None + random_bk = os.listdir(background_path) + if random_bk: + bk = random.choice(random_bk) + A = BuildImage( + h, + h, + font_size=24, + color="#FFEFD5", + background=(background_path / bk) if bk else None, + ) + curr_w = 50 + for ig in image_group: + curr_h = 180 + for img in ig: + await A.apaste(img, (curr_w, curr_h), True) + curr_h += img.h + 10 + curr_w += max([x.w for x in ig]) + 30 + return A + + +def group_image(image_list: List[BuildImage]) -> Tuple[List[List[BuildImage]], int]: + """ + 说明: + 根据图片大小进行分组 + 参数: + :param image_list: 排序图片列表 + """ + image_list.sort(key=lambda x: x.h, reverse=True) + max_image = max(image_list, key=lambda x: x.h) + + image_list.remove(max_image) + max_h = max_image.h + total_w = 0 + + # 图片分组 + image_group = [[max_image]] + is_use = [] + surplus_list = image_list[:] + + for image in image_list: + if image.uid not in is_use: + group = [image] + is_use.append(image.uid) + curr_h = image.h + while True: + surplus_list = [x for x in surplus_list if x.uid not in is_use] + for tmp in surplus_list: + temp_h = curr_h + tmp.h + 10 + if temp_h < max_h or abs(max_h - temp_h) < 100: + curr_h += tmp.h + 15 + is_use.append(tmp.uid) + group.append(tmp) + break + else: + break + total_w += max([x.w for x in group]) + 15 + image_group.append(group) + while surplus_list: + surplus_list = [x for x in surplus_list if x.uid not in is_use] + if not surplus_list: + break + surplus_list.sort(key=lambda x: x.h, reverse=True) + for img in surplus_list: + if img.uid not in is_use: + _w = 0 + index = -1 + for i, ig in enumerate(image_group): + if s := sum([x.h for x in ig]) > _w: + _w = s + index = i + if index != -1: + image_group[index].append(img) + is_use.append(img.uid) + max_h = 0 + max_w = 0 + for i, ig in enumerate(image_group): + if (_h := sum([x.h + 15 for x in ig])) > max_h: + max_h = _h + max_w += max([x.w for x in ig]) + 30 + is_use.clear() + while abs(max_h - max_w) > 200 and len(image_group) - 1 >= len(image_group[-1]): + for img in image_group[-1]: + _min_h = 0 + _min_index = -1 + for i, ig in enumerate(image_group): + if i not in is_use and (_h := sum([x.h for x in ig]) + img.h) > _min_h: + _min_h = _h + _min_index = i + is_use.append(_min_index) + image_group[_min_index].append(img) + max_w -= max([x.w for x in image_group[-1]]) + image_group.pop(-1) + return image_group, max(max_h + 250, max_w + 70) diff --git a/utils/image_utils.py b/utils/image_utils.py index 89d73147..1b0d3ca7 100755 --- a/utils/image_utils.py +++ b/utils/image_utils.py @@ -2,11 +2,12 @@ import asyncio import base64 import random import re +import time from io import BytesIO from math import ceil from pathlib import Path from typing import List, Literal, Optional, Tuple, Union - +import uuid import cv2 import imagehash from configs.path_config import FONT_PATH, IMAGE_PATH @@ -180,6 +181,7 @@ class BuildImage: self.paste_image_height = int(paste_image_height) self._current_w = 0 self._current_h = 0 + self.uid = uuid.uuid1() self.font = ImageFont.truetype(str(FONT_PATH / font), int(font_size)) if not plain_text and not color: color = (255, 255, 255)