Skip to content

Commit f6aac0e

Browse files
committed
[icon-maker]: 新增Windows图标生成工具并重构图标生成代码
- 新增 `make_ico.py`: 支持生成Windows图标(.ico),包含多尺寸 - 重构: 将原 `make_icns` 工具移至 `icon-maker` 目录,并提取公共函数至 `icon_common.py` - 更新README: 添加 `make_ico` 工具说明
1 parent bad22ae commit f6aac0e

File tree

4 files changed

+106
-36
lines changed

4 files changed

+106
-36
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
| **dirwatch** | 实时监控文件夹变化(增 / 删 / 改 / 重命名) | [`dirwatch.py`](dirwatch/dirwatch.py) |
1212
| **m3u8_download** | m3u8 下载器,自动合并 ts 为单个视频 | [`m3u8_dl.py`](m3u8_download/m3u8_dl.py) |
1313
| **make_icns** | 一键生成 macOS 应用图标(`.icns`| [`make_icns.py`](make_icns/make_icns.py) |
14+
| **make_ico** | 一键生成 Windows 图标(`.ico`| [`make_ico.py`](make_ico/make_ico.py) |
1415
| **procmon** | 按进程名实时监控 CPU / 内存 / 线程 / 句柄 | [`procmon.py`](procmon/procmon.py) |
1516
| **resolve** | 域名解析,快速获取 IP、端口、协议 | [`resolve.py`](resolve/resolve.py) |
1617
| **syncthing** | Syncthing API 封装,监控文件夹与设备状态 | [`syncthing_monitor.py`](syncthing/syncthing_monitor.py) |

icon-maker/icon_common.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# -*- coding: utf-8 -*-
2+
from typing import Tuple, Optional
3+
from PIL import Image
4+
5+
6+
def parse_hex_color(hex_color: str) -> Tuple[int, int, int, int]:
7+
hex_color = hex_color.lstrip("#")
8+
n = len(hex_color)
9+
if n == 3:
10+
hex_color = "".join(c * 2 for c in hex_color) + "FF"
11+
elif n == 4:
12+
hex_color = "".join(c * 2 for c in hex_color)
13+
elif n == 6:
14+
hex_color += "FF"
15+
elif n != 8:
16+
raise ValueError("颜色格式错误,支持:#RGB、#RGBA、#RRGGBB、#RRGGBBAA")
17+
return tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4, 6))
18+
19+
20+
def square_image(
21+
img: Image.Image, bg_color: Optional[Tuple[int, int, int, int]] = None
22+
) -> Image.Image:
23+
if img.width == img.height:
24+
return img.copy()
25+
side = max(img.width, img.height)
26+
canvas = Image.new("RGBA", (side, side), bg_color or (0, 0, 0, 0))
27+
x, y = (side - img.width) // 2, (side - img.height) // 2
28+
canvas.paste(img, (x, y), img if img.mode == "RGBA" else None)
29+
return canvas
Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#!/usr/bin/env python3
21
# -*- coding: utf-8 -*-
32
"""
43
make-icns.py
@@ -15,46 +14,12 @@
1514
import tempfile
1615
import subprocess
1716
from PIL import Image, ImageOps
17+
from icon_common import parse_hex_color, square_image
1818

1919
# icns 所需全部分辨率
2020
ICNS_SIZES = [16, 32, 64, 128, 256, 512, 1024]
2121

2222

23-
def parse_hex_color(hex_color: str):
24-
"""
25-
支持 #RGB、#RGBA、#RRGGBB、#RRGGBBAA
26-
返回 (R, G, B, A) 0-255
27-
"""
28-
hex_color = hex_color.lstrip("#")
29-
n = len(hex_color)
30-
if n == 3: # #RGB -> #RRGGBB + 不透明
31-
hex_color = "".join(c * 2 for c in hex_color) + "FF"
32-
elif n == 4: # #RGBA -> #RRGGBBAA
33-
hex_color = "".join(c * 2 for c in hex_color)
34-
elif n == 6: # #RRGGBB -> 不透明
35-
hex_color += "FF"
36-
elif n != 8:
37-
raise ValueError("颜色格式错误,支持:#RGB、#RGBA、#RRGGBB、#RRGGBBAA")
38-
39-
return tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4, 6))
40-
41-
42-
def square_image(img: Image.Image, bg_color=None) -> Image.Image:
43-
"""将 img 做成最小外接正方形,bg_color=None 代表透明"""
44-
if img.width == img.height:
45-
return img.copy()
46-
side = max(img.width, img.height)
47-
if bg_color is None: # 透明
48-
new_im = Image.new("RGBA", (side, side), (0, 0, 0, 0))
49-
else: # 纯色
50-
new_im = Image.new("RGBA", (side, side), bg_color)
51-
# 居中粘贴
52-
x = (side - img.width) // 2
53-
y = (side - img.height) // 2
54-
new_im.paste(img, (x, y), img if img.mode == "RGBA" else None)
55-
return new_im
56-
57-
5823
def build_icns(source_img: Image.Image, output_icns: str):
5924
"""生成 .icns 文件"""
6025
tempdir = tempfile.mkdtemp()

icon-maker/make_ico.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
make_ico.py
4+
将任意图片快速生成多尺寸 ico 图标。
5+
6+
用法:
7+
./make_ico.py input.png # 透明背景
8+
./make_ico.py input.jpg "#FF00FF" # 指定背景色
9+
./make_ico.py input.tiff -o MyIcon.ico # 指定输出文件名
10+
"""
11+
import os
12+
import sys
13+
import argparse
14+
from PIL import Image, ImageOps
15+
from icon_common import parse_hex_color, square_image
16+
17+
# 常用 ico 尺寸(Windows 推荐倒序存放)
18+
# Pillow 还是会从小到大生成,无视传入的顺序
19+
ICO_SIZES = [256, 128, 64, 48, 32, 16]
20+
21+
22+
def build_ico(source_img: Image.Image, output_ico: str) -> None:
23+
"""生成 .ico 文件"""
24+
25+
icons = []
26+
for size in ICO_SIZES:
27+
icons.append(source_img.resize((size, size), Image.LANCZOS))
28+
29+
# 保存为 ico(Pillow 会自动打包多尺寸)
30+
# 写入为PNG压缩格式,windows 文件属性分辨率只能识别第一张图,即16x16
31+
icons[0].save(
32+
output_ico,
33+
format="ICO",
34+
sizes=[(s, s) for s in ICO_SIZES],
35+
append_images=icons[1:],
36+
)
37+
print(f"✅ 已生成 {output_ico}(含尺寸:{ICO_SIZES})")
38+
39+
40+
def main(argv=None):
41+
parser = argparse.ArgumentParser(description="把任意图片做成 ico 图标")
42+
parser.add_argument("input", help="输入图片路径")
43+
parser.add_argument("bgcolor", nargs="?", help="背景色(十六进制,可选,默认透明)")
44+
parser.add_argument("-o", "--output", help="输出 .ico 文件名(可选)")
45+
args = parser.parse_args()
46+
47+
if not os.path.isfile(args.input):
48+
sys.exit("输入文件不存在")
49+
50+
# 1. 读图
51+
img = Image.open(args.input).convert("RGBA")
52+
53+
# 2. 背景色
54+
if args.bgcolor:
55+
bg_color = parse_hex_color(args.bgcolor)
56+
else:
57+
bg_color = None # 透明
58+
59+
# 3. 正方形化
60+
square = square_image(img, bg_color)
61+
62+
# 4. 输出文件名
63+
if args.output:
64+
out_ico = args.output
65+
else:
66+
base, _ = os.path.splitext(args.input)
67+
out_ico = base + ".ico"
68+
69+
# 5. 生成 ico
70+
build_ico(square, out_ico)
71+
print("已生成 →", out_ico)
72+
73+
74+
if __name__ == "__main__":
75+
main()

0 commit comments

Comments
 (0)