吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 993|回复: 18
上一主题 下一主题
收起左侧

[Windows] 【Python】图片转换器_支持jfif,webp,avif,png批量转换JPG

  [复制链接]
跳转到指定楼层
楼主
Willian 发表于 2026-4-28 20:59 回帖奖励
[Python] 纯文本查看 复制代码
import os
import tkinter as tk
from tkinter import ttk, filedialog
from tkinterdnd2 import TkinterDnD, DND_FILES
from PIL import Image
import pillow_avif  # 处理avif文件

class ImageConverterApp:
    def __init__(self, root):
        self.root = root
        self.root.title("图片批量转换工具 (AVIF/WEBP/PNG/JFIF → JPG)")
        self.root.geometry("600x300")
        
        # 创建UI元素
        self.create_widgets()
        
    def create_widgets(self):
        # 拖拽区域
        self.drop_frame = ttk.LabelFrame(self.root, text="拖拽文件到这里", width=550, height=150)
        self.drop_frame.pack(pady=20, padx=20, fill=tk.BOTH)
        self.drop_frame.pack_propagate(False)
        
        self.drop_label = ttk.Label(self.drop_frame, text="拖拽图片文件或文件夹到这里", font=('Arial', 12))
        self.drop_label.pack(expand=True)
        
        # 注册拖拽功能
        self.drop_frame.drop_target_register(DND_FILES)
        self.drop_frame.dnd_bind('<<Drop>>', self.on_drop)
        
        # 按钮区域
        self.button_frame = ttk.Frame(self.root)
        self.button_frame.pack(pady=10)
        
        self.select_files_btn = ttk.Button(self.button_frame, text="选择文件", command=self.select_files)
        self.select_files_btn.pack(side=tk.LEFT, padx=5)
        
        self.select_folder_btn = ttk.Button(self.button_frame, text="选择文件夹", command=self.select_folder)
        self.select_folder_btn.pack(side=tk.LEFT, padx=5)
        
        self.convert_btn = ttk.Button(self.button_frame, text="开始转换", command=self.start_conversion)
        self.convert_btn.pack(side=tk.LEFT, padx=5)
        
        # 进度条
        self.progress = ttk.Progressbar(self.root, orient=tk.HORIZONTAL, length=500, mode='determinate')
        self.progress.pack(pady=10)
        
        # 存储文件路径
        self.file_paths = []
        
    def on_drop(self, event):
        # 处理拖拽事件
        self.file_paths = []
        paths = event.data.split() if isinstance(event.data, str) else event.data
        
        for path in paths:
            path = path.strip('{}')  # 处理Windows路径中的花括号
            if os.path.isdir(path):
                self.collect_images_from_folder(path)
            elif self.is_supported_image(path):
                self.file_paths.append(path)
        
        self.update_file_count()
    
    def select_files(self):
        # 选择文件对话框
        filetypes = (
            ("图片文件", "*.avif;*.webp;*.png;*.jfif"),
            ("所有文件", "*.*")
        )
        
        files = filedialog.askopenfilenames(title="选择要转换的图片", filetypes=filetypes)
        if files:
            self.file_paths = [f for f in files if self.is_supported_image(f)]
            self.update_file_count()
    
    def select_folder(self):
        # 选择文件夹对话框
        folder = filedialog.askdirectory(title="选择包含图片的文件夹")
        if folder:
            self.file_paths = []
            self.collect_images_from_folder(folder)
            self.update_file_count()
    
    def collect_images_from_folder(self, folder):
        # 从文件夹收集支持的图片文件
        supported_extensions = ('.avif', '.webp', '.png', '.jfif')
        
        for root, _, files in os.walk(folder):
            for file in files:
                if file.lower().endswith(supported_extensions):
                    self.file_paths.append(os.path.join(root, file))
    
    def is_supported_image(self, file_path):
        # 检查文件是否是支持的图片格式
        ext = os.path.splitext(file_path)[1].lower()
        return ext in ('.avif', '.webp', '.png', '.jfif')
    
    def update_file_count(self):
        # 更新UI显示文件数量
        count = len(self.file_paths)
        self.drop_label.config(text=f"已选择 {count} 个文件")
    
    def start_conversion(self):
        # 开始转换过程
        if not self.file_paths:
            return
        
        total_files = len(self.file_paths)
        self.progress['maximum'] = total_files
        self.progress['value'] = 0
        
        for i, file_path in enumerate(self.file_paths, 1):
            try:
                # 转换图片
                output_path = os.path.splitext(file_path)[0] + '.jpg'
                
                with Image.open(file_path) as img:
                    if img.mode != 'RGB':
                        img = img.convert('RGB')
                    img.save(output_path, 'JPEG', quality=95)
                
                # 删除原文件
                os.remove(file_path)
            except Exception as e:
                pass
            
            self.progress['value'] = i
            self.root.update()
        
        # 重置状态
        self.file_paths = []
        self.update_file_count()
        self.progress['value'] = 0

