上一篇文章完成了动态二维码文件发送端的开发,本篇文章研究动态二维码文件接收端的开发。
左边是发送端,右边是接收端。
之前第四篇文章已验证过可行性,现对原代码进行优化改进,接收端设计思路如下:
(一)通过pyautogui库对电脑屏幕进行高频截图(移动端摄像头拍摄),使用pyzbar库、OpenCV库、numpy库把截图中单张二维码包含的数据识别出来;
(二)使用base64库、zlib库、对识别出来的全部数据进行拼接、解码、解压缩、最后还原出源文件;
(三)使用tkinter的Progressbar、Label,显示文件接收进度,最后显示还原文件的绝对路径。
第四篇文章参考↓↓↓:
【Python-利用二维码传输文件(四)】使用pyautogui录屏(连续截图),并利用OpenCV按帧读取二维码,重组被拆分的文件_清远小阮的博客-CSDN博客上一篇文章实现了使用tkinter显示动态二维码。本篇为了模拟摄像头读取动态二维码信息,使用pyautogui库,对电脑屏幕进行录屏(连续截图),最后利用OpenCV按帧读取二维码,重组被拆分的文件。https://blog.csdn.net/qq616491978/article/details/125744317
(一)程序界面设计
tkinter界面包括进度条、状态来、开始录屏按钮和暂停录屏按钮,使用.grid(row=,column=,)网格布局。开始录屏按钮调用save_qrs()函数,暂停录屏按钮调用stop_progress()函数。设计效果如下图:
程序界面主要代码如下:
def open_window():
# 创建窗口
root = Tk()
# 设置窗口的标题
root.title("动态二维码文件接收端")
# 设置进度条
_progressbar = Progressbar(root, orient='horizontal', length=400, mode='determinate')
_progressbar.grid(row=0, column=0, columnspan=3)
_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_savepath = Label(root, text='文件存放地址')
_label_savepath.grid(row=1, column=0)
_label_filepath_text = StringVar()
_label_filepath = Label(root, textvariable=_label_filepath_text)
_label_filepath.grid(row=1, column=1, columnspan=2)
# 暂停标志,用于停止录制,使用传递引用
_stop_flag = [False]
# 设置开始、停止按钮
_button_start = Button(root, text="开始录屏",
command=lambda: save_qrs(root, _progressbar, _label_progress_text, _stop_flag,
_label_filepath_text))
_button_start.grid(row=2, column=0)
_button_stop = Button(root, text="停止录屏", command=lambda: stop_progress(_stop_flag))
_button_stop.grid(row=2, column=1)
# 显示窗口
root.mainloop()
(二)循环截屏(录屏)和逐张识别二维码
录屏和二维码识别全部在save_qrs()函数实现,这有几个需要注意的点:
1)pyautogui.screenshot()截图返回的是一个pillow图片对象,为提高识别效率,这里先用_numpy_img=numpy.array(_screentshot_img)把pillow图片对象转换为numpy图片对象,进行cvtColor()灰度处理,再用pyzbar进行图片识别。
2)对比第四篇文章,这里使用set()类存放识别出来的二维码信息,进行了优化。_qr_set用于存放每帧识别出来的二维码信息,set数据结构用于存放不重复数据,对存入的数据会自动去重。由于截取可能出现掉帧,掉帧后需要重新开始截图获取丢失的帧,如果用其他结构来存放二维码信息,会导致大量重复帧。
3)使用列表值_stop_flag[0]进行引用传递改变原参数的值,如果不用列表是改变不了原参的值。这样一来,通过设置_stop_flag[0]的True或False,便可在while _stop_flag[0] is False循环语句控制接收的启动和暂停,并且无需使用到全局变量。
4)最后调用regroup()函数完成文件重组
# 使用OpenCV库连续截图,并逐张识别出二维码信息
def save_qrs(_tk, _progressbar, _label_progress_text, _stop_flag,_label_filepath_text):
# _qr_set用于存放每帧识别出来的二维码信息,set数据结构用于存放不重复数据,对存入的数据会自动去重
_qr_set = set()
# 设置进度条默认长度为100
_pb_len = 100
# 当_stop_flag为False,一直循环截图
i = 0
_stop_flag[0] = False
while _stop_flag[0] is False:
# 使用pyautogui.screenshot()对屏幕截图
_screenshot_img = pyautogui.screenshot()
# 把Pillow图片对象转换为numpy图片对象
_numpy_img = numpy.array(_screenshot_img)
# 将图像灰度化提高识别效率
_cvt_image = cv2.cvtColor(_numpy_img, cv2.COLOR_BGR2GRAY)
# 解析二维码中的数据
_qr_data = pyzbar.decode(_cvt_image)
# 统计接收进度,_qr_set_len为目前接收的不重复的二维码信息数
# _data_len为单张二维码图片识别后数据的长度用于判断二维码是否识别成功
_qr_set_len = len(_qr_set)
_data_len = len(_qr_data)
# 更新界面状态栏状态,如:正在接收,[11/51]
_label_progress_text.set("正在接收,[%d/%d]" % (_qr_set_len, _pb_len))
# 判断二维码是否识别成功,若识别成功,识别数据的长度_data_len应大于0
if _data_len > 0:
_str = _qr_data[0].data.decode('utf-8')
# 把识别出来的单张二维码图片信息存入_qr_set中
_qr_set.add(_str)
if _qr_set_len == 0:
# 用于更新进度条长度_pb_len,只在接收第一次数据时更新长度提高效率
# 接收数据的格式如:[1/51]xsdgsiakghalshg,获取51存入_pd_len
_str1 = _str.split(']')
_str2 = _str1[0].split('/')
_pb_len = int(_str2[1])
_progressbar["maximum"] = _pb_len
# 更新进度条进度
_progressbar["value"] = _qr_set_len
if _qr_set_len == _pb_len:
# 全部数据接收完毕跳出循环
break
_tk.update()
i = i + 1
if _stop_flag[0] is False:
# 调用合并函数
regroup(_qr_set,_label_filepath_text)
_label_progress_text.set("已完成接收")
else:
_label_progress_text.set("已暂停")
(三)重组文件和文件另存至默认路径
当上述while循环正常结束,证明文件已经全部接收完毕,然后把_qr_set和_label_filepath_text传入regroup(_qr_set,_label_filepath_text)函数进行文件重组。这里有几点需要注意的:
1)对set进行排序。set虽然是不重复的但是乱序的,需要对里面的数据进行重新排序。排序用到了.sort(key=lambda x:comp(x))函数,利用comp(x)的结果进行排序,而comp(x)函数则是提取二维码信息中的序号进行排序,例如[16/51]xxxxx,[5/51]xxxx,提取16和15。
2)提取文件名及文件后缀。排序后提取第一帧,第一帧存放了待发送文件的文件名和文件后缀,可用于还原文件。如下图:
3)解码、解压缩。第二帧开始到结束就是传输的文件数据了,同时由于发送的时候对数据进行了base64编码和zlib压缩,所以还原后需要解码、解压缩才能得到正确的结果,如下图:
4)保存文件。最后使用open()以二进制形式写入文件,并使用os.path.abspath(_file_path)获取文件存放的绝对路径显示在程序界面上,如下图:
5)编写暂停函数。编写stop_progress(_stop_flag)函数,使用引用传递改变_stop_flag的值为True,实现暂停录屏。
主要代码如下:
# 暂停录屏
def stop_progress(_stop_flag):
_stop_flag[0] = True
# 列表排序函数
def comp(_str):
# 从[1/28]分离出1,用于重组排序
_str1 = _str.split(']')
_str2 = _str1[0].split('/')
_str3 = _str2[0].split('[')
# 返回整型序号用于list.sort(key=)排序
return int(_str3[1])
# 还原并另存为对应格式文件
def regroup(_qr_set,_label_filepath_text):
_list = list(_qr_set)
# 根据lambda公式对列表进行排序
_list.sort(key=lambda x: comp(x))
_regroup_str = ""
# 提取第一帧,第一帧存放的是发送文件的文件名和文件后缀,如:[1/51]temp.xlsx
_file_path = _list[0].split(']')[1]
# 提取从第二帧以后的内容
for i in range(1, len(_list)):
_regroup_str = _regroup_str + _list[i].split(']')[1]
print(_list[i])
# 使用base64格式对_uini_str进行解码
print("解码前动态二维码信息 %s" % _regroup_str)
_regroup_str_b64decode = base64.b64decode(_regroup_str)
print("解码后动态二维码信息 %s" % _regroup_str_b64decode)
_regroup_str_b64decode_unzip = zlib.decompress(_regroup_str_b64decode)
print("解压后动态二维码信息 %s" % _regroup_str_b64decode_unzip)
# 使用二进制方式把重组的信息写入文件,并另存为_regroup_pic001.png
with open(_file_path, 'wb') as f:
f.write(_regroup_str_b64decode_unzip)
_label_filepath_text.set(os.path.abspath(_file_path))
f.close()
源代码下载地址↓↓↓:
Python-动态二维码文件接收端开发,涉及tkinter进度条Progressbar、Pillow对象转换为numpy对象等-Python文档类资源-CSDN下载之前第四篇文章已经验证过可行性,现在对之前的代码进行优化改进,接收端的设计原理如下:(一)通过pya更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/qq616491978/86339453