桌面宠物其实也就是在桌面自动播放动态图,在这基础上我添加了四处设计:
- 左键单击随机切换gif
- 左键长按拖动
- 滑轮修改播放速度
- 将gif和代码打包到一个exe
其中第四步将gif转为二进制字节流,并添加到py文件中,转换代码如下
import os
import io
from PIL import Imageall_file = os.listdir()
for file in all_file:if file.endswith('.gif'):real_name = file[:-4]gif_frames = Image.open(file)write_data = "{}=[".format(real_name)for idx,frame in enumerate(ImageSequence.Iterator(gif_frames)):imgByteArr = io.BytesIO() frame.save(imgByteArr, format='PNG') imgByteArr = imgByteArr.getvalue() write_data = write_data + "%s," % (imgByteArr)write_data = write_data + '{}]\n'.format(frame.info['duration'])f = open("all_image.py","a+")f.write(write_data)f.close()
该代码遍历了当前路径下的所有以".gif"结尾的文件,并把gif逐帧转换成png,转为字节流后保存到一个列表,并且列表的最后一个元素记录了每一帧的持续时间。(我尝试直接保存gif,但在使用时仅展示了第一帧,且保存的py文件也远小于所有gif的大小,因此才选择了逐帧保存的方案)
保存的py文件如下所示
引用以上方法创建的all_image.py,完整代码如下:
from tkinter import *
import io
from PIL import Image,ImageTk
from all_image import *
import randomTRANSCOLOUR = 'gray'
class MyPet():def __init__(self):self.root = Tk()#窗口坐标self.root_x=500self.root_y=150self.root.geometry('200x200+{}+{}'.format(self.root_x,self.root_y))#窗口置顶self.root.attributes('-topmost',1)#设置透明self.root.wm_attributes('-transparentcolor', TRANSCOLOUR)#隐藏边框self.root.overrideredirect(True)#鼠标点击坐标self.press_x = 0self.press_y = 0self.root.bind("<ButtonPress-1>", self.start_move)self.root.bind("<B1-Motion>", self.on_motion)self.root.bind("<ButtonRelease-1>", self.end_move)self.root.bind("<MouseWheel>",self.change_duration)#当前播放帧数self.frame_idx = 0#加载gifgif_name = [gif1,gif2,gif3,gif4,gif5]self.normal_frames_list = []self.normal_duration_list = []for temp_gif in gif_name:temp_frames,temp_duration = self.get_tk_image(temp_gif)self.normal_frames_list.append(temp_frames)self.normal_duration_list.append(temp_duration)self.float_frames,self.float_duration = self.get_tk_image(gif6)self.normal_frames = self.normal_frames_list.pop(0)self.normal_duration = self.normal_duration_list.pop(0)#当前gif速度self.cur_duration = self.normal_duration#当前播放gifself.cur_frames = self.normal_frames#当前状态self.pet_state = 'normal'self.root.after(0, self.update) # 立即启动定时器函数(update)#添加画板self.canvas = Canvas(self.root,borderwidth=0,highlightthickness=0)self.canvas.pack(fill=BOTH, expand=Y)self.last_rect = self.canvas.create_rectangle(0, 0, 200, 200, fill=TRANSCOLOUR,)self.last_image = self.canvas.create_image(0,0,image=self.cur_frames[0],anchor='nw')self.root.mainloop()def start_move(self,event):#鼠标左键单击self.press_x = event.x_rootself.press_y = event.y_rootdef end_move(self,event):#鼠标释放if self.pet_state=='float':self.change_state('normal')elif self.pet_state=='normal':self.change_state('change')def on_motion(self,event):#鼠标左键单击并移动if self.pet_state!='float':self.change_state('float')else:passdeltax = event.x_root - self.press_xdeltay = event.y_root - self.press_yself.press_x = self.press_x + deltaxself.press_y = self.press_y + deltayself.root_x = self.root_x + deltaxself.root_y = self.root_y + deltayself.root.geometry("+%s+%s" % (self.root_x,self.root_y))def change_state(self,state):#修改pet状态self.pet_state = stateself.frame_idx = 0if self.pet_state == 'float':self.cur_frames = self.float_framesself.cur_duration = self.float_durationelif self.pet_state == 'normal':self.cur_frames = self.normal_framesself.cur_duration = self.normal_durationelif self.pet_state == 'change':#随机更换giftemp_n = random.choice(range(len(self.normal_frames_list)))temp_frames = self.normal_framestemp_duration = self.normal_durationself.normal_frames = self.normal_frames_list.pop(temp_n)self.normal_duration = self.normal_duration_list.pop(temp_n)self.cur_frames = self.normal_framesself.cur_duration = self.normal_durationself.normal_frames_list.append(temp_frames)self.normal_duration_list.append(temp_duration)self.pet_state = 'normal'def change_duration(self,event):#鼠标滑轮if event.delta>0:if self.cur_duration<=10:returnelse:self.cur_duration-=5else:self.cur_duration+=5 def get_tk_image(self,gif):#从字节流获取图像和durationgif_frames = []for temp_png in gif[:-1]:bytes_stream = io.BytesIO(temp_png)gifimg = Image.open(bytes_stream)roiimg = ImageTk.PhotoImage(gifimg)gif_frames.append(roiimg)return gif_frames,gif[-1]def update(self): # 定时器函数frame = self.cur_frames[self.frame_idx]self.frame_idx += 1if self.frame_idx>=len(self.cur_frames):self.frame_idx=0self.canvas.delete(self.last_rect)self.canvas.delete(self.last_image)self.last_rect = self.canvas.create_rectangle(0, 0, 200, 200, fill=TRANSCOLOUR, outline=TRANSCOLOUR)self.last_image = self.canvas.create_image(0,0,image=frame,anchor='nw')self.root.after(self.cur_duration, self.update) # 0.1秒(100毫秒)之后继续执行定时器函数(update)if __name__=='__main__':mypet = MyPet()
此时使用以下命令打包时将生成一个独立运行的exe
pyinstaller -F -w tkinter_gif.py
效果如下