if __name__ == "__main__":
    root = TkinterDnD.Tk()
    app = ImageConverterApp(root)
    root.mainloop()



以上为源码
成品效果图

成品打包
LZY:https://xybyg.lanzoul.com/iYYYw3o8srxe

免费评分

参与人数 6吾爱币 +5 热心值 +6 收起 理由
udon2333 + 1 + 1 谢谢@Thanks!
gzodwn + 1 + 1 谢谢@Thanks!
naixubao + 1 + 1 谢谢@Thanks!
dogox + 1 + 1 我很赞同!
Issacclark1 + 1 谢谢@Thanks!
jtjt68 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
土鸡炖蘑菇 发表于 2026-4-29 20:48
webp转gif  , 可以拖进去,可以文件夹转换 , 抖音表情包转微信好用。  转jpg意义不大啊  截个图就行了


[Python] 纯文本查看 复制代码
import sys
import os
from PIL import Image
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QPushButton, QLabel, QFileDialog, QListWidget, QProgressBar,
                             QMessageBox, QAction, QStatusBar, QGroupBox, QFrame)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize, QMimeData
from PyQt5.QtGui import QIcon, QFont, QPixmap, QColor, QPalette, QDragEnterEvent, QDropEvent


class ConversionThread(QThread):
    """后台线程用于执行图像转换任务"""
    progress_updated = pyqtSignal(int, str)
    conversion_finished = pyqtSignal()
    error_occurred = pyqtSignal(str)

    def __init__(self, file_paths, output_dir=None, background_color=(255, 255, 255)):
        super().__init__()
        self.file_paths = file_paths
        self.output_dir = output_dir
        self.background_color = background_color

    def run(self):
        total = len(self.file_paths)
        for i, file_path in enumerate(self.file_paths):
            try:
                # 更新进度
                self.progress_updated.emit(int((i + 1) / total * 100),
                                           f"正在转换: {os.path.basename(file_path)}")

                # 处理单个文件
                self.convert_webp_to_gif(file_path)

            except Exception as e:
                self.error_occurred.emit(f"转换失败: {os.path.basename(file_path)} - {str(e)}")

        self.conversion_finished.emit()

    def convert_webp_to_gif(self, file_path):
        """转换单个WebP文件到GIF"""
        # 打开WebP图像
        img = Image.open(file_path)

        # 确定输出路径
        if self.output_dir:
            output_path = os.path.join(
                self.output_dir,
                f"{os.path.splitext(os.path.basename(file_path))[0]}.gif"
            )
        else:
            output_path = os.path.splitext(file_path)[0] + ".gif"

        # 保存为GIF
        self.save_as_gif(img, output_path)

        # 关闭图像
        img.close()

    def save_as_gif(self, img, output_path):
        """将PIL Image保存为GIF"""
        # 准备帧列表
        frames = []
        durations = []

        # 检测是否是动画
        if hasattr(img, 'is_animated') and img.is_animated:
            frame_count = img.n_frames
        else:
            frame_count = 1

        # 处理每一帧
        for frame_index in range(frame_count):
            try:
                if frame_count > 1:
                    img.seek(frame_index)

                # 获取当前帧
                current_frame = img.copy()

                # 获取帧持续时间(毫秒)
                duration = img.info.get('duration', 100)
                durations.append(duration)

                # 处理透明度 - 使用更可靠的方法
                frame_rgb = self.process_transparency(current_frame)

                frames.append(frame_rgb)

            except EOFError:
                # 帧索引超出范围,停止处理
                break
            except Exception as e:
                # 处理单帧错误,继续处理其他帧
                print(f"处理第{frame_index}帧时出错: {e}")
                continue

        # 确保至少有1帧
        if not frames:
            raise ValueError("没有有效的帧可以保存")

        # 保存为GIF
        if len(frames) == 1:
            # 单帧GIF
            frames[0].save(
                output_path,
                format="GIF",
                save_all=False,
                optimize=True,
                quality=85
            )
        else:
            # 多帧GIF
            frames[0].save(
                output_path,
                format="GIF",
                save_all=True,
                append_images=frames[1:],
                duration=durations,
                loop=0,  # 无限循环
                optimize=True,
                disposal=2,  # 恢复到背景色
                quality=85
            )

    def process_transparency(self, frame):
        """处理帧的透明度,确保返回RGB模式的图像"""
        # 转换为RGBA模式以便处理透明度
        if frame.mode in ['RGBA', 'LA', 'P', 'PA']:
            # 转换为RGBA
            rgba_frame = frame.convert('RGBA')

            # 创建白色背景
            background = Image.new('RGBA', rgba_frame.size,
                                   (*self.background_color, 255))

            # 合并:将透明图像放在白色背景上
            alpha = rgba_frame.split()[3]  # Alpha通道
            rgb_frame = Image.composite(rgba_frame, background, alpha)

            # 转换为RGB
            rgb_frame = rgb_frame.convert('RGB')
        elif frame.mode in ['RGB', 'L']:
            # 已经是RGB或灰度,直接转换
            rgb_frame = frame.convert('RGB')
        else:
            # 其他模式尝试转换为RGB
            try:
                rgb_frame = frame.convert('RGB')
            except:
                # 如果转换失败,先转换为RGBA再处理
                rgba_frame = frame.convert('RGBA')
                background = Image.new('RGBA', rgba_frame.size,
                                       (*self.background_color, 255))
                alpha = rgba_frame.split()[3]
                rgb_frame = Image.composite(rgba_frame, background, alpha)
                rgb_frame = rgb_frame.convert('RGB')

        return rgb_frame


