Skip to content

Commit

Permalink
add feat: save log to file
Browse files Browse the repository at this point in the history
  • Loading branch information
zfb132 committed Jan 1, 2023
1 parent 9313b28 commit a83046a
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 24 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
__pycache__/
*.py[cod]
*$py.class
[Ll]og/

# C extensions
*.so
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# QrScan
二维码图片批量检测识别软件
支持常见的图片文件:`jpg``jpeg``png``bmp``tif``tiff``pbm``pgm``ppm``ras`等(识别二维码根据的是文件内容,即使扩展名为其他的,只要文件内容是图片编码都可识别)
**Win7及以下系统**可能存在兼容性问题,建议使用Win10系统

软件截图
<img src="docs/result.png" alt="软件截图" align=center />
Expand All @@ -14,11 +15,15 @@
* 对于剪切操作,需要设置保存剪切文件的文件夹,遇到文件重名将会按时间戳重命名
* 对于识别操作,需要设置保存二维码识别结果的文件夹,结果自动保存在该目录的`qrcode.csv`文件
* 支持实时日志显示与进度展示
* 支持文件日志记录,默认保存在程序目录下的`log`文件夹,文件名格式为`年月日时分秒毫秒.txt`
* 对于剪切和删除操作,默认会在日志文件夹中保存二维码识别结果,文件名格式为`年月日时分秒毫秒.csv`
* 支持多进程极速检测识别

