使用Python实现播放“.gif”文件增强版
使用Python实现播放“.gif”文件增强版
使用Python和第三方库Pillow实现实现播放gif文件,以前介绍过一个简单实现https://blog.csdn.net/cnds123/article/details/137992560 ,那个功能比较简单,实用意义不大。现在介绍一个增强版的。
关于“.gif”文件
GIF(Graphics Interchange Format,图形交换格式)是一种由CompuServe公司开发的图像格式,通过连续帧实现动画效果,文件体积小且画面连贯 。GIF本质是静态图片的无损压缩格式,虽支持动画效果,但缺乏音频流且色彩深度有限(仅8位)。
下面介绍用Python实现的控制播放器,用户可细致的控制,包括:打开、播放、暂停、停止,设置播放速度,逐帧控制——前一帧、后一帧。
界面截图:

打开:停止当前播放,加载新GIF文件。
播放:启动动画循环。
暂停:停止动画循环。
停止:暂停播放并重置到第一帧。
前一帧:允许用户逐步后退到前一帧,自动暂停播放。
后一帧:允许用户逐步前进到后一帧,自动暂停播放。
循环处理,当到达第一帧时后退会跳转到最后一帧,反之亦然。
滑动条控制:添加了速度滑动条,允许用户在10ms到500ms之间调整帧延迟。
使用GIF原始速度:可以自动设置GIF的原始平均延迟时间。
状态栏:显示播放中还是暂停状态,当前设置的延迟时间,显示当前帧位置和总帧数(例如:"帧 5/24"),当前设置的帧延迟时间。
这个GIF播放器基于以下三个核心库构建:
Tkinter - Python的标准GUI库,用于创建窗口、按钮等界面元素。
PIL/Pillow - Python图像处理库,用于读取和处理GIF文件。
tkinter.filedialog - 文件选择对话框,用于选择GIF文件。
确保已安装所需的库pillow,关于pillow简要介绍和安装可见https://blog.csdn.net/cnds123/article/details/137992560
源码如下:
import tkinter as tk
from tkinter import filedialog, ttk
from PIL import Image, ImageTkclass GifPlayer:def __init__(self, master):self.master = masterself.master.title("GIF Player")self.master.geometry("500x550") # 增加高度以容纳新控件 ☆#self.master.minsize(400, 450)# 初始化变量self.frames = [] # 存储GIF的所有帧self.current_frame = 0 # 当前帧索引self.is_playing = False # 播放状态self.gif_image = None # PIL Image对象self.frame_delay = 100 # 默认帧延迟(毫秒)self.original_delays = [] # 存储原始GIF帧延迟# 创建主框架,用于管理布局main_frame = tk.Frame(self.master)main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)# 创建显示GIF的标签self.label = tk.Label(main_frame, text="请打开一个GIF文件", bg="white", relief=tk.SUNKEN, bd=1)self.label.pack(fill=tk.BOTH, expand=True)# 创建状态显示标签self.status_label = tk.Label(main_frame, text="状态: 未加载", relief=tk.SUNKEN, bd=1)self.status_label.pack(fill=tk.X, pady=(5, 0))# 创建控制面板框架control_frame = tk.Frame(main_frame)control_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=10)# 基本控制部分basic_control_frame = tk.Frame(control_frame)basic_control_frame.pack(fill=tk.X, pady=5)# 打开按钮self.open_button = tk.Button(basic_control_frame, text="打开", command=self.open_gif)self.open_button.pack(side=tk.LEFT, padx=5)# 播放按钮self.play_button = tk.Button(basic_control_frame, text="播放", command=self.play_gif)self.play_button.pack(side=tk.LEFT, padx=5)# 暂停按钮self.pause_button = tk.Button(basic_control_frame, text="暂停", command=self.pause_gif)self.pause_button.pack(side=tk.LEFT, padx=5)# 停止按钮self.stop_button = tk.Button(basic_control_frame, text="停止", command=self.stop_gif)self.stop_button.pack(side=tk.LEFT, padx=5) # 前一帧按钮self.prev_frame_button = tk.Button(basic_control_frame, text="前一帧", command=self.prev_frame)self.prev_frame_button.pack(side=tk.LEFT, padx=5)# 后一帧按钮self.next_frame_button = tk.Button(basic_control_frame, text="后一帧", command=self.next_frame)self.next_frame_button.pack(side=tk.LEFT, padx=5)# 速度控制部分speed_control_frame = tk.Frame(control_frame)speed_control_frame.pack(fill=tk.X, pady=5)# 速度标签tk.Label(speed_control_frame, text="速度:").pack(side=tk.LEFT, padx=5)# 速度滑动条self.speed_scale = tk.Scale(speed_control_frame, from_=10, to=500, orient=tk.HORIZONTAL,showvalue=False, # 不显示默认数值False, 否者用 True command=self.set_speed)self.speed_scale.set(self.frame_delay) # 设置初始值self.speed_scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)# 使用原始延迟按钮self.use_original_delay_button = tk.Button(speed_control_frame, text="使用GIF原始速度", command=self.use_original_delay)self.use_original_delay_button.pack(side=tk.LEFT, padx=5)# 初始化按钮状态self.update_button_state()def open_gif(self):"""打开文件对话框选择GIF文件并加载"""# 打开文件对话框,只显示GIF文件file_path = filedialog.askopenfilename(title="选择GIF文件",filetypes=[("GIF文件", "*.gif"), ("所有文件", "*.*")])# 如果用户选择了文件if file_path:# 停止当前播放self.stop_gif()try:# 打开新的GIF文件self.gif_image = Image.open(file_path)# 获取屏幕尺寸,用于限制GIF显示大小screen_width = self.master.winfo_screenwidth()screen_height = self.master.winfo_screenheight()# 设置最大显示尺寸(留出空间给窗口边框和按钮)max_width = int(screen_width * 0.7)max_height = int(screen_height * 0.7) # ☆# 提取所有帧和原始延迟self.frames = []self.original_delays = []try:for frame in range(self.gif_image.n_frames):self.gif_image.seek(frame) # 定位到指定帧# 获取帧延迟时间(毫秒)delay = self.gif_image.info.get('duration', 100)self.original_delays.append(delay)frame_image = self.gif_image.copy().convert("RGBA") # 复制并转换帧# 如果GIF尺寸过大,则进行缩放if frame_image.width > max_width or frame_image.height > max_height:# 计算缩放比例,保持宽高比ratio = min(max_width / frame_image.width, max_height / frame_image.height)new_width = int(frame_image.width * ratio)new_height = int(frame_image.height * ratio)frame_image = frame_image.resize((new_width, new_height), Image.Resampling.LANCZOS)photo = ImageTk.PhotoImage(frame_image) # 转换为Tkinter可用的格式self.frames.append(photo)except EOFError:pass # 处理帧读取完毕的情况# 重置当前帧索引self.current_frame = 0# 显示第一帧if self.frames:self.label.config(image=self.frames[0], text="")# 调整窗口大小以适应GIF,但不超过最大尺寸img_width = self.frames[0].width()img_height = self.frames[0].height()# 设置窗口大小,考虑按钮区域window_width = min(img_width + 50, max_width)window_height = min(img_height + 150, max_height) # 增加高度以容纳新控件 ☆self.master.geometry(f"{window_width}x{window_height}")# 更新窗口标题显示文件名self.master.title(f"GIF Player - {file_path.split('/')[-1]}")# 更新状态显示self.update_status()# 更新按钮状态self.update_button_state()except Exception as e:# 如果加载失败,显示错误信息self.label.config(image="", text=f"无法加载GIF文件:\n{str(e)}")self.frames = []self.update_button_state()self.status_label.config(text="状态: 加载失败")def update_button_state(self):"""根据当前状态更新按钮的可用性"""has_frames = len(self.frames) > 0# 只有在有帧的情况下,播放、暂停和停止按钮才可用self.play_button.config(state=tk.NORMAL if has_frames else tk.DISABLED)self.pause_button.config(state=tk.NORMAL if has_frames else tk.DISABLED)self.stop_button.config(state=tk.NORMAL if has_frames else tk.DISABLED)self.prev_frame_button.config(state=tk.NORMAL if has_frames else tk.DISABLED)self.next_frame_button.config(state=tk.NORMAL if has_frames else tk.DISABLED)self.speed_scale.config(state=tk.NORMAL if has_frames else tk.DISABLED)self.use_original_delay_button.config(state=tk.NORMAL if has_frames else tk.DISABLED)def update_status(self):"""更新状态显示"""if self.frames:status = f"状态: 帧 {self.current_frame+1}/{len(self.frames)}"if self.is_playing:status += " - 播放中"else:status += " - 暂停"status += f" - 延迟: {self.frame_delay}ms"self.status_label.config(text=status)else:self.status_label.config(text="状态: 未加载")def set_speed(self, value):"""设置播放速度"""self.frame_delay = int(value)self.update_status()def use_original_delay(self):"""使用GIF的原始延迟时间"""if self.frames and self.original_delays:# 计算平均延迟avg_delay = sum(self.original_delays) // len(self.original_delays)self.frame_delay = avg_delayself.speed_scale.set(avg_delay)self.update_status()def play_gif(self):"""开始播放GIF动画"""if not self.is_playing and self.frames:self.is_playing = Trueself.update_status()self.play_next_frame() # 开始播放循环def pause_gif(self):"""暂停播放GIF动画"""self.is_playing = Falseself.update_status()def stop_gif(self):"""停止播放GIF动画并回到第一帧"""self.is_playing = Falseif self.frames:self.current_frame = 0self.label.config(image=self.frames[self.current_frame])self.update_status()def prev_frame(self):"""显示前一帧"""if self.frames:self.is_playing = Falseself.current_frame = (self.current_frame - 1) % len(self.frames)self.label.config(image=self.frames[self.current_frame])self.update_status()def next_frame(self):"""显示后一帧"""if self.frames:self.is_playing = Falseself.current_frame = (self.current_frame + 1) % len(self.frames)self.label.config(image=self.frames[self.current_frame])self.update_status()def play_next_frame(self):"""播放下一帧并设置定时器播放后续帧"""if self.is_playing and self.frames:# 显示当前帧self.label.config(image=self.frames[self.current_frame])# 更新到下一帧(循环播放)self.current_frame = (self.current_frame + 1) % len(self.frames)# 更新状态显示self.update_status()# 设置定时器,使用当前延迟时间播放下一帧self.master.after(self.frame_delay, self.play_next_frame)if __name__ == "__main__":# 创建主窗口并启动GIF播放器root = tk.Tk()player = GifPlayer(root)root.mainloop()