class WebPtoGifConverter(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("WebP 转 GIF 转换器")
        self.setGeometry(100, 100, 850, 650)

        # 设置应用图标
        self.setWindowIcon(self.create_app_icon())

        # 创建主部件和布局
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        main_layout = QVBoxLayout(main_widget)
        main_layout.setContentsMargins(15, 15, 15, 15)
        main_layout.setSpacing(15)

        # 标题标签
        title_label = QLabel("WebP 转 GIF 转换器")
        title_font = QFont()
        title_font.setPointSize(24)
        title_font.setBold(True)
        title_label.setFont(title_font)
        title_label.setAlignment(Qt.AlignCenter)
        title_label.setStyleSheet("color: #3498db; margin-bottom: 10px;")
        main_layout.addWidget(title_label)

        # 说明标签
        description_label = QLabel("支持拖放文件/文件夹 | 支持批量转换")
        description_label.setAlignment(Qt.AlignCenter)
        description_label.setStyleSheet("color: #7f8c8d; margin-bottom: 20px; font-size: 14px;")
        main_layout.addWidget(description_label)

        # 创建操作区域
        group_box = QGroupBox("操作面板")
        group_box.setStyleSheet("""
            QGroupBox {
                font-weight: bold;
                font-size: 14px;
                border: 2px solid #3498db;
                border-radius: 8px;
                margin-top: 20px;
                padding-top: 15px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                subcontrol-position: top center;
                padding: 0 10px;
                color: #3498db;
            }
        """)
        main_layout.addWidget(group_box)

        button_layout = QHBoxLayout()
        button_layout.setContentsMargins(15, 20, 15, 20)
        button_layout.setSpacing(20)

        # 选择文件按钮
        self.select_file_btn = QPushButton("选择文件")
        self.select_file_btn.setIcon(QIcon.fromTheme("document-open"))
        self.select_file_btn.setIconSize(QSize(20, 20))
        self.select_file_btn.setStyleSheet(self.get_button_style("#3498db"))
        self.select_file_btn.clicked.connect(self.select_files)
        button_layout.addWidget(self.select_file_btn)

        # 选择文件夹按钮
        self.select_folder_btn = QPushButton("选择文件夹")
        self.select_folder_btn.setIcon(QIcon.fromTheme("folder-open"))
        self.select_folder_btn.setIconSize(QSize(20, 20))
        self.select_folder_btn.setStyleSheet(self.get_button_style("#2ecc71"))
        self.select_folder_btn.clicked.connect(self.select_folder)
        button_layout.addWidget(self.select_folder_btn)

        # 清空列表按钮
        self.clear_list_btn = QPushButton("清空列表")
        self.clear_list_btn.setIcon(QIcon.fromTheme("edit-clear"))
        self.clear_list_btn.setIconSize(QSize(20, 20))
        self.clear_list_btn.setStyleSheet(self.get_button_style("#e74c3c"))
        self.clear_list_btn.clicked.connect(self.clear_file_list)
        button_layout.addWidget(self.clear_list_btn)

        # 转换按钮
        self.convert_btn = QPushButton("开始转换")
        self.convert_btn.setIcon(QIcon.fromTheme("media-playback-start"))
        self.convert_btn.setIconSize(QSize(20, 20))
        self.convert_btn.setStyleSheet(self.get_button_style("#9b59b6"))
        self.convert_btn.clicked.connect(self.start_conversion)
        button_layout.addWidget(self.convert_btn)

        group_box.setLayout(button_layout)

        # 文件列表区域
        file_group = QGroupBox("待转换文件列表")
        file_group.setStyleSheet("""
            QGroupBox {
                font-weight: bold;
                font-size: 14px;
                border: 2px solid #2c3e50;
                border-radius: 8px;
                margin-top: 10px;
                padding-top: 15px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                subcontrol-position: top center;
                padding: 0 10px;
                color: #2c3e50;
            }
        """)
        main_layout.addWidget(file_group, 1)

        file_layout = QVBoxLayout()
        file_layout.setContentsMargins(10, 20, 10, 10)

        # 文件列表
        self.file_list = QListWidget()
        self.file_list.setSelectionMode(QListWidget.ExtendedSelection)
        self.file_list.setStyleSheet("""
            QListWidget {
                background-color: #f8f9fa;
                border: 1px solid #ced4da;
                border-radius: 5px;
                padding: 5px;
                font-size: 13px;
            }
            QListWidget::item {
                padding: 10px;
                border-bottom: 1px solid #e9ecef;
            }
            QListWidget::item:selected {
                background-color: #d6eaf8;
                color: #1a73e8;
                border-radius: 3px;
            }
        """)
        self.file_list.setAlternatingRowColors(True)

        # 设置拖放支持
        self.file_list.setAcceptDrops(True)
        self.file_list.setDragEnabled(True)
        self.file_list.setDragDropMode(QListWidget.DropOnly)
        self.file_list.viewport().setAcceptDrops(True)
        self.file_list.setDropIndicatorShown(True)

        file_layout.addWidget(self.file_list)

        # 添加拖放提示标签
        drop_hint = QLabel("拖放 WebP 文件或文件夹到这里")
        drop_hint.setAlignment(Qt.AlignCenter)
        drop_hint.setStyleSheet("color: #95a5a6; font-style: italic; font-size: 12px; padding: 10px;")
        file_layout.addWidget(drop_hint)

        file_group.setLayout(file_layout)

        # 进度区域
        progress_frame = QFrame()
        progress_frame.setStyleSheet("background-color: #f1f2f6; border-radius: 8px; padding: 15px;")
        main_layout.addWidget(progress_frame)

        progress_layout = QVBoxLayout(progress_frame)

        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setStyleSheet("""
            QProgressBar {
                border: 1px solid #ced4da;
                border-radius: 5px;
                text-align: center;
                background-color: white;
                height: 25px;
                font-size: 12px;
            }
            QProgressBar::chunk {
                background-color: #3498db;
                width: 10px;
                border-radius: 4px;
            }
        """)
        self.progress_bar.setVisible(False)
        progress_layout.addWidget(self.progress_bar)

        # 状态标签
        self.status_label = QLabel("就绪")
        self.status_label.setAlignment(Qt.AlignCenter)
        self.status_label.setStyleSheet("color: #7f8c8d; font-size: 13px; padding-top: 8px;")
        progress_layout.addWidget(self.status_label)

        # 状态栏
        self.status_bar = QStatusBar()
        self.status_bar.setStyleSheet("background-color: #ecf0f1; color: #7f8c8d; font-size: 11px;")
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage("就绪 | 拖放文件或文件夹到列表中")

        # 创建菜单
        self.create_menu()

        # 存储文件路径
        self.selected_files = []

        # 设置初始状态
        self.update_ui_state()

        # 启用拖放
        self.setAcceptDrops(True)

    def create_app_icon(self):
        """创建应用图标"""
        # 这里使用内置图标作为示例,实际应用中可以使用自定义图标
        return QIcon.fromTheme("image-x-generic")

    def get_button_style(self, color):
        """返回按钮样式表"""
        return f"""
            QPushButton {{
                background-color: {color};
                color: white;
                border: none;
                border-radius: 5px;
                padding: 12px 20px;
                font-weight: bold;
                font-size: 13px;
                min-width: 100px;
            }}
            QPushButton:hover {{
                background-color: {self.adjust_color(color, -30)};
            }}
            QPushButton:pressed {{
                background-color: {self.adjust_color(color, -50)};
                padding: 13px 19px 11px 21px;
            }}
            QPushButton:disabled {{
                background-color: #bdc3c7;
                color: #7f8c8d;
            }}
        """

    def adjust_color(self, color, amount):
        """调整颜色亮度"""
        # 将十六进制颜色转换为RGB
        r = int(color[1:3], 16)
        g = int(color[3:5], 16)
        b = int(color[5:7], 16)

        # 调整RGB值
        r = max(0, min(255, r + amount))
        g = max(0, min(255, g + amount))
        b = max(0, min(255, b + amount))

        # 转换回十六进制
        return f"#{r:02x}{g:02x}{b:02x}"

    def create_menu(self):
        """创建菜单栏"""
        menubar = self.menuBar()
        menubar.setStyleSheet("""
            QMenuBar {
                background-color: #f8f9fa;
                padding: 4px;
                border-bottom: 1px solid #e0e0e0;
            }
            QMenuBar::item {
                padding: 5px 10px;
                background: transparent;
                border-radius: 4px;
            }
            QMenuBar::item:selected {
                background: #e3f2fd;
            }
            QMenuBar::item:pressed {
                background: #bbdefb;
            }
        """)

        # 文件菜单
        file_menu = menubar.addMenu("文件")

        open_file_action = QAction("打开文件", self)
        open_file_action.setShortcut("Ctrl+O")
        open_file_action.triggered.connect(self.select_files)
        file_menu.addAction(open_file_action)

        open_folder_action = QAction("打开文件夹", self)
        open_folder_action.setShortcut("Ctrl+D")
        open_folder_action.triggered.connect(self.select_folder)
        file_menu.addAction(open_folder_action)

        file_menu.addSeparator()

        exit_action = QAction("退出", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 编辑菜单
        edit_menu = menubar.addMenu("编辑")

        clear_action = QAction("清空列表", self)
        clear_action.setShortcut("Ctrl+Del")
        clear_action.triggered.connect(self.clear_file_list)
        edit_menu.addAction(clear_action)

        # 帮助菜单
        help_menu = menubar.addMenu("帮助")

        about_action = QAction("关于", self)
        about_action.setShortcut("F1")
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)

    def dragEnterEvent(self, event: QDragEnterEvent):
        """处理拖拽进入事件"""
        if event.mimeData().hasUrls():
            event.acceptProposedAction()

    def dropEvent(self, event: QDropEvent):
        """处理拖放事件"""
        urls = event.mimeData().urls()
        if urls:
            new_files = []
            for url in urls:
                path = url.toLocalFile()
                # 如果是目录,则遍历
                if os.path.isdir(path):
                    for root, dirs, files in os.walk(path):
                        for file in files:
                            if file.lower().endswith('.webp'):
                                new_files.append(os.path.join(root, file))
                # 如果是文件,直接添加
                elif path.lower().endswith('.webp'):
                    new_files.append(path)

            if new_files:
                self.selected_files.extend(new_files)
                self.update_file_list()
                self.status_bar.showMessage(f"添加了 {len(new_files)} 个文件")
            else:
                self.status_bar.showMessage("未找到有效的 WebP 文件")
            event.acceptProposedAction()

    def select_files(self):
        """选择单个或多个文件"""
        files, _ = QFileDialog.getOpenFileNames(
            self, "选择WebP文件", "",
            "WebP图像 (*.webp);;所有文件 (*)"
        )

        if files:
            self.selected_files.extend(files)
            self.update_file_list()
            self.status_bar.showMessage(f"添加了 {len(files)} 个文件")

    def select_folder(self):
        """选择文件夹并添加所有WebP文件"""
        folder = QFileDialog.getExistingDirectory(
            self, "选择包含WebP文件的文件夹"
        )

        if folder:
            new_files = []
            # 查找文件夹中的所有WebP文件
            for root, dirs, files in os.walk(folder):
                for file in files:
                    if file.lower().endswith(".webp"):
                        new_files.append(os.path.join(root, file))

            if new_files:
                self.selected_files.extend(new_files)
                self.update_file_list()
                self.status_bar.showMessage(f"添加了 {len(new_files)} 个文件")
            else:
                self.status_bar.showMessage("该文件夹中未找到 WebP 文件")

    def clear_file_list(self):
        """清空文件列表"""
        self.selected_files = []
        self.file_list.clear()
        self.update_ui_state()
        self.status_bar.showMessage("已清空文件列表")

    def update_file_list(self):
        """更新文件列表显示"""
        self.file_list.clear()
        for file_path in self.selected_files:
            self.file_list.addItem(os.path.basename(file_path))

        self.update_ui_state()

        # 更新状态栏
        count = len(self.selected_files)
        self.status_bar.showMessage(f"已选择 {count} 个文件 | 拖放添加更多文件")

    def update_ui_state(self):
        """根据当前状态更新UI元素"""
        has_files = bool(self.selected_files)
        self.convert_btn.setEnabled(has_files)
        self.clear_list_btn.setEnabled(has_files)

        if has_files:
            self.convert_btn.setToolTip(f"转换 {len(self.selected_files)} 个文件")
        else:
            self.convert_btn.setToolTip("请先选择文件")

    def start_conversion(self):
        """开始转换过程"""
        if not self.selected_files:
            QMessageBox.warning(self, "没有文件", "请先选择要转换的文件!")
            return

        # 询问输出目录
        output_dir = QFileDialog.getExistingDirectory(
            self, "选择输出目录",
            options=QFileDialog.ShowDirsOnly
        )

        if not output_dir:
            return

        # 准备UI进行转换
        self.progress_bar.setVisible(True)
        self.progress_bar.setValue(0)
        self.status_label.setText("正在准备转换...")
        self.status_bar.showMessage("转换中...")

        # 禁用按钮
        self.set_buttons_enabled(False)

        # 创建并启动转换线程
        self.conversion_thread = ConversionThread(self.selected_files, output_dir)
        self.conversion_thread.progress_updated.connect(self.update_progress)
        self.conversion_thread.conversion_finished.connect(self.conversion_complete)
        self.conversion_thread.error_occurred.connect(self.show_error)
        self.conversion_thread.start()

    def set_buttons_enabled(self, enabled):
        """启用或禁用所有按钮"""
        self.select_file_btn.setEnabled(enabled)
        self.select_folder_btn.setEnabled(enabled)
        self.clear_list_btn.setEnabled(enabled)
        self.convert_btn.setEnabled(enabled)

    def update_progress(self, value, message):
        """更新进度条和状态标签"""
        self.progress_bar.setValue(value)
        self.status_label.setText(message)
        self.status_bar.showMessage(f"转换中: {value}%")

    def conversion_complete(self):
        """转换完成时调用"""
        self.progress_bar.setValue(100)
        self.status_label.setText("转换完成!")
        self.status_bar.showMessage(f"成功转换 {len(self.selected_files)} 个文件")

        # 显示完成消息
        QMessageBox.information(
            self, "转换完成",
            f"成功转换 {len(self.selected_files)} 个文件!\n\n"
            f"输出目录: {self.conversion_thread.output_dir}"
        )

        # 重置UI
        self.set_buttons_enabled(True)
        self.selected_files = []
        self.update_file_list()

        # 隐藏进度条
        self.progress_bar.setVisible(False)

    def show_error(self, message):
        """显示错误消息"""
        self.status_bar.showMessage(message)
        QMessageBox.critical(self, "转换错误", message)

    def show_about(self):
        """显示关于对话框"""
        about_text = """
        <html>
        <head>
        <style>
        h2 { color: #3498db; text-align: center; }
        p { font-size: 13px; }
        .center { text-align: center; }
        .highlight { color: #e74c3c; font-weight: bold; }
        </style>
        </head>
        <body>
        <h2>WebP 转 GIF 转换器</h2>
        <p class="center">版本: 2.0</p>
        <p>此工具可将WebP图像转换为GIF格式,支持批量转换和拖放操作。</p>
        <p><b>主要功能:</b></p>
        <ul>
            <li>拖放文件或文件夹添加文件</li>
            <li>批量转换多个文件</li>
            <li>转换进度实时显示</li>
            <li>支持选择输出目录</li>
        </ul>
        <p class="center highlight">使用方法: 拖放WebP文件到窗口或使用按钮添加文件</p>
        <p class="center">&#169; 2023 WebP转GIF工具 | 使用Python和PyQt5开发</p>
        </body>
        </html>
        """
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)
        msg.setWindowTitle("关于")
        msg.setTextFormat(Qt.RichText)
        msg.setText(about_text)
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()