## 2. 下载软件
### 2.1. 使用已经编译成功的发布版软件
下载地址:[release](https://github.com/zfb132/QrScan/releases)
建议解压后的程序放置在**不需要管理员权限的目录**下,否则可能会出现无法写入日志文件的问题
若某一个版本出现问题,可以尝试下载其他版本

### 2.2. 从代码编译运行打包软件
根据本机系统平台的不同,选择不同的文件后缀名: `Windows`平台选择`.bat``Linux`平台选择`.sh`
Expand Down
25 changes: 20 additions & 5 deletions batch_work.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import logging

from multiprocessing import cpu_count, Pool
from os.path import join
from os.path import join, dirname, basename
from os import walk
from PyQt5.QtCore import QObject, pyqtSignal

Expand All @@ -26,7 +26,8 @@ def __init__(self, myvar, func):
通过func函数接收具体用于任务处理的函数
'''
self.work = func
self.var = myvar
self.var = myvar[:3]
self.log_file = myvar[3]
super(BatchWork, self).__init__()

def chunks(self, l, n):
Expand Down Expand Up @@ -84,8 +85,12 @@ def show_info(self, img_status, op_status, name):
else:
logging.error(f"{name}程序出现bug!")

def save_qrcode(self, path, qrcode):
name = join(path, "qrcode.csv")
def save_qrcode(self, path, qrcode, is_log=False):
if is_log:
pure_log_name = basename(self.log_file.name).split('.')[:-1]
name = join(path, f"{'.'.join(pure_log_name)}.csv")
else:
name = join(path, "qrcode.csv")
flag = False
with open(name, "a", encoding="utf8") as f:
for single_res in qrcode:
Expand Down Expand Up @@ -153,7 +158,7 @@ def run(self):
# 格式化输出结果
for t in range(len(m)):
name = join(final_pathes[i][t][0], final_pathes[i][t][1])
img_status, op_status, qrcode_thread = m[t]
img_status, op_status, qrcode_thread, note = m[t]
if img_status == 1:
qr_img_num += 1
# 剪切成功、删除成功、添加时间戳后剪切成功
Expand All @@ -162,13 +167,23 @@ def run(self):
qrcode.append(qrcode_thread)
# 显示每个批次的日志
self.show_info(img_status, op_status, name)
# 写入日志文件
if self.log_file:
self.log_file.write(f"{note}\n")
if self.log_file:
self.log_file.flush()
# 更新进度条(进度条的上限初始化为100)
if operation == "decode":
self.save_qrcode(cut_path, qrcode)
else:
if self.log_file:
self.save_qrcode(join(dirname(__file__), "log"), qrcode, self.log_file)
self.notifyProgress.emit((i+1)*100//num_procs)
# logging.info(f"进程{i}结束")
logging.info(f"扫描任务结束:")
logging.info(f"共计检测到{qr_img_num}个包含二维码的图片,其中{op_success_num}个文件执行操作成功")
if self.log_file:
self.log_file.close()
# 使用信号槽机制,启用按钮,因为任务已经处理完成
# 用户可以再次提交任务或创建新任务
self.notifyStatus.emit(False)
15 changes: 14 additions & 1 deletion custom_qwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

from custom_formatter import CustomFormatter
from batch_work import BatchWork
from os import makedirs, path
from datetime import datetime

class QPlainTextEditLogger(QObject, logging.Handler):
'''自定义Qt控件,继承自QObject,初始化时创建QPlainTextEdit控件\n
Expand Down Expand Up @@ -75,6 +77,7 @@ def __init__(self, parent=None):
self.logger.new_record.connect(self.logger.widget.appendHtml)
self._loadThread = None
self.run_func = None
self.log_file = None

# 界面基本元素创建
self.createBottomLeftGroupBox()
Expand Down Expand Up @@ -119,6 +122,15 @@ def get_cut_path(self):
directory = QFileDialog.getExistingDirectory(self, "选取保存结果的文件夹")
self.cutPathTextBox.setText(directory)

def set_log_file(self):
log_name = path.join(path.dirname(__file__), "log", f"{datetime.now().strftime('%Y%m%d%H%M%S')}.txt")
try:
self.log_file = open(log_name, "w", encoding="utf-8")
logging.info(f"日志文件{log_name}创建成功")
except Exception as e:
logging.warning(f"日志文件{log_name}创建失败")
logging.warning(repr(e))

def batch_work(self):
# 运行按钮的响应函数
# 获取操作类型,cut还是delete
Expand Down Expand Up @@ -155,8 +167,9 @@ def batch_work(self):
self.logger.widget.setPlainText("作者:zfb\nhttps://github.com/zfb132/QrScan")
# 创建线程
self._loadThread=QThread(parent=self)
self.set_log_file()
# vars = [1, 2, 3, 4, 5, 6]*6
vars = [img_path, cut_path, operation]
vars = [img_path, cut_path, operation, self.log_file]
self.loadThread=BatchWork(vars, self.run_func)
# 为BatchWork类的两个信号绑定槽函数
self.loadThread.notifyProgress.connect(self.updateProgressBar)
Expand Down
64 changes: 46 additions & 18 deletions pyqt5_qr_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@
print("初始化识别器失败!")
exit(0)

def log_init():
try:
# 当前程序的路径
log_dir = os.path.join(os.path.dirname(__file__), "log")
if not os.path.exists(log_dir):
os.makedirs(log_dir)
logging.info(f"日志文件夹{log_dir}创建成功")
else:
logging.info(f"日志文件夹{log_dir}已存在")
except Exception as e:
logging.warning(f"日志文件{log_dir}创建失败")
logging.warning(repr(e))

def scan(root, name, cut_path, operation):
'''
@param root 图片文件的路径
Expand Down Expand Up @@ -65,28 +78,35 @@ def scan(root, name, cut_path, operation):
op_status = None
# [图片名称, [二维码]]
qrcode = None
# 处理过程中的运行信息
note = None
img_name = os.path.join(root, name)
# imread不支持中文路径
# img = imread(img_name)
npfile = fromfile(img_name, dtype=uint8)
if npfile.size == 0:
logging.error(f"{img_name} 是空文件!")
return [4, op_status, qrcode]
note = f"空白文件: {img_name}"
logging.error(note)
return [4, op_status, qrcode, note]
img = imdecode(npfile, IMREAD_UNCHANGED)
if img is None:
logging.warning(f"{img_name} 不是一个图片!")
return [4, op_status, qrcode]
note = f"不是图片: {img_name}"
logging.warning(note)
return [4, op_status, qrcode, note]
# if img.empty():
if img.size == 0 :
logging.error(f"{img_name} 不是一个合法图片!")
return [3, op_status, qrcode]
note = f"非法图片: {img_name} 不是一个合法图片!"
logging.error(note)
return [3, op_status, qrcode, note]
try:
res, points = detector.detectAndDecode(img)
except Exception as e:
print(repr(e))
return [None, op_status, qrcode]
note = f"未知文件: {img_name} 识别失败!"
return [None, op_status, qrcode, note]
if res == None or len(res) < 1:
logging.info(f"{img_name} 不包含二维码")
note = f"无二维码: {img_name}"
logging.info(note)
img_status = 2
op_status = 7
else:
Expand All @@ -98,41 +118,48 @@ def scan(root, name, cut_path, operation):
try:
if not os.path.exists(new_name):
move(img_name, new_name)
logging.debug(f"{img_name}--->{new_name}")
note = f"直接剪切: {img_name}--->{new_name}"
logging.debug(note)
op_status = 1
else:
file_exist = True
logging.warning(f"文件{img_name}已存在!")
except Exception as e:
logging.error(repr(e))
logging.error(f"剪切文件{img_name}失败!")
note = f"剪切失败: {img_name} 直接剪切失败!"
logging.error(note)
op_status = 2
if file_exist:
time_str = datetime.now().strftime("_%Y%m%d%H%M%S")
time_str = datetime.now().strftime("_%Y%m%d%H%M%S%f")
m = os.path.splitext(new_name)
new_name = m[0] + time_str + m[1]
try:
move(img_name, new_name)
logging.debug(f"{img_name}--->{new_name}")
note = f"重名剪切: {img_name}--->{new_name}"
logging.debug(note)
op_status = 5
except Exception as e:
logging.error(repr(e))
logging.error(f"剪切文件{img_name}失败!")
note = f"剪切失败: {img_name} 重命名后剪切失败!"
logging.error(note)
op_status = 6
elif operation == 'delete':
try:
os.remove(img_name)
logging.debug(f"已删除文件{img_name}")
note = f"删除成功: {img_name}"
logging.debug(note)
op_status = 3
except Exception as e:
logging.error(repr(e))
logging.error(f"删除文件{img_name}失败!")
note = f"删除失败: {img_name}"
logging.error(note)
op_status = 4
elif operation == 'decode':
full_name = os.path.normpath(img_name)
qrcode = [full_name, res]
op_status = 8
return [img_status, op_status, qrcode]
note = f"识别成功: {img_name}"
full_name = os.path.normpath(img_name)
qrcode = [full_name, res]
return [img_status, op_status, qrcode, note]


def scan_process(pathes, cut_path, operation):
Expand All @@ -159,6 +186,7 @@ def scan_process(pathes, cut_path, operation):
sc = screen.availableGeometry().center()
fg.moveCenter(sc)
detectDialog.show()
log_init()
code = app.exec_()
if detectDialog._loadThread:
detectDialog._loadThread.quit()
Expand Down

0 comments on commit a83046a

Please sign in to comment.