Python pyside6编写一个广告图片生成器

 更新时间:2025年01月03日 08:50:35   作者:PieroPc  
这篇文章主要为大家详细介绍了Python如何使用pyside6编写一个广告图片生成器,可以快速制作包含产品图片和文字的广告图片,需要的可以参考下

一、效果图

二、说明书

广告图片生成器使用说明

1.软件功能

这是一个用于生成广告图片的工具,可以快速制作包含产品图片和文字的广告图片。

2.主要特点

  • 自定义广告尺寸(默认620×420像素)
  • 智能去除产品图片背景
  • 自动排版(左文右图布局)
  • 自动文字换行
  • 可自定义颜色和文字

使用步骤

1. 基本设置

广告尺寸: 可设置宽度(300-1920px)和高度(200-1080px)

广告文本: 在文本框中输入需要显示的广告文字

输出路径: 默认为"D:\PIC",可通过"选择路径"按钮更改

2. 图片处理

选择图片: 点击"选择产品图片"按钮选择要使用的图片

去背景功能:

  • 可通过复选框开启/关闭去背景功能
  • 使用滑动条调整去背景阈值(180-250)
  • 点击"预览去背景"查看效果

3. 样式设置

背景颜色: 点击"选择背景颜色"按钮自定义背景色

文字颜色: 点击"选择文字颜色"按钮自定义文字颜色

4. 生成和保存

生成预览: 所有设置完成后会自动预览效果

生成图片: 点击"生成广告图片"按钮生成最终图片

保存图片: 点击"保存图片"按钮将图片保存到指定位置

布局说明

文字区域占图片宽度的1/3,位于左侧

产品图片占图片宽度的2/3,位于右侧

文字和图片都会自动居中对齐

注意事项

去背景功能最适合处理白色或浅色背景的图片

文字会根据区域大小自动换行

图片会自动等比例缩放以适应区域

每次生成的图片会自动以时间戳命名

系统要求

操作系统:Windows

Python环境

需要安装的库:

PySide6

Pillow (PIL)

快捷操作

可以直接拖拽图片到软件窗口

使用滑动条快速调整去背景效果

实时预览所有修改效果

三、实现代码

from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                                 QHBoxLayout, QPushButton, QLabel, QLineEdit, QFileDialog,
                                 QSpinBox, QColorDialog, QTextEdit, QSlider, QDialog, QCheckBox,
                                 QScrollArea)
from PySide6.QtCore import Qt
from PySide6.QtGui import QPixmap, QImage
from PIL import Image, ImageDraw, ImageFont
import sys
import os
import time
 
class PreviewDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("去背景预览")
        self.setModal(False)  # 非模态对话框
        layout = QVBoxLayout(self)
        
        # 预览标签
        self.preview_label = QLabel()
        self.preview_label.setFixedSize(300, 300)
        self.preview_label.setStyleSheet("QLabel { background-color: #808080; }")  # 灰色背景
        self.preview_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.preview_label)
        
        # 关闭按钮
        close_btn = QPushButton("关闭预览")
        close_btn.clicked.connect(self.close)
        layout.addWidget(close_btn)
 
class HelpDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("使用说明")
        self.setGeometry(150, 150, 600, 400)
        
        layout = QVBoxLayout(self)
        
        # 创建滚动区域
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        layout.addWidget(scroll)
        
        # 创建内容容器
        content = QWidget()
        scroll.setWidget(content)
        content_layout = QVBoxLayout(content)
        
        # 读取说明文件
        try:
            with open("readme.md", "r", encoding="utf-8") as f:
                help_text = f.read()
        except:
            help_text = "找不到说明文件"
        
        # 创建文本显示
        text_display = QTextEdit()
        text_display.setReadOnly(True)
        text_display.setMarkdown(help_text)
        content_layout.addWidget(text_display)
        
        # 关闭按钮
        close_btn = QPushButton("关闭")
        close_btn.clicked.connect(self.close)
        layout.addWidget(close_btn)
 