if __name__ == "__main__":
    app = QApplication(sys.argv)

    # 设置应用样式
    app.setStyle("Fusion")

    # 设置全局字体
    font = QFont()
    font.setFamily("Segoe UI")
    font.setPointSize(10)
    app.setFont(font)

    # 设置调色板
    palette = QPalette()
    palette.setColor(QPalette.Window, QColor(245, 245, 245))
    palette.setColor(QPalette.WindowText, Qt.black)
    palette.setColor(QPalette.Base, QColor(255, 255, 255))
    palette.setColor(QPalette.AlternateBase, QColor(240, 240, 240))
    palette.setColor(QPalette.ToolTipBase, Qt.white)
    palette.setColor(QPalette.ToolTipText, Qt.black)
    palette.setColor(QPalette.Text, Qt.black)
    palette.setColor(QPalette.Button, QColor(240, 240, 240))
    palette.setColor(QPalette.ButtonText, Qt.black)
    palette.setColor(QPalette.BrightText, Qt.red)
    palette.setColor(QPalette.Highlight, QColor(52, 152, 219))
    palette.setColor(QPalette.HighlightedText, Qt.white)
    app.setPalette(palette)

    converter = WebPtoGifConverter()
    converter.show()

    sys.exit(app.exec_())
推荐
gzodwn 发表于 2026-4-29 08:52
谢谢楼主分享!辛苦了。
有两个地方,建议改一下。
1、不要删除原图片文件。
2、图片压缩率,请允许自定义,或者直接使用100%的,不要用95%的压缩率
沙发
jtjt68 发表于 2026-4-28 21:11
3#
helian147 发表于 2026-4-28 21:24
[Python] 纯文本查看 复制代码
import pillow_avif  # 处理avif文件


看代码里面, pillow_avif模块没用上啊?
4#
qinlao123 发表于 2026-4-28 21:45
喜欢本地的,不想用网站转。谢谢。
5#
 楼主| Willian 发表于 2026-4-28 21:45 |楼主
helian147 发表于 2026-4-28 21:24
看代码里面, pillow_avi ...

它是pillow里面的插件,实际上是调用了的,缺了没法转avif。库全名pillow-avif-plugin

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
helian147 + 1 + 1 谢谢@Thanks!

查看全部评分

6#
thornjay 发表于 2026-4-28 22:52
为什么python开发的界面都这么丑
7#
雷神119 发表于 2026-4-29 03:22
实用的软件,可以下载试用。
8#
hbdzbs 发表于 2026-4-29 06:37
喜欢本地的,不想用网站转。谢谢。
9#
weijunyan 发表于 2026-4-29 08:06
转换图片格式方便了啊
10#
anzong5211314 发表于 2026-4-29 08:22
感谢分享  下载试试好用吗?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-6 03:22

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表