File size: 4,361 Bytes
346533a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# 新表情编写指北

## 表情注册

meme-generator 会以包的形式加载表情,通过 `add_meme` 函数来“注册”一个表情

以 `petpet` 表情为例,文件结构如下:

```
meme_generator/memes/petpet
├── __init__.py  # 表情制作程序
└── images  # 表情需要的图片文件
    ├── 0.png
    ├── 1.png
    ├── 2.png
    ├── 3.png
    └── 4.png
```

在不考虑额外参数的情况下,`petpet` 表情的 `__init__.py` 编写如下:

```python
from typing import List
from pathlib import Path
from pil_utils import BuildImage
from PIL.Image import Image as IMG

from meme_generator.utils import save_gif
from meme_generator import add_meme


img_dir = Path(__file__).parent / "images"


def petpet(images: List[BuildImage], texts, args):
    """表情制作函数

    函数会接收 3 个参数:
    - `images`: 传入的图片列表,类型为 `pil_utils.BuildImage`
    - `texts`: 传入的文字列表,类型为 `str`
    - `args`: 其他参数,类型为 `meme_generator.meme.MemeArgsModel`
    """
    img = images[0].convert("RGBA").square()
    frames: List[IMG] = []
    locs = [
        (14, 20, 98, 98),
        (12, 33, 101, 85),
        (8, 40, 110, 76),
        (10, 33, 102, 84),
        (12, 20, 98, 98),
    ]
    for i in range(5):
        hand = BuildImage.open(img_dir / f"{i}.png")
        frame = BuildImage.new("RGBA", hand.size, (255, 255, 255, 0))
        x, y, w, h = locs[i]
        frame.paste(img.resize((w, h)), (x, y), alpha=True)
        frame.paste(hand, alpha=True)
        frames.append(frame.image)
    return save_gif(frames, 0.06)


add_meme(
    "petpet",  # 表情唯一名
    petpet,  # 表情制作函数
    min_images=1,  # 至少需要 1 张图片
    max_images=1,  # 另有 `min_texts` 和 `max_texts` 选项来控制传入文字的数量
    keywords=["摸", "摸摸", "摸头", "rua"],  # 关键词,填写言简意赅的词语,用于展示表情含义、方便聊天Bot调用等
)
```

通常情况下,建议每个表情一个文件夹,表情所需的图片文件等都放置于该文件夹中,方便增删表情

也可以一个文件中注册多个表情,如:[gif_subtitle](../meme_generator/memes/gif_subtitle/__init__.py)


## 参数定义

部分表情需要额外的参数。表情参数的类型定义如下:

```python
@dataclass
class MemeArgsType:
    parser: MemeArgsParser  # 参数解析器,将命令行形式的文本解析为字典形式,方便通过命令行使用
    model: Type[MemeArgsModel]  # 参数模型,用于验证字典形式的参数,并传入表情制作函数
    instances: List[MemeArgsModel] = field(default_factory=list)  # 可选,参数模型示例,推荐填写,方便生成不同参数下的预览图
````petpet` 表情为例,需要定义一个控制图片是否变为圆形的参数 `circle`

可以定义如下的 `pydantic` 模型:

```python
from pydantic import Field
from meme_generator import MemeArgsModel

class Model(MemeArgsModel):
    circle: bool = Field(False, description="是否将图片变为圆形")
```

定义参数时推荐使用 `Field` 定义默认值,可以定义 `description` 描述参数含义,方便生成文档

同时定义如下的参数解析器:

```python
from meme_generator import MemeArgsParser

parser = MemeArgsParser(prefix_chars="-/")
parser.add_argument("--circle", "/圆", action="store_true", help="是否将图片变为圆形")
```

以上参数解析器可以将形如 `["--circle"]` 的参数列表解析为 `{"circle": true}` 的形式,继而通过 `pydantic` 模型验证

推荐在定义选项时添加自然语言风格的别名,如 `/圆`,这样可以方便聊天机器人等场合调用,比如可以解析 `摸头 /圆` 这样的文本

定义好上述的 `parser``Model` 后,需要在 `add_meme` 时传入:

```python
add_meme(
    "petpet",
    petpet,
    min_images=1,
    max_images=1,
    args_type=MemeArgsType(
        parser,
        Model,
        [
            Model(circle=False),
            Model(circle=True),
        ],
    ),
    keywords=["摸", "摸摸", "摸头", "rua"],
)
```

这里传入了 `circle=False``circle=True` 两个模型实例,可以在生成文档时生成不同参数时的预览图,效果如 [memes.md](memes.md#petpet) 所示