跳转至

§5.3 图片进阶#

约 823 个字 39 行代码 预计阅读时间 8 分钟

这一小节不讲别的,就讲进度条和 gif 解析的联动。

注意

此小节会涉及到一些与多线程相关的知识。

一、获取 gif 图片的总帧数#

要想知道 gif 解析了多少,必须得先知道 gif 的总帧数。你可以通过其他的软件来获取 gif 图的总帧数,你也可也通过下面的代码来获取 gif 的总帧数。

PhotoImageget_total_frames 可以获取 gif 的总帧数(并非 FPS):

image = tkt.PhotoImage('example.gif')

print(image.get_total_frames())

提示

当 gif 的总帧数比较大的时候,上面代码还是比较慢的,此时如果你有 PIL 库,这一过程将会加快几倍。当然,如果你没有 PIL 库,上面的代码依然可以获得 gif 的总帧数,只不过慢一些罢了。

通常我们并不会在正式程序中直接使用上述方法来获取 gif 的总帧数,我们应该在正式程序之前,就通过测试得到 gif 的帧数,然后作为常量写在程序中。因为获取 gif 总帧数并不是一个很快的过程,如果直接在正式程序中调用,卡顿的现象将十分明显。

二、以进度条展示加载进度#

2.1 运行前解析的进度条展示#

由于 gif 的解析是一个阻塞过程,所以这里我们要采用多线程来避免程序的严重卡顿。

import threading

import tkintertools as tkt

root = tkt.Tk('动图解析和进度条的关联', 1280, 720)
canvas = tkt.Canvas(root, 1280, 720, 0, 0)
canvas_item_id = canvas.create_image(640, 360)
pb = tkt.ProgressBar(canvas, 310, 580, 660, 30)

image = tkt.PhotoImage('example.gif')


def load_image() -> None:
    """解析动图"""
    for i in image.parse():
        pb.load(i/39)
    image.start(canvas, canvas_item_id, interval=30)


threading.Thread(target=load_image, daemon=True).start()
root.mainloop()

运行的效果是这样的(示例所用 gif 图片来自互联网,侵权删),如果你发现下面的图片很卡,放心,不是 tkintertools 的问题,而是你网页加载比较慢,耐心等待就好:

gif

当我们有大量的资源需要加载的时候,就可以将它们组合一下,一起加载,然后用进度条的方式展现啦!

2.2 运行时解析的进度条展示#

尽管这种需求可能很少见,但在某些情况下,我们可能还是需要运行时解析的进度条展示。由于运行时解析的过程是封装在 start 方法中的,我们无法在外部通过像上述代码那样的方法来展示其进度。此时,我们就需要用到 start 方法的一个特殊的参数 callback 了,它可以在运行时解析过程中添加一个回调函数。

有了这个方法,我们就可以轻松展示运行时解析的进度了:

import tkintertools as tkt

root = tkt.Tk('动图解析和进度条的关联', 1280, 720)
canvas = tkt.Canvas(root, 1280, 720, 0, 0)
canvas_item_id = canvas.create_image(640, 360)
pb = tkt.ProgressBar(canvas, 310, 580, 660, 30)

image = tkt.PhotoImage(r"C:\Users\Yzk\OneDrive\Temp\Photo\ToolPaper\动1.gif")

frames = image.get_total_frames()  # 这一步可能会略微卡顿

image.start(canvas, canvas_item_id, interval=30,
            callback=lambda x: pb.load(x/frames))

root.mainloop()

gif

看上面的效果图,你可能看不出来解析时和没有解析时有什么区别,但其实解析时的延迟为 1 毫秒(为了减少卡顿感和加速解析),而正式播放时为 30 毫秒,几乎没有区别的原因是解析占用了一部分时间。一般可能在测试中会使用这种方式。

猜你想问:为什么第二段代码不同于第一段代码,没有使用多线程模块呢?

第二段代码的解析过程被嵌入在 UI 线程中了,所以没有使用多线程模块了。这样虽然不需要使用多线程模块,但会在解析过程中略微降低 UI 线程的响应速度。

欢迎在评论区留下你的想法


下面没有更多评论啦😆