【Python-利用动态二维码传输文件(五)】动态二维码文件发送端开发,使用Tkinter filedialog实现任意格式文件选中,并显示发送状态

        之前四篇文章论证了利用二维码传输文件的可行性,本章使用tkinter开发【动态二维码文件发送端】,发送端具备文件选择、开始发送文件、停止发送文件以及显示发送状态的功能,程序界面下:

程序运行界面

这里下载源码运行↓↓↓       

使用tkinter开发动态二维码文件发送端-图像识别文档类资源-CSDN下载之前四篇文章论证了利用二维码传输文件的可行性,本章使用python的tkinter库开发【动态二维码更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/qq616491978/86268732    


目录

一、程序的界面设计

二、tkinter选择文件功能开发

三、文件拆分成二维码功能开发

四、发送和停止线程开发

五、发送状态更新功能开发

六、运行效果和完整代码


        经过简单分析,要开发该程序,需要完成以下五个步骤,1)程序的界面设计;2)选择文件功能开发;3)文件拆分成二维码功能开发;4)发送和停止线程开发;5)发送状态更新功能开发。接一下一个一个步骤进行研究。

一、程序的界面设计

        程序界面内容不多,只需用到图片显示组件、文本组件、按钮组件,然后使用tkinter的网格布局.grid(row=,column=,......)把组件放置在对应的位置即可。由于文件路径、图片、状态会随着用户的操作发生变化,这里把文件路径变量_label_filepath、图片对象变量_label_dt、进度和状态变量_label_progress_text_label_state_text设置为程序的全局变量,方便后续开启线程调用全局global变量,更新上述组件。

        主要代码如下:

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

# 显示图片组件
_label_dt = None
# 显示文件路径组件,_label_filepath_text为文件路径
_label_filepath_text = None
# 显示发送状态组件,_label_progress_text为进度,_label_state_text为状态
_label_progress_text = None
_label_state_text = None

def open_window():
    # 创建窗口
    root = Tk()
    # 设置窗口的标题
    root.title("动态二维码文件发送端")
    # 创建图片组件,None.png为未启动发送时的默认图片
    _pil_image = Image.open('None.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, columnspan=4, column=0)
    _button = Button(root, text="选择待发送文件......", command=open_file)
    _button.grid(row=1, column=0,columnspan=2)
    # 创建label可变文本,用于动态更新选中的文件路径
    global _label_filepath_text
    _label_filepath_text = StringVar()
    _label_filepath = Label(root, textvariable=_label_filepath_text)
    _label_filepath.grid(row=1,column=2, columnspan=2)
    # 创新开始、停止按钮
    _button_start = Button(root, text="开始发送", command=start_send_file)
    _button_stop = Button(root, text="停止发送", command=stop_send_file)
    _button_start.grid(row=2, column=0)
    _button_stop.grid(row=2, column=1)
    # 创建label可变文本,用于动态更新发送文件状态
    global _label_progress_text
    _label_progress_text = StringVar()
    _label_progress_text.set("[0/0]")
    _label_progress = Label(root, textvariable=_label_progress_text)
    _label_progress.grid(row=2, column=2)
    # 创建label可变文本,用于动态更新发送文件状态
    global _label_state_text
    _label_state_text = StringVar()
    _label_state_text.set("待发送")
    _label_state = Label(root, textvariable=_label_state_text)
    _label_state.grid(row=2, column=3)
    # 显示窗口
    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选择文件功能开发

        第三篇文章中的python程序是通过固定地址读取默认图片文件的。本章为了增加程序的灵活性,实现用户自行选择待发送文件功能。

        经过百度搜索和学习,发现tkinter中的filedialog可实现文件选取功能。下面编写open_file函数实现该功能,open_file函数通过filedialog.askopenfilename()获取用户选择的文件路径,然后使用_label_filepath_text.set()方法,把用户选中文件路径更新到界面中。

        主要代码如下:

from tkinter import filedialog

# 选择待发送的文件
def open_file():
    _file_path = filedialog.askopenfilename(title=u'选择文件', initialdir=(os.path.expanduser('C:/')))
    if _file_path is not None:
        # 设置并显示选中的文件路径
        global _label_filepath_text
        _label_filepath_text.set(_file_path)
        print(get_file_size(_file_path))
    return _file_path

        显示效果如下:

选择待发送文件
显示选中的文件全路径

三、文件拆分成二维码功能开发

        文件拆分成二维码,在之前几篇文章中都有介绍,这里就不多说了。本章主要对拆分代码进行优化:

        1)拆解前先对文件进行压缩(使用了zlib库进行压缩),减少拆分发送二维码的数量;

        2) 利用_label_state_text.set()把拆分的状态实时显示在界面上。

        主要代码如下:

