【Python-利用动态二维码传输文件(三)】动态二维码展示界面制作,涉及PIL库、threading库、os库、time库和tkinter库的Tk、Label组件

        

程序示意图

 前言

        为实现二维码传输文件,首先要在电脑屏幕上动态展示已经之前已拆分好的二维码,然后使用摄像头对着电脑屏幕按帧读取,最后还原出原来的文件。

        本章使用Python自带的GUI库tkinter实现在电脑屏幕上动态展示已经之前已拆分好的二维码。


目录

一、制作基于tkinter的静态图片显示窗口

二、读取已拆分的二维码,使用tkinter窗口循环显示

三、运行效果和完整代码


一、制作基于tkinter的静态图片显示窗口

        首先是使用tkinter库的Tk组件,创建tkinter窗口对象root,并对窗口对象root的标题进行设置。代码如下:

from tkinter import Tk
# 创建窗口
root = Tk()
# 设置窗口的标题
root.title("利用二维码发送文件工具")

        然后使用pillow(PIL)Image组件,读取需要显示的图片。代码如下:

from PIL import Image

# 创建图片组件
_pil_image = Image.open('cache/split_file_000.png')

        最后是把图片显示在窗口中,由于事先不知道图片长宽,同时窗口可显示大小有限,太大的图片只能一部分,所以这里需要对图片进行缩放,以适应窗口大小。缩放图片的函数代码如下,函数会返回一个缩放好的图像对象。

# 缩放图片的函数,w是原来图片的宽,h是原来图片的高,w_box是想缩放的宽,h_box是想缩放的高
# pil_image是原图片对象,函数会返回一个缩放好的图像对象
def resize(w, h, w_box, h_box, pil_image):
    f1 = 1.0 * w_box / w
    f2 = 1.0 * h_box / h
    factor = min([f1, f2])
    width = int(w * factor)
    height = int(h * factor)
    return pil_image.resize((width, height), Image.Resampling.LANCZOS)

        有了缩放函数后,就可把任意大小的图片显示在tkinter窗口了。这里使用tkinterLabel组件加载图片,Label组件通过.grid(x,y)网格布局模式加入窗口对象root中。

        需要注意的是PILImage对象不能直接显示在tkinter窗口上,需要先把Image对象转换成ImageTk对象,完整的代码如下:

from tkinter import Tk
from tkinter import Label
from PIL import Image, ImageTk


def open_window():
    # 创建窗口
    root = Tk()
    # 设置窗口的标题
    root.title("利用二维码发送文件工具")
    # 创建图片组件
    _pil_image = Image.open('cache/split_file_000.png')
    # 缩放图片
    _w, _h = _pil_image.size
    _pil_image_resized = resize(_w, _h, 300, 300, _pil_image)
    _tk_image = ImageTk.PhotoImage(_pil_image_resized)
    # 创建图片组件,放置在root中
    _label_dt = Label(root, image=_tk_image, width=300, height=300)
    _label_dt.grid(row=0, column=0)
    root.mainloop()


# 缩放图片的函数,w是原来图片的宽,h是原来图片的高,w_box是想缩放的宽,h_box是想缩放的高
# pil_image是原图片对象,函数会返回一个缩放好的图像对象
def resize(w, h, w_box, h_box, pil_image):
    f1 = 1.0 * w_box / w
    f2 = 1.0 * h_box / h
    factor = min([f1, f2])
    width = int(w * factor)
    height = int(h * factor)
    return pil_image.resize((width, height), Image.Resampling.LANCZOS)

运行效果如下:

二、读取已拆分的二维码,使用tkinter窗口循环显示

        刚才已完成静态二维码图片的显示,现需要将拆分的二维码动态进行显示,这里读取上一篇文章拆分好的29张图片,实现按文件名顺序,每隔0.2S显示一张不同的二维码图片。

        首先,编制更新tkinter窗口中Label组件_label_dt图像的函数,若要更新_label_dt中的图片只需使用_label_dt.configure(image=_tk_image)即可,其中_tk_image通过使用按顺序的文件名读取新的二维码图片,_pil_image = Image.open("cache/split_file_00" + str(i) + ".png"),这里通过str(i)循环文件名,并通过函数传入的_time_interval参数控制循环时的时间间隔,同时_label_dt要换成global全局变量,以便分线程可以操作主线程创建的_label_dt组件主要代码如下:

