设计民间艺术表情包生成器,把剪纸,皮影图案等做成表情包,添加文字,分享到社交平台。

1 阅读8分钟

我将为您创建一个完整的民间艺术表情包生成器程序。这个项目结合了传统文化与现代社交媒体需求,让用户能够轻松制作具有中国特色的表情包。

项目结构

folk_emoji_generator/ ├── main.py # 主程序入口 ├── config.py # 配置文件 ├── asset_manager.py # 素材管理模块 ├── emoji_generator.py # 表情包生成模块 ├── text_overlay.py # 文字叠加模块 ├── social_sharer.py # 社交分享模块 ├── ui_components.py # UI组件模块 ├── folk_art_data.json # 民间艺术数据 ├── README.md # 使用说明 └── assets/ # 资源文件夹 ├── paper_cutting/ # 剪纸素材 ├── shadow_puppet/ # 皮影素材 ├── chinese_ink/ # 水墨素材 └── fonts/ # 字体文件

  1. 配置文件 (config.py)

""" 配置文件 - 定义应用程序的基本配置和常量 """

import os

应用基本配置

APP_NAME = "民间艺术表情包生成器" VERSION = "1.0.0" WINDOW_SIZE = (1000, 700)

文件路径配置

BASE_DIR = os.path.dirname(os.path.abspath(file)) ASSETS_DIR = os.path.join(BASE_DIR, "assets") FONT_DIR = os.path.join(ASSETS_DIR, "fonts") OUTPUT_DIR = os.path.join(BASE_DIR, "output") DATA_FILE = os.path.join(BASE_DIR, "folk_art_data.json")

素材分类目录

ART_CATEGORIES = { 'paper_cutting': '剪纸艺术', 'shadow_puppet': '皮影艺术', 'chinese_ink': '水墨艺术', 'embroidery': '刺绣艺术', 'clay_sculpture': '泥塑艺术' }

支持的图片格式

SUPPORTED_IMAGE_FORMATS = ['.png', '.jpg', '.jpeg', '.bmp', '.tiff'] SUPPORTED_OUTPUT_FORMATS = ['.png', '.jpg', '.gif']

字体配置

DEFAULT_FONT_SIZE = 24 MAX_FONT_SIZE = 72 MIN_FONT_SIZE = 12

表情包尺寸配置