import os
import datetime
import zlib
import base64


# 按固定长度拆分_base64格式的字节流,_file_path为文件路径,_step_length为拆分的固定长度(步长)
def split_file(_file_path, _step_length):
    global _label_state_text
    _start_time = datetime.datetime.now()
    # 使用二进制模式读取以_file_path为路径的文件
    _file = open(_file_path, mode="rb")
    # 一次性读取所有字节(若文件太大请分段读取避免爆内存)
    _label_state_text.set("读取文件")
    _contents = _file.read()
    _label_state_text.set("压缩文件")
    _contents_zip = zlib.compress(_contents, zlib.Z_BEST_COMPRESSION)
    # 转换为_base64格式方面传输数据
    _label_state_text.set("编码")
    _contents_base64 = base64.b64encode(_contents_zip)
    # 调用拆分函数
    _base64_str_list = []
    # 字节流长度
    _length = len(_contents_base64)
    # //整除(只保留商的整数部分)
    _frequency = _length // _step_length
    _j = 0
    _label_state_text.set("拆分文件信息")
    for i in range(0, _length, _step_length):
        _temp_str = _contents_base64[i:i + _step_length]
        _temp_str_utf8 = '[' + str(_j) + '/' + str(_frequency) + ']' + _temp_str.decode('utf-8')
        _base64_str_list.append(_temp_str_utf8)
        _j = _j + 1
    _end_time = datetime.datetime.now()
    print("拆分块数:", _frequency)
    print("拆分文件用时(秒):", (_end_time - _start_time).seconds)
    return _base64_str_list

四、发送和停止线程开发

        文件拆分好后就可以动态生成二维码了,动态显示二维码在第三篇文章中已经探讨过,本章主要对代码进行优化。

        1)打印的二维码不用先拼接成链表,可边拆解边显示二维码;

        2)可以控制动态二维码的开始和暂停,由于目前没发现pythonthread类的停止方法,所以使用循环判断全局变量标志_thread_flag的方法控制线程结束。

        主要代码如下:

_thread_flag = None
_thread = None

# 根据拆分后的字节流列表动态显示二维码,_base64_str_list为拆分后的字节列表,_time_interval为显示间隔
def print_qr(_base64_str_list, _time_interval):
    global _thread_flag
    # 循环生成二维码图片
    _qr = qrcode.QRCode()
    _pil_image = None
    _pil_image_resized = None
    _tk_image = None
    _list_len = len(_base64_str_list)
    global _label_state_text
    _label_state_text.set("文件发送中")
    while _thread_flag is True:
        for i in range(0, _list_len):
            # 判断_thread_flag的值,当为False时中断执行
            if _thread_flag is True:
                # 1)清空原_qr对象数据;2)把列表中的数据加入_qr对象中
                _qr.clear()
                _qr.add_data(_base64_str_list[i], 0)
                # 1)根据列表中的数据制作二维码图片;2)缩放图片;3)显示在界面上
                _pil_image = _qr.make_image()
                _w, _h = _pil_image.size
                _pil_image_resized = resize(_w, _h, 300, 300, _pil_image)
                _tk_image = ImageTk.PhotoImage(_pil_image_resized)
                # 更新二维码显示组件_label_dt
                global _label_dt
                _label_dt.configure(image=_tk_image)
                _label_dt.image = _tk_image
                global _label_progress_text
                _label_progress_text.set("[%d/%d]" % (i, _list_len))
                # 设置二维码刷新时间间隔
                time.sleep(_time_interval)
            else:
                break


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


# 点击,发送文件
def start_send_file():
    global _label_filepath_text, _thread_flag, _thread, _label_state_text
    if _label_filepath_text is not None:
        if (_thread is None) or (_thread.is_alive() is False):
            _f_size = get_file_size(_label_filepath_text.get())
            if 0 < _f_size < 2:
                # 拆分选中文件
                _temp_list = split_file(_label_filepath_text.get(), 512)
                # 调用二维码生成函数
                _thread_flag = True
                update_qr_thread(_temp_list, 0)
            else:
                _label_state_text.set("文件应小于2MB")
        else:
            _label_state_text.set("有发送任务正在进行")


# 点击,停止发送文件
def stop_send_file():
    global _thread_flag
    _thread_flag = False

五、发送状态更新功能开发

        界面状态更新相对简单,图片使用_label_dt.configure(image=_tk_image)更新,文字使用StringVar类调用_label_state_text.set()即可。

六、运行效果和完整代码

       

猜你喜欢

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