之前四篇文章論證了利用二維碼傳輸文件的可行性,本章使用tkinter開發【動態二維碼文件發送端】,發送端具備文件選擇、開始發送文件、停止發送文件以及顯示發送狀態的功能,程序界面下:
這裡下載源碼運行↓↓↓
使用tkinter開發動態二維碼文件發送端-圖像識別文檔類資源-CSDN下載之前四篇文章論證了利用二維碼傳輸文件的可行性,本章使用python的tkinter庫開發【動態二維碼更多下載資源、學習資料請訪問CSDN下載頻道.https://download.csdn.net/download/qq616491978/86268732 經過簡單分析,要開發該程序,需要完成以下五個步驟,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)
第三篇文章中的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)可以控制動態二維碼的開始和暫停,由於目前沒發現python中thread類的停止方法,所以使用循環判斷全局變量標志_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()即可。