class AdGenerator(QMainWindow):
    def __init__(self):
        super().__init__()
        # 设置默认输出路径
        self.output_dir = "D:\\PIC"
        # 确保输出目录存在
        if not os.path.exists(self.output_dir):
            try:
                os.makedirs(self.output_dir)
            except Exception as e:
                print(f"创建输出目录失败: {str(e)}")
                self.output_dir = os.getcwd()  # 如果创建失败,使用当前目录
        
        self.initUI()
        self.product_image_path = None
        self.bg_color = (30, 30, 30)
        self.text_color = (255, 255, 255)
        self.threshold = 220  # 默认阈值
        self.preview_dialog = None
        self.current_image = None
        self.remove_bg_enabled = True  # 默认启用去背景
        self.ad_width = 620   # 默认宽度
        self.ad_height = 420  # 默认高度
        self.help_dialog = None
        
    def initUI(self):
        self.setWindowTitle('广告图片生成器')
        self.setGeometry(100, 100, 800, 600)
        
        # 创建中心部件和布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        
        # 创建预览标签
        self.preview_label = QLabel()
        self.preview_label.setFixedSize(620, 420)
        self.preview_label.setStyleSheet("QLabel { background-color: #1e1e1e; }")
        self.preview_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.preview_label)
        
        # 创建控制面板
        controls_layout = QHBoxLayout()
        
        # 左侧控制面板
        left_panel = QVBoxLayout()
        
        # 添加尺寸设置
        size_layout = QHBoxLayout()
        size_label = QLabel("广告尺寸:")
        self.width_input = QSpinBox()
        self.width_input.setRange(300, 1920)  # 设置合理的范围
        self.width_input.setValue(620)
        self.width_input.setSuffix(" px")
        self.width_input.valueChanged.connect(self.size_changed)
        
        size_x_label = QLabel("×")
        
        self.height_input = QSpinBox()
        self.height_input.setRange(200, 1080)
        self.height_input.setValue(420)
        self.height_input.setSuffix(" px")
        self.height_input.valueChanged.connect(self.size_changed)
        
        size_layout.addWidget(size_label)
        size_layout.addWidget(self.width_input)
        size_layout.addWidget(size_x_label)
        size_layout.addWidget(self.height_input)
        left_panel.addLayout(size_layout)
        
        # 文本输入
        text_layout = QVBoxLayout()
        text_label = QLabel("广告文本:")
        self.text_input = QTextEdit()
        self.text_input.setPlaceholderText("输入广告文本")
        self.text_input.setMaximumHeight(100)
        text_layout.addWidget(text_label)
        text_layout.addWidget(self.text_input)
        left_panel.addLayout(text_layout)
        
        # 图片选择按钮
        self.select_image_btn = QPushButton("选择产品图片")
        self.select_image_btn.clicked.connect(self.select_image)
        left_panel.addWidget(self.select_image_btn)
        
        # 背景颜色选择
        self.bg_color_btn = QPushButton("选择背景颜色")
        self.bg_color_btn.clicked.connect(self.select_bg_color)
        left_panel.addWidget(self.bg_color_btn)
        
        # 文字颜色选择
        self.text_color_btn = QPushButton("选择文字颜色")
        self.text_color_btn.clicked.connect(self.select_text_color)
        left_panel.addWidget(self.text_color_btn)
        
        # 阈值选择
        threshold_layout = QHBoxLayout()
        
        # 添加去背景开关
        self.remove_bg_checkbox = QCheckBox("启用去背景")
        self.remove_bg_checkbox.setChecked(True)
        self.remove_bg_checkbox.stateChanged.connect(self.toggle_remove_bg)
        left_panel.addWidget(self.remove_bg_checkbox)
        
        threshold_label = QLabel("去背景阈值:")
        self.threshold_value_label = QLabel("220")
        self.threshold_slider = QSlider(Qt.Horizontal)
        self.threshold_slider.setMinimum(180)
        self.threshold_slider.setMaximum(250)
        self.threshold_slider.setValue(220)
        self.threshold_slider.valueChanged.connect(self.threshold_changed)
        
        # 添加预览按钮
        self.preview_btn = QPushButton("预览去背景")
        self.preview_btn.clicked.connect(self.show_preview)
        
        threshold_layout.addWidget(threshold_label)
        threshold_layout.addWidget(self.threshold_slider)
        threshold_layout.addWidget(self.threshold_value_label)
        threshold_layout.addWidget(self.preview_btn)
        left_panel.addLayout(threshold_layout)
        
        controls_layout.addLayout(left_panel)
        
        # 右侧控制面板
        right_panel = QVBoxLayout()
        
        # 添加输出路径设置
        output_layout = QHBoxLayout()
        output_label = QLabel("输出路径:")
        self.output_path_label = QLabel(self.output_dir)
        self.output_path_label.setStyleSheet("padding: 5px; background-color: #f0f0f0; border: 1px solid #ccc;")
        self.output_path_btn = QPushButton("选择路径")
        self.output_path_btn.clicked.connect(self.select_output_path)
        
        output_layout.addWidget(output_label)
        output_layout.addWidget(self.output_path_label, stretch=1)
        output_layout.addWidget(self.output_path_btn)
        right_panel.addLayout(output_layout)
        
        # 生成按钮
        self.generate_btn = QPushButton("生成广告图片")
        self.generate_btn.clicked.connect(self.generate_ad)
        right_panel.addWidget(self.generate_btn)
        
        # 保存按钮
        self.save_btn = QPushButton("保存图片")
        self.save_btn.clicked.connect(self.save_image)
        right_panel.addWidget(self.save_btn)
        
        # 添加帮助按钮
        self.help_btn = QPushButton("使用说明")
        self.help_btn.clicked.connect(self.show_help)
        right_panel.addWidget(self.help_btn)
        
        controls_layout.addLayout(right_panel)
        layout.addLayout(controls_layout)
    
    def select_image(self):
        file_name, _ = QFileDialog.getOpenFileName(
            self,
            "选择产品图片",
            "",
            "图片文件 (*.png *.jpg *.jpeg *.bmp)"
        )
        if file_name:
            self.product_image_path = file_name
            self.preview_image()
    
    def select_bg_color(self):
        color = QColorDialog.getColor()
        if color.isValid():
            self.bg_color = (color.red(), color.green(), color.blue())
            self.preview_image()
    
    def select_text_color(self):
        color = QColorDialog.getColor()
        if color.isValid():
            self.text_color = (color.red(), color.green(), color.blue())
            self.preview_image()
    
    def threshold_changed(self):
        self.threshold = self.threshold_slider.value()
        self.threshold_value_label.setText(str(self.threshold))
        if self.preview_dialog and self.preview_dialog.isVisible():
            self.show_preview()
        if self.product_image_path:
            self.preview_image()
    
    def toggle_remove_bg(self, state):
        self.remove_bg_enabled = bool(state)
        # 更新阈值控件的启用状态
        self.threshold_slider.setEnabled(self.remove_bg_enabled)
        self.threshold_value_label.setEnabled(self.remove_bg_enabled)
        self.preview_btn.setEnabled(self.remove_bg_enabled)
        # 如果有图片,立即更新预览
        if self.product_image_path:
            self.preview_image()
    
    def remove_background(self, image):
        if not self.remove_bg_enabled:
            return image.convert('RGBA')
            
        # 转换图片为RGBA模式
        image = image.convert('RGBA')
        data = image.getdata()
        new_data = []
        
        for item in data:
            # 如果像素接近白色,则设置为透明
            if item[0] >= self.threshold and item[1] >= self.threshold and item[2] >= self.threshold:
                new_data.append((255, 255, 255, 0))
            else:
                new_data.append(item)
        
        image.putdata(new_data)
        return image
    
    def show_preview(self):
        if not self.product_image_path:
            return
            
        if not self.preview_dialog:
            self.preview_dialog = PreviewDialog(self)
        
        try:
            # 加载原图并去背景
            img = Image.open(self.product_image_path)
            img = self.remove_background(img)
            
            # 调整图片大小以适应预览窗口
            preview_size = 300
            img.thumbnail((preview_size, preview_size), Image.Resampling.LANCZOS)
            
            # 转换为QImage
            img_data = img.convert("RGBA").tobytes("raw", "RGBA")
            qimg = QImage(img_data, img.size[0], img.size[1], QImage.Format_RGBA8888)
            
            # 显示在预览窗口中
            pixmap = QPixmap.fromImage(qimg)
            self.preview_dialog.preview_label.setPixmap(pixmap)
            
            # 显示预览窗口
            self.preview_dialog.show()
            
        except Exception as e:
            print(f"预览出错: {str(e)}")
    
    def preview_image(self):
        self.current_image = self.generate_ad(preview=True)
        if self.current_image:
            pixmap = QPixmap.fromImage(self.current_image)
            self.preview_label.setPixmap(pixmap.scaled(
                self.preview_label.size(),
                Qt.KeepAspectRatio,
                Qt.SmoothTransformation
            ))
    
    def generate_ad(self, preview=False):
        width, height = self.ad_width, self.ad_height  # 使用设置的尺寸
        image = Image.new('RGB', (width, height), self.bg_color)
        draw = ImageDraw.Draw(image)
        
        if self.product_image_path and os.path.exists(self.product_image_path):
            try:
                product_img = Image.open(self.product_image_path)
                product_img = self.remove_background(product_img)
                
                # 严格计算2/3宽度
                right_width = int(width * (2/3))  # 确保宽度正好是2/3
                product_height = int((right_width * product_img.height) / product_img.width)
                
                # 如果高度超出,按高度缩放,但保持宽度不超过2/3
                if product_height > height:
                    product_height = height
                    calc_width = int((product_height * product_img.width) / product_img.height)
                    right_width = min(calc_width, right_width)  # 取较小值确保不超过2/3
                
                product_img = product_img.resize((right_width, product_height))
                
                # 计算粘贴位置(右侧居中)
                paste_x = width - right_width
                paste_y = (height - product_height) // 2
                
                # 粘贴产品图片
                image.paste(product_img, (paste_x, paste_y), product_img)
            except Exception as e:
                print(f"处理图片时出错: {str(e)}")
        
        try:
            # 根据广告尺寸调整字体大小
            font_size = min(32, int(height / 13))  # 动态计算合适的字体大小
            font = ImageFont.truetype("msyh.ttc", font_size)
        except:
            font = ImageFont.load_default()
        
        # 文本区域(使用左侧1/3的空间)
        text_area_width = int(width * (1/3))  # 确保文本区域是1/3
        text = self.text_input.toPlainText()
        
        # 改进的文本换行处理
        lines = []
        current_line = ""
        
        # 按字符分割文本(考虑中文字符)
        for char in text:
            test_line = current_line + char
            text_bbox = draw.textbbox((0, 0), test_line, font=font)
            if text_bbox[2] - text_bbox[0] > text_area_width - 40:  # 留出左右边距
                if current_line:
                    lines.append(current_line)
                    current_line = char
                else:
                    lines.append(char)
                    current_line = ""
            else:
                current_line += char
        
        if current_line:
            lines.append(current_line)
        
        # 计算文本总高度
        line_height = font.size + 8
        total_text_height = len(lines) * line_height
        
        # 绘制文本(左侧居中)
        start_y = (height - total_text_height) // 2
        for i, line in enumerate(lines):
            text_bbox = draw.textbbox((0, 0), line, font=font)
            text_width = text_bbox[2] - text_bbox[0]
            x = 30  # 增加左边距
            y = start_y + i * line_height
            draw.text((x, y), line, font=font, fill=self.text_color)
        
        if preview:
            # 转换为QImage用于预览
            data = image.tobytes("raw", "RGB")
            qimage = QImage(data, image.size[0], image.size[1], QImage.Format_RGB888)
            return qimage
        else:
            # 生成文件名(使用时间戳)
            timestamp = time.strftime("%Y%m%d_%H%M%S")
            output_path = os.path.join(self.output_dir, f"ad_{timestamp}.png")
            
            # 创建图片
            width, height = self.ad_width, self.ad_height
            image = Image.new('RGB', (width, height), self.bg_color)
            draw = ImageDraw.Draw(image)
            
            if self.product_image_path and os.path.exists(self.product_image_path):
                try:
                    product_img = Image.open(self.product_image_path)
                    product_img = self.remove_background(product_img)
                    
                    # 严格计算2/3宽度
                    right_width = int(width * (2/3))  # 确保宽度正好是2/3
                    product_height = int((right_width * product_img.height) / product_img.width)
                    
                    # 如果高度超出,按高度缩放,但保持宽度不超过2/3
                    if product_height > height:
                        product_height = height
                        calc_width = int((product_height * product_img.width) / product_img.height)
                        right_width = min(calc_width, right_width)  # 取较小值确保不超过2/3
                    
                    product_img = product_img.resize((right_width, product_height))
                    
                    # 计算粘贴位置(右侧居中)
                    paste_x = width - right_width
                    paste_y = (height - product_height) // 2
                    
                    # 粘贴产品图片
                    image.paste(product_img, (paste_x, paste_y), product_img)
                except Exception as e:
                    print(f"处理图片时出错: {str(e)}")
            
            try:
                # 根据广告尺寸调整字体大小
                font_size = min(32, int(height / 13))  # 动态计算合适的字体大小
                font = ImageFont.truetype("msyh.ttc", font_size)
            except:
                font = ImageFont.load_default()
            
            # 文本区域(使用左侧1/3的空间)
            text_area_width = int(width * (1/3))  # 确保文本区域是1/3
            text = self.text_input.toPlainText()
            
            # 改进的文本换行处理
            lines = []
            current_line = ""
            
            # 按字符分割文本(考虑中文字符)
            for char in text:
                test_line = current_line + char
                text_bbox = draw.textbbox((0, 0), test_line, font=font)
                if text_bbox[2] - text_bbox[0] > text_area_width - 40:  # 留出左右边距
                    if current_line:
                        lines.append(current_line)
                        current_line = char
                    else:
                        lines.append(char)
                        current_line = ""
                else:
                    current_line += char
            
            if current_line:
                lines.append(current_line)
            
            # 计算文本总高度
            line_height = font.size + 8
            total_text_height = len(lines) * line_height
            
            # 绘制文本(左侧居中)
            start_y = (height - total_text_height) // 2
            for i, line in enumerate(lines):
                text_bbox = draw.textbbox((0, 0), line, font=font)
                text_width = text_bbox[2] - text_bbox[0]
                x = 30  # 增加左边距
                y = start_y + i * line_height
                draw.text((x, y), line, font=font, fill=self.text_color)
            
            # 保存图片
            try:
                image.save(output_path)
                print(f"图片已保存到: {output_path}")
                return output_path
            except Exception as e:
                print(f"保存图片失败: {str(e)}")
                return None
    
    def save_image(self):
        timestamp = time.strftime("%Y%m%d_%H%M%S")
        default_name = f"ad_{timestamp}.png"
        file_name, _ = QFileDialog.getSaveFileName(
            self,
            "保存广告图片",
            os.path.join(self.output_dir, default_name),
            "PNG图片 (*.png)"
        )
        if file_name:
            if self.generate_ad():
                # 复制生成的图片到选择的位置
                latest_file = os.path.join(self.output_dir, default_name)
                if os.path.exists(latest_file):
                    Image.open(latest_file).save(file_name)
                    print(f"图片已保存到: {file_name}")
                else:
                    print("找不到生成的图片文件")
    
    def size_changed(self):
        # 更新尺寸
        self.ad_width = self.width_input.value()
        self.ad_height = self.height_input.value()
        
        # 更新预览标签大小
        self.preview_label.setFixedSize(self.ad_width, self.ad_height)
        
        # 如果有图片,重新生成预览
        if self.product_image_path:
            self.preview_image()
    
    def select_output_path(self):
        dir_path = QFileDialog.getExistingDirectory(
            self,
            "选择输出目录",
            self.output_dir,
            QFileDialog.ShowDirsOnly
        )
        if dir_path:
            self.output_dir = dir_path
            self.output_path_label.setText(self.output_dir)
    
    def show_help(self):
        if not self.help_dialog:
            self.help_dialog = HelpDialog(self)
        self.help_dialog.show()
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = AdGenerator()
    window.show()
    sys.exit(app.exec())