EMOJI_SIZES = { 'small': (200, 200), 'medium': (400, 400), 'large': (600, 600), 'social': (500, 400) # 适合社交平台的尺寸 }

UI颜色主题

COLORS = { 'primary': '#FF6B6B', 'secondary': '#4ECDC4', 'accent': '#45B7D1', 'background': '#F8F9FA', 'text': '#2C3E50', 'success': '#27AE60', 'warning': '#F39C12' }

常用表情包文字模板

TEXT_TEMPLATES = [ "哈哈哈 😂", "太可爱了 🥰", "我太难了 😭", "开心到飞起 🎉", "无语 😑", "点赞 👍", "比心 ❤️", "加油 💪", "晚安 🌙", "早安 ☀️" ]

社交平台配置

SOCIAL_PLATFORMS = { 'wechat': '微信', 'qq': 'QQ', 'weibo': '微博', 'douyin': '抖音', 'bilibili': '哔哩哔哩' }

  1. 素材管理模块 (asset_manager.py)

""" 素材管理模块 - 负责加载和管理民间艺术素材 """

import os import json from PIL import Image, ImageTk import random from config import *

class AssetManager: def init(self): self.art_assets = {} self.fonts = [] self.load_assets() self.load_fonts()

def load_assets(self):
    """加载民间艺术素材"""
    try:
        # 首先尝试从JSON文件加载素材信息
        if os.path.exists(DATA_FILE):
            with open(DATA_FILE, 'r', encoding='utf-8') as f:
                art_data = json.load(f)
                self.process_art_data(art_data)
        else:
            print("素材数据文件不存在,扫描目录加载素材...")
            self.scan_asset_directories()
            
    except Exception as e:
        print(f"加载素材时出错: {e}")
        self.scan_asset_directories()

def scan_asset_directories(self):
    """扫描素材目录加载图片文件"""
    for category, name in ART_CATEGORIES.items():
        category_dir = os.path.join(ASSETS_DIR, category)
        if os.path.exists(category_dir):
            self.art_assets[category] = []
            for filename in os.listdir(category_dir):
                if any(filename.lower().endswith(fmt) for fmt in SUPPORTED_IMAGE_FORMATS):
                    file_path = os.path.join(category_dir, filename)
                    self.art_assets[category].append({
                        'name': filename,
                        'path': file_path,
                        'category': category,
                        'display_name': name
                    })
    
    print(f"加载了 {sum(len(assets) for assets in self.art_assets.values())} 个素材文件")

def process_art_data(self, art_data):
    """处理从JSON加载的艺术数据"""
    for category, items in art_data.items():
        if category not in self.art_assets:
            self.art_assets[category] = []
        
        for item in items:
            # 验证文件是否存在
            if os.path.exists(item['path']):
                self.art_assets[category].append(item)
            else:
                print(f"素材文件不存在: {item['path']}")

def load_fonts(self):
    """加载可用的字体文件"""
    if os.path.exists(FONT_DIR):
        for filename in os.listdir(FONT_DIR):
            if filename.lower().endswith(('.ttf', '.otf')):
                self.fonts.append(os.path.join(FONT_DIR, filename))
    
    # 如果没有找到字体文件,使用系统默认字体
    if not self.fonts:
        print("未找到字体文件,将使用系统默认字体")
        self.fonts = ['default']

def get_random_asset(self, category=None):
    """获取随机素材"""
    if category and category in self.art_assets:
        assets = self.art_assets[category]
    else:
        # 从所有类别中随机选择一个
        all_assets = []
        for assets in self.art_assets.values():
            all_assets.extend(assets)
        assets = all_assets if all_assets else []
    
    return random.choice(assets) if assets else None

def get_assets_by_category(self, category):
    """根据类别获取素材列表"""
    return self.art_assets.get(category, [])

def get_all_categories(self):
    """获取所有素材类别"""
    return list(ART_CATEGORIES.keys())

def get_category_display_name(self, category):
    """获取类别显示名称"""
    return ART_CATEGORIES.get(category, category)

def load_image_for_display(self, image_path, size=None):
    """加载图片用于显示(调整大小)"""
    try:
        if not os.path.exists(image_path):
            return None
        
        image = Image.open(image_path)
        if size:
            image.thumbnail(size, Image.Resampling.LANCZOS)
        
        return image
    except Exception as e:
        print(f"加载图片失败: {e}")
        return None

def create_sample_data(self):
    """创建示例素材数据文件"""
    sample_data = {
        "paper_cutting": [
            {
                "name": "剪纸_福字.png",
                "path": "assets/paper_cutting/剪纸_福字.png",
                "category": "paper_cutting",
                "display_name": "剪纸艺术",
                "description": "传统红色剪纸福字,寓意吉祥如意"
            },
            {
                "name": "剪纸_生肖鼠.png",
                "path": "assets/paper_cutting/剪纸_生肖鼠.png", 
                "category": "paper_cutting",
                "display_name": "剪纸艺术",
                "description": "十二生肖剪纸老鼠图案,造型生动"
            }
        ],
        "shadow_puppet": [
            {
                "name": "皮影_武将.png",
                "path": "assets/shadow_puppet/皮影_武将.png",
                "category": "shadow_puppet", 
                "display_name": "皮影艺术",
                "description": "传统皮影戏武将形象,色彩鲜艳"
            }
        ]
    }
    
    # 保存到JSON文件
    with open(DATA_FILE, 'w', encoding='utf-8') as f:
        json.dump(sample_data, f, ensure_ascii=False, indent=2)
    
    return sample_data

3. 表情包生成模块 (emoji_generator.py)

""" 表情包生成模块 - 负责创建和处理表情包图片 """

from PIL import Image, ImageDraw, ImageFont, ImageFilter import os from config import * import textwrap

class EmojiGenerator: def init(self, asset_manager): self.asset_manager = asset_manager self.current_image = None self.current_size = EMOJI_SIZES['medium']

def create_base_emoji(self, asset_info, size=None):
    """创建基础表情包"""
    try:
        if size is None:
            size = self.current_size
        
        # 加载原始图片
        original_image = Image.open(asset_info['path'])
        
        # 创建新图片(白色背景)
        emoji_image = Image.new('RGB', size, 'white')
        
        # 计算缩放比例以适应目标尺寸
        original_width, original_height = original_image.size
        target_width, target_height = size
        
        # 保持宽高比的缩放
        scale_ratio = min(target_width/original_width, target_height/original_height) * 0.8
        new_width = int(original_width * scale_ratio)
        new_height = int(original_height * scale_ratio)
        
        # 缩放图片
        resized_image = original_image.resize((new_width, new_height), Image.Resampling.LANCZOS)
        
        # 居中粘贴
        x = (target_width - new_width) // 2
        y = (target_height - new_height) // 2
        emoji_image.paste(resized_image, (x, y))
        
        self.current_image = emoji_image
        return emoji_image
        
    except Exception as e:
        print(f"创建表情包失败: {e}")
        return None

def add_decorative_border(self, border_style='simple'):
    """添加装饰边框"""
    if not self.current_image:
        return None
    
    img = self.current_image.copy()
    width, height = img.size
    
    if border_style == 'simple':
        # 简单黑色边框
        draw = ImageDraw.Draw(img)
        draw.rectangle([0, 0, width-1, height-1], outline='black', width=2)
    
    elif border_style == 'fancy':
        # 花式边框
        draw = ImageDraw.Draw(img)
        # 外边框
        draw.rectangle([0, 0, width-1, height-1], outline='#FF6B6B', width=3)
        # 内边框
        draw.rectangle([5, 5, width-6, height-6], outline='#4ECDC4', width=1)
    
    elif border_style == 'traditional':
        # 传统中国风边框
        # 这里可以添加中国传统纹样
        pass
    
    self.current_image = img
    return img

def apply_filters(self, filter_type='none'):
    """应用图片滤镜效果"""
    if not self.current_image:
        return None
    
    img = self.current_image.copy()
    
    if filter_type == 'vintage':
        # 复古效果
        img = Image.blend(img.convert('L'), img, 0.3)
    
    elif filter_type == 'bright':
        # 增亮效果
        from PIL import ImageEnhance
        enhancer = ImageEnhance.Brightness(img)
        img = enhancer.enhance(1.2)
    
    elif filter_type == 'contrast':
        # 增强对比度
        from PIL import ImageEnhance
        enhancer = ImageEnhance.Contrast(img)
        img = enhancer.enhance(1.3)
    
    elif filter_type == 'blur':
        # 模糊效果
        img = img.filter(ImageFilter.GaussianBlur(radius=1))
    
    self.current_image = img
    return img

def resize_for_social(self, platform='wechat'):
    """调整尺寸适应社交平台"""
    if not self.current_image:
        return None
    
    # 根据不同平台调整尺寸
    if platform in SOCIAL_PLATFORMS:
        if platform == 'weibo':
            size = (900, 500)  # 微博配图比例
        elif platform == 'douyin':
            size = (540, 960)  # 抖音竖屏比例
        else:
            size = EMOJI_SIZES['social']
    else:
        size = EMOJI_SIZES['social']
    
    resized_img = self.current_image.resize(size, Image.Resampling.LANCZOS)
    self.current_image = resized_img
    return resized_img

def save_emoji(self, filename=None, format='PNG'):
    """保存表情包"""
    if not self.current_image:
        return None
    
    # 确保输出目录存在
    os.makedirs(OUTPUT_DIR, exist_ok=True)
    
    if filename is None:
        import time
        timestamp = int(time.time())
        filename = f"emoji_{timestamp}.{format.lower()}"
    
    filepath = os.path.join(OUTPUT_DIR, filename)
    
    # 根据格式保存
    if format.upper() == 'PNG':
        self.current_image.save(filepath, 'PNG', optimize=True)
    elif format.upper() == 'JPEG':
        # JPEG不支持透明,转换为RGB
        if self.current_image.mode in ('RGBA', 'LA'):
            background = Image.new('RGB', self.current_image.size, (255, 255, 255))
            background.paste(self.current_image, mask=self.current_image.split()[-1] if self.current_image.mode == 'RGBA' else None)
            background.save(filepath, 'JPEG', quality=95)
        else:
            self.current_image.save(filepath, 'JPEG', quality=95)
    
    print(f"表情包已保存到: {filepath}")
    return filepath

def get_current_image(self):
    """获取当前处理的图片"""
    return self.current_image

def set_current_image(self, image):
    """设置当前处理的图片"""
    self.current_image = image

4. 文字叠加模块 (text_overlay.py)

""" 文字叠加模块 - 负责在图片上添加文字 """

from PIL import Image, ImageDraw, ImageFont import textwrap from config import *

class TextOverlay: def init(self): self.current_font = None self.font_size = DEFAULT_FONT_SIZE self.text_color = 'black' self.stroke_color = 'white' self.stroke_width = 2

def set_font(self, font_path=None, font_size=None):
    """设置字体"""
    if font_size:
        self.font_size = max(MIN_FONT_SIZE, min(MAX_FONT_SIZE, font_size))
    
    try:
        if font_path and font_path != 'default':
            self.current_font = ImageFont.truetype(font_path, self.font_size)
        else:
            # 使用默认字体
            self.current_font = ImageFont.load_default()
    except Exception as e:
        print(f"字体加载失败,使用默认字体: {e}")
        self.current_font = ImageFont.load_default()

def add_text_to_image(self, image, text, position='bottom', align='center'):
    """在图片上添加文字"""
    if not image:
        return image
    
    img = image.copy()
    draw = ImageDraw.Draw(img)
    
    # 自动换行处理
    wrapped_text = self.wrap_text(text, img.width - 40)  # 留边距
    
    # 计算文字位置
    text_bbox = self.calculate_text_bbox(wrapped_text, draw)
    text_x, text_y = self.calculate_position(position, img.size, text_bbox)
    
    # 绘制文字描边(提高可读性)
    if self.stroke_width > 0:
        self.draw_text_with_stroke(draw, wrapped_text, text_x, text_y, 
                                 self.stroke_color, self.stroke_width)
    
    # 绘制主要文字
    self.draw_text(draw, wrapped_text, text_x, text_y, self.text_color)
    
    return img

def wrap_text(self, text, max_width):
    """文字自动换行"""
    if not self.current_font:
        self.set_font()
    
    # 估算字符宽度
    char_width = self.font_size * 0.6  # 粗略估计
    chars_per_line = int(max_width / char_width)
    
    # 使用textwrap进行换行
    lines = textwrap.wrap(text, width=chars_per_line)
    return '\n'.join(lines)

def calculate_text_bbox(self, text, draw):
    """计算文字边界框"""
    if hasattr(draw, 'textbbox'):
        # PIL 8.0+ 版本
        bbox = draw.textbbox((0, 0), text, font=self.current_font)
        return (bbox[2] - bbox[0], bbox[3] - bbox[1])
    else:
        # 旧版本PIL
        return (len(text) * self.font_size * 0.6, self.font_size)

def calculate_position(self, position, image_size, text_size):
    """计算文字位置"""
    img_width, img_height = image_size
    text_width, text_height = text_size
    
    margin = 20
    
    if position == 'top':
        x = (img_width - text_width) // 2
        y = margin
    elif position == 'bottom':
        x = (img_width - text_width) // 2
        y = img_height - text_height - margin
    elif position == 'center':
        x = (img_width - text_width) // 2
        y = (img_height - text_height) // 2
    elif position == 'left':
        x = margin
        y = (img_height - text_height) // 2
    elif position == 'right':
        x = img_width - text_width - margin
        y = (img_height - text_height) // 2
    else:
        x = (img_width - text_width) // 2
        y = img_height - text_height - margin
    
    return x, y

def draw_text_with_stroke(self, draw, text, x, y, stroke_color, stroke_width):
    """绘制带描边的文字"""
    # 绘制描边
    for dx in range(-stroke_width, stroke_width + 1):
        for dy in range(-stroke_width, stroke_width + 1):
            if dx != 0 or dy != 0:
                draw.text((x + dx, y + dy), text, font=self.current_font, fill=stroke_color)

def draw_text(self, draw, text, x, y, color):
    """绘制文字"""
    draw.text((x, y), text, font=self.current_font, fill=color)

def set_text_style(self, color=None, stroke_color=None, stroke_width=None):
    """设置文字样式"""
    if color:
        self.text_color = color
    if stroke_color:
        self.stroke_color = stroke_color
    if stroke_width is not None:
        self.stroke_width = stroke_width

def get_available_templates(self):
    """获取预设的文字模板"""
    return TEXT_TEMPLATES

def suggest_text_for_art(self, art_info):
    """根据艺术作品建议合适的文字"""
    art_name = art_info.get('name', '')
    description = art_info.get('description', '')
    
    suggestions = []
    
    # 根据关键词匹配建议
    keywords_map = {
        '福': ['福气满满', '好运连连', '五福临门'],
        '生肖': ['萌萌哒', '太可爱了', '新年快乐'],
        '武将': ['霸气侧漏', '威武霸气', '帅炸了'],
        '花': ['花开富贵', '美美哒', '春暖花开']
    }
    
    for keyword, texts in keywords_map.items():
        if keyword in art_name or keyword in description:
            suggestions.extend(texts)
    
    return suggestions[:5] if suggestions else TEXT_TEMPLATES[:3]

5. 社交分享模块 (social_sharer.py)

""" 社交分享模块 - 负责模拟分享到社交平台的功能 """

import os import json from datetime import datetime from config import *

class SocialSharer: def init(self): self.share_history = [] self.load_share_history()

def share_to_platform(self, image_path, platform, caption=""):
    """分享到指定社交平台"""
    if not os.path.exists(image_path):
        return {'success': False, 'message': '图片文件不存在'}
    
    # 模拟分享过程
    share_id = self.generate_share_id()
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    share_record = {
        'id': share_id,
        'platform': platform,
        'platform_name': SOCIAL_PLATFORMS.get(platform, platform),
        'image_path': image_path,
        'caption': caption,
        'timestamp': timestamp,
        'status': 'shared'
    }
    
    # 添加到分享历史
    self.share_history.append(share_record)
    self.save_share_history()
    
    # 模拟不同平台的分享效果
    platform_messages = {
        'wechat': f"✅ 已分享到微信!表情包很可爱呢~",
        'qq': f"✅ 已发送到QQ空间,好友们都说不错!",
        'weibo': f"✅ 微博发布成功!转发量很高哦~",
        'douyin': f"✅ 抖音上传完成!期待更多点赞~",
        'bilibili': f"✅ 动态发布成功!UP主推荐了你的作品!"
    }
    
    message = platform_messages.get(platform, f"✅ 已分享到{platform}!")
    
    print(f"分享成功: {message}")
    return {
        'success': True, 
        'message': message,
        'share_id': share_id
    }

def generate_share_id(self):
    """生成分享ID"""
    import time
    import random
    return f"share_{int(time.time())}_{random.randint(1000, 9999)}"

def get_share_history(self):
    """获取分享历史"""
    return self.share_history

def get_platforms(self):
    """获取支持的社交平台"""
    return SOCIAL_PLATFORMS

def delete_share_record(self, share_id):
    """删除分享记录"""
    self.share_history = [record for record in self.share_history if record['id'] != share_id]
    self.save_share_history()
    return True

def save_share_history(self):
    """保存分享历史到文件"""
    try:
        history_file = os.path.join(OUTPUT_DIR, 'share_history.json')
        with open(history_file, 'w', encoding='utf-8') as f:
            json.

关注我,有更多实用程序等着你!