UI更新闪烁的解决办法——双缓冲技术
- 前置内容
- Canvas组件的图像初始化
- Canvas组件的图像动态更新
- 双缓冲技术
最近项目需要动态更新tkinter的Canvas组件的图片,出现画面闪烁现象,用双缓冲技术解决了。
前置内容
Canvas组件的图像初始化
可以调用Canvas的create_image方法完成其图像初始化,此方法返回一个句柄,后面可以利用此句柄更新Canvas的图像。
# coding=utf-8fromtkinterimportTk,CanvasfromPILimportImage,ImageTkimportnumpyasnpdefrandom_img():returnImageTk.PhotoImage(Image.fromarray(np.random.randint(0,255,(480,640),dtype=np.uint8)))if__name__=='__main__':root_wd=Tk()root_wd.geometry("640x480")root_wd.title("Dynamic change canvas images")root_wd.minsize(640,480)canvas=Canvas(root_wd,width=640,height=480)canvas.place(x=0,y=0)img=random_img()canvas.img_id=canvas.create_image(320,240,image=img)root_wd.mainloop()Canvas组件的图像动态更新
可以调用Canvas的itemconfig方法完成动态更新,需要用到create_image方法返回的句柄。
# coding=utf-8fromtkinterimportTk,CanvasfromPILimportImage,ImageTkfromthreadingimportThreadimportnumpyasnpfromtimeimportsleepdefrandom_img():returnImageTk.PhotoImage(Image.fromarray(np.random.randint(0,255,(800,1280),dtype=np.uint8)))defupdate(canvas:Canvas):whileTrue:img=random_img()canvas.itemconfig(canvas.img_id,image=img)sleep(.05)if__name__=='__main__':root_wd=Tk()root_wd.geometry("1280x800")root_wd.title("Dynamic change canvas images")root_wd.minsize(1280,800)canvas=Canvas(root_wd,width=1280,height=800)canvas.place(x=0,y=0)img=random_img()canvas.img_id=canvas.create_image(640,400,image=img)Thread(target=update,args=(canvas,),daemon=True).start()root_wd.mainloop()运行上面这份脚本可以看到画面闪烁的现象。
双缓冲技术
上面闪烁的本质原因是Canvas的帧数据提前释放了。双缓冲技术始终保持了上一帧和当前帧的引用,避免提前释放,直接上代码看效果:
# coding=utf-8fromtkinterimportTk,CanvasfromPILimportImage,ImageTkfromthreadingimportThreadimportnumpyasnpfromtimeimportsleep buf1,buf2,curr=None,None,Nonedefrandom_img():returnImageTk.PhotoImage(Image.fromarray(np.random.randint(0,255,(480,640),dtype=np.uint8)))defupdate(canvas:Canvas,canvas2:Canvas,buf1:ImageTk,buf2:ImageTk,curr:ImageTk):whileTrue:ifcurrisbuf1:buf2=random_img()else:buf1=random_img()curr=buf1ifcurrisbuf2elsebuf2 img=random_img()canvas1.itemconfig(canvas1.img_id,image=img)canvas2.itemconfig(canvas2.img_id,image=curr)sleep(.05)if__name__=='__main__':root_wd=Tk()root_wd.geometry("1280x480")root_wd.title("Dynamic change canvas images")root_wd.minsize(640,480)canvas1=Canvas(root_wd,width=640,height=480)canvas1.place(x=0,y=0)canvas2=Canvas(root_wd,width=640,height=480)canvas2.place(x=640,y=0)curr=buf1=random_img()buf2=random_img()canvas1.img_id=canvas1.create_image(320,240,image=curr)canvas2.img_id=canvas2.create_image(320,240,image=curr)Thread(target=update,args=(canvas1,canvas2,buf1,buf2,curr),daemon=True).start()root_wd.mainloop()可以看到右边用了双缓冲技术的Canvas不闪烁