到此这篇关于Python pyside6编写一个广告图片生成器的文章就介绍到这了,更多相关Python pyside6广告图片生成内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python代码实现PDF与Word之间互转

    Python代码实现PDF与Word之间互转

    这篇文章主要介绍了如何使用Python的pdf2docx和win32com库进行PDF与DOCX之间的转换,包括单个文件和批量文件的处理,文中的示例代码讲解详细,有需要的小伙伴可以了解下
    2026-03-03
  • Python实现将MySQL数据库表中的数据导出生成csv格式文件的方法

    Python实现将MySQL数据库表中的数据导出生成csv格式文件的方法

    这篇文章主要介绍了Python实现将MySQL数据库表中的数据导出生成csv格式文件的方法,涉及Python针对mysql数据库的连接、查询、csv格式数据文件的生成等相关操作技巧,需要的朋友可以参考下
    2018-01-01
  • Selenium chrome配置代理Python版的方法

    Selenium chrome配置代理Python版的方法

    这篇文章主要介绍了Selenium chrome配置代理Python版的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • Python参数配置使用XML文件的教程

    Python参数配置使用XML文件的教程

    当配置项存储在外部文件(如 XML、JSON)时,修改配置无需重新编译和发布代码,下面就来介绍一下Python参数配置使用XML文件的教程,感兴趣的可以了解一下
    2025-04-04
  • python列表添加元素append(),extend(),insert(),+list的区别及说明

    python列表添加元素append(),extend(),insert(),+list的区别及说明

    这篇文章主要介绍了python列表添加元素append(),extend(), insert(),+list的区别及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • python数据处理和数据清洗的示例详解

    python数据处理和数据清洗的示例详解

    数据清洗是指发现并纠正数据文件中可识别的错误的最后一道程序,包括检查数据一致性,处理无效值和缺失值等,数据清洗与处理的目的是提高数据的质量,提高实验结果的可靠度,本文给大家介绍了python数据处理和数据清洗的示例,需要的朋友可以参考下
    2024-08-08
  • TensorFlow 合并/连接数组的方法

    TensorFlow 合并/连接数组的方法

    今天小编就为大家分享一篇TensorFlow 合并/连接数组的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • 树莓派使用python-librtmp实现rtmp推流h264的方法

    树莓派使用python-librtmp实现rtmp推流h264的方法

    今天小编就为大家分享一篇树莓派使用python-librtmp实现rtmp推流h264的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • python将dict中的unicode打印成中文实例

    python将dict中的unicode打印成中文实例

    这篇文章主要介绍了python将dict中的unicode打印成中文实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-05-05
  • 详解python 模拟豆瓣登录(豆瓣6.0)

    详解python 模拟豆瓣登录(豆瓣6.0)

    这篇文章主要介绍了python模拟豆瓣登录,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04

最新评论