# 循环更新tkinter窗口中,二维码图片的函数,_time_interval为更新频率,
def update_qr(_time_interval):
    for i in range(0, len(os.listdir("cache"))):
        time.sleep(_time_interval)
        # 读取二维码图片
        _image_filepath = "cache/split_file_00" + str(i) + ".png"
        _pil_image = Image.open(_image_filepath)
        print(_image_filepath)
        # 缩放图片
        _w, _h = _pil_image.size
        _pil_image_resized = resize(_w, _h, 300, 300, _pil_image)
        _tk_image = ImageTk.PhotoImage(_pil_image_resized)
        global _label_dt
        _label_dt.configure(image=_tk_image)
        _label_dt.image = _tk_image

        有了二维码图片的更新函数后,还有一个问题需要解决,就是更新操作不能阻塞tkinter的窗口主线程,否则当更新图片的时候窗口会被阻塞不能同时进行其他操作,因此这里需要为更新二维码图片update_qr()函数启动单独的线程。

        开启线程需要用到Python的threading包,这里需要特别注意的是当.Thread()中的args=(,)只有一个参数时必须要有,逗号结尾,因为它接受的是列表类型的参数,主要代码如下:

# 用于更新图片的线程
def update_qr_thread(_time_interval):
    # 设置更新频率为_time_interval秒
    # 设置线程参数,target为线程启动函数,name为线程名,args为传递给线程函数的参数
    _t = threading.Thread(target=update_qr, name='update_qr', args=(_time_interval,))
    # 启动线程
    _t.start()

        所有准备工作都完成了,现在只需要在创建窗口时调用线程便可,这里使用root.after(3,update_qr_thread(0.2))函数,在创建窗口后3秒才启动线程以免一些窗口初始化工作未完成就启动线程,造成不必要错误。

# 窗口显示后3秒执行update_qr_thread()函数
root.after(3, update_qr_thread(0.2))
# 显示窗口
root.mainloop()

三、运行效果和完整代码

        全部代码码完,运行看到以下效果:

bb7cfb9de15a42c782ff6f2f58301782.gif

 完整代码如下:

import os
import time
from tkinter import Tk
from tkinter import Label
from PIL import Image, ImageTk
import threading

# 图片组件
_label_dt = None


def open_window():
    # 创建窗口
    root = Tk()
    # 设置窗口的标题
    root.title("利用二维码发送文件工具")
    # 创建图片组件
    _pil_image = Image.open('cache/split_file_000.png')
    # 缩放图片
    _w, _h = _pil_image.size
    _pil_image_resized = resize(_w, _h, 300, 300, _pil_image)
    _tk_image = ImageTk.PhotoImage(_pil_image_resized)
    # 创建图片组件,放置在root中
    global _label_dt
    _label_dt = Label(root, image=_tk_image, width=300, height=300)
    _label_dt.grid(row=0, column=0)
    # 窗口显示后3秒执行update_qr_thread()函数
    root.after(3, update_qr_thread(0.2))
    # 显示窗口
    root.mainloop()


# 缩放图片的函数,w是原来图片的宽,h是原来图片的高,w_box是想缩放的宽,h_box是想缩放的高
# pil_image是原图片对象,函数会返回一个缩放好的图像对象
def resize(w, h, w_box, h_box, pil_image):
    f1 = 1.0 * w_box / w
    f2 = 1.0 * h_box / h
    factor = min([f1, f2])
    width = int(w * factor)
    height = int(h * factor)
    return pil_image.resize((width, height), Image.Resampling.LANCZOS)


# 用于更新图片的线程
def update_qr_thread(_time_interval):
    # 设置更新频率为_time_interval秒
    # 设置线程参数,target为线程启动函数,name为线程名,args为传递给线程函数的参数
    _t = threading.Thread(target=update_qr, name='update_qr', args=(_time_interval,))
    # 启动线程
    _t.start()


# 循环更新tkinter窗口中,二维码图片的函数,_time_interval为更新频率,
def update_qr(_time_interval):
    for i in range(0, len(os.listdir("cache"))):
        time.sleep(_time_interval)
        # 读取二维码图片
        _image_filepath = "cache/split_file_00" + str(i) + ".png"
        _pil_image = Image.open(_image_filepath)
        print(_image_filepath)
        # 缩放图片
        _w, _h = _pil_image.size
        _pil_image_resized = resize(_w, _h, 300, 300, _pil_image)
        _tk_image = ImageTk.PhotoImage(_pil_image_resized)
        global _label_dt
        _label_dt.configure(image=_tk_image)
        _label_dt.image = _tk_image


if __name__ == '__main__':
    open_window()

猜你喜欢

转载自blog.csdn.net/qq616491978/article/details/125574114