Files
starbot/starbot/painter/RankingGenerator.py
T

184 lines
8.5 KiB
Python

from typing import Optional, Union, Tuple, List
from PIL import Image, ImageDraw
from .PicGenerator import Color, PicGenerator
from ..utils.utils import mask_round
class RankingGenerator:
"""
排行榜图片生成器
"""
@classmethod
def __get_rank_bar_pic(cls,
width: int,
height: int,
start_color: Union[Color, Tuple[int, int, int]] = Color.DEEPBLUE,
end_color: Union[Color, Tuple[int, int, int]] = Color.LIGHTBLUE,
reverse: bool = False) -> Image:
"""
生成排行榜中排行条图片
Args:
width: 排行条长度
height: 排行条宽度
start_color: 排行条渐变起始颜色。默认:深蓝色 (57, 119, 230)
end_color: 排行条渐变终止颜色。默认:浅蓝色 (55, 187, 248)
reverse: 是否生成反向排行条,用于双向排行榜的负数排行条。默认:False
"""
if isinstance(start_color, Color):
start_color = start_color.value
if isinstance(end_color, Color):
end_color = end_color.value
if reverse:
start_color, end_color = end_color, start_color
r_step = (end_color[0] - start_color[0]) / width
g_step = (end_color[1] - start_color[1]) / width
b_step = (end_color[2] - start_color[2]) / width
now_color = [start_color[0], start_color[1], start_color[2]]
bar = Image.new("RGBA", (width, 1))
draw = ImageDraw.Draw(bar)
for i in range(width):
draw.point((i, 0), (int(now_color[0]), int(now_color[1]), int(now_color[2])))
now_color[0] += r_step
now_color[1] += g_step
now_color[2] += b_step
bar = bar.resize((width, height))
mask = Image.new("L", (width, height), 255)
mask_draw = ImageDraw.Draw(mask)
if not reverse:
mask_draw.polygon(((width - height, height), (width, 0), (width, height)), 0)
else:
mask_draw.polygon(((0, 0), (0, height), (height, height)), 0)
bar.putalpha(mask)
mask.close()
return bar
@classmethod
def get_ranking(cls,
row_space: int,
faces: List[Image.Image],
unames: List[str],
counts: Union[List[int], List[float]],
width: int,
top_count: Optional[Union[int, float]] = None,
start_color: Union[Color, Tuple[int, int, int]] = Color.DEEPBLUE,
end_color: Union[Color, Tuple[int, int, int]] = Color.LIGHTBLUE) -> Image:
"""
绘制排行榜
Args:
row_space: 行间距
faces: 头像图片列表,按照数量列表降序排序
unames: 昵称列表,按照数量列表降序排序
counts: 数量列表,降序排序
width: 排行榜图片宽度
top_count: 第一名数量,后续排行条长度会基于此数量计算长度。默认:自动取数量列表中第一名
start_color: 排行条渐变起始颜色。默认:深蓝色 (57, 119, 230)
end_color: 排行条渐变终止颜色。默认:浅蓝色 (55, 187, 248)
"""
count = len(counts)
if count == 0 or len(faces) != len(unames) or len(unames) != len(counts):
raise ValueError("绘制排行榜错误, 头像昵称列表与数量列表长度不匹配")
face_size = 100
offset = 10
bar_height = 30
bar_x = face_size - offset
top_bar_width = width - face_size + offset
if top_count is None:
top_count = counts[0]
chart = PicGenerator(width, (face_size * count) + (row_space * (count - 1)))
chart.set_row_space(row_space)
for i in range(count):
bar_width = int(counts[i] / top_count * top_bar_width)
if bar_width != 0:
bar = cls.__get_rank_bar_pic(bar_width, bar_height, start_color, end_color)
chart.draw_img_alpha(bar, (bar_x, chart.y + int((face_size - bar_height) / 2)))
chart.draw_tip(unames[i], Color.BLACK, (bar_x + (offset * 2), chart.y))
count_pos = (max(chart.x + bar_width, bar_x + (offset * 3) + chart.get_tip_length(unames[i])), chart.y)
chart.draw_tip(str(counts[i]), xy=count_pos)
chart.draw_img_alpha(mask_round(faces[i].resize((face_size, face_size)).convert("RGBA")))
return chart.img
@classmethod
def get_double_ranking(cls,
row_space: int,
faces: List[Image.Image],
unames: List[str],
counts: Union[List[int], List[float]],
width: int,
top_count: Optional[Union[int, float]] = None,
start_color: Union[Color, Tuple[int, int, int]] = Color.DEEPRED,
end_color: Union[Color, Tuple[int, int, int]] = Color.LIGHTRED,
reverse_start_color: Union[Color, Tuple[int, int, int]] = Color.DEEPGREEN,
reverse_end_color: Union[Color, Tuple[int, int, int]] = Color.LIGHTGREEN) -> Image:
"""
绘制双向排行榜
Args:
row_space: 行间距
faces: 头像图片列表,按照数量列表降序排序
unames: 昵称列表,按照数量列表降序排序
counts: 数量列表,降序排序
width: 排行榜图片宽度
top_count: 第一名数量,后续排行条长度会基于此数量计算长度。默认:自动取数量列表中第一名
start_color: 正向排行条渐变起始颜色,数量为正时使用。默认:深红色 (57, 119, 230)
end_color: 正向排行条渐变终止颜色,数量为正时使用。默认:浅红色 (55, 187, 248)
reverse_start_color: 反向排行条渐变起始颜色,数量为负时使用。默认:深绿色 (57, 119, 230)
reverse_end_color: 反向排行条渐变终止颜色,数量为负时使用。默认:浅绿色 (55, 187, 248)
"""
count = len(counts)
if count == 0 or len(faces) != len(unames) or len(unames) != len(counts):
raise ValueError("绘制排行榜错误, 头像昵称列表与数量列表长度不匹配")
face_size = 100
offset = 10
bar_height = 30
face_x = int((width - face_size) / 2)
bar_x = face_x + face_size - offset
reverse_bar_x = face_x + offset
top_bar_width = (width - face_size) / 2 + offset
if top_count is None:
top_count = max(max(counts), abs(min(counts)))
chart = PicGenerator(width, (face_size * count) + (row_space * (count - 1)))
chart.set_row_space(row_space)
for i in range(count):
bar_width = int(abs(counts[i]) / top_count * top_bar_width) if top_count != 0 else 0.1
if bar_width != 0:
if counts[i] > 0:
bar = cls.__get_rank_bar_pic(bar_width, bar_height, start_color, end_color)
chart.draw_img_alpha(bar, (bar_x, chart.y + int((face_size - bar_height) / 2)))
elif counts[i] < 0:
bar = cls.__get_rank_bar_pic(bar_width, bar_height, reverse_start_color, reverse_end_color, True)
chart.draw_img_alpha(bar, (reverse_bar_x - bar_width, chart.y + int((face_size - bar_height) / 2)))
if counts[i] >= 0:
chart.draw_tip(unames[i], Color.BLACK, (bar_x + (offset * 2), chart.y))
count_pos = (max(face_x + bar_width, bar_x + (offset * 3) + chart.get_tip_length(unames[i])), chart.y)
chart.draw_tip(str(counts[i]), xy=count_pos)
else:
uname_length = chart.get_tip_length(unames[i])
count_length = chart.get_tip_length(str(counts[i]))
chart.draw_tip(unames[i], Color.BLACK, (reverse_bar_x - (offset * 2) - uname_length, chart.y))
count_pos = (min(face_x + face_size - bar_width - count_length,
reverse_bar_x - (offset * 3) - uname_length - count_length), chart.y)
chart.draw_tip(str(counts[i]), xy=count_pos)
chart.set_pos(x=face_x).draw_img_alpha(mask_round(faces[i].resize((face_size, face_size)).convert("RGBA")))
chart.set_pos(x=0)
return chart.img