docs: 添加涉密文件自检工具实施计划
This commit is contained in:
312
UmiOCR-data/qt_res/qml/TabPages/BatchOCR/BatchOCR.qml
Normal file
312
UmiOCR-data/qt_res/qml/TabPages/BatchOCR/BatchOCR.qml
Normal file
@@ -0,0 +1,312 @@
|
||||
// ==============================================
|
||||
// =============== 功能页:批量OCR ===============
|
||||
// ==============================================
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import ".."
|
||||
import "../../Widgets"
|
||||
import "../../Widgets/ResultLayout"
|
||||
import "../../Widgets/IgnoreArea"
|
||||
|
||||
TabPage {
|
||||
id: tabPage
|
||||
|
||||
// ========================= 【逻辑】 =========================
|
||||
|
||||
property int errorNum: 0 // 异常的任务个数
|
||||
property string msnID: "" // 当前任务ID
|
||||
|
||||
Component.onCompleted: {
|
||||
}
|
||||
|
||||
// 异步加载一批图像路径
|
||||
function addImages(paths) {
|
||||
if(ctrlPanel.state_ !== "stop") return
|
||||
if(paths.length <= 0) return
|
||||
const isRecurrence = configsComp.getValue("mission.recurrence")
|
||||
qmlapp.asynFilesLoader.run(paths,"image",isRecurrence,onAddImages)
|
||||
}
|
||||
// 完毕后,将合法路径加入表格
|
||||
function onAddImages(paths) {
|
||||
for(let i in paths) {
|
||||
filesTableView.add({ path: paths[i], time: "", state: "" })
|
||||
}
|
||||
}
|
||||
|
||||
// 运行OCR
|
||||
function ocrStart() {
|
||||
let msnLength = filesTableView.rowCount
|
||||
if(msnLength <= 0) {
|
||||
ctrlPanel.stopFinished()
|
||||
return
|
||||
}
|
||||
// 刷新表格
|
||||
for(let i = 0; i < msnLength; i++) {
|
||||
filesTableView.set(i, { time: "", state: qsTr("排队") })
|
||||
}
|
||||
// 刷新计数
|
||||
errorNum = 0 // 异常任务个数
|
||||
// 开始运行
|
||||
const paths = filesTableView.getColumnsValue("path")
|
||||
const argd = configsComp.getValueDict()
|
||||
msnID = tabPage.callPy("msnPaths", paths, argd)
|
||||
// 若tabPanel面板的下标没有变化过,则切换到记录页
|
||||
if(tabPanel.indexChangeNum < 2)
|
||||
tabPanel.currentIndex = 1
|
||||
ctrlPanel.runFinished(msnLength)
|
||||
}
|
||||
|
||||
// 停止OCR
|
||||
function ocrStop() {
|
||||
_ocrStop()
|
||||
tabPage.callPy("msnStop")
|
||||
ctrlPanel.stopFinished()
|
||||
}
|
||||
|
||||
function _ocrStop() {
|
||||
msnID = "" // 清除任务ID
|
||||
// 刷新表格,清空未执行的任务的状态
|
||||
let msnLength = filesTableView.rowCount
|
||||
for(let i = 0; i < msnLength; i++) {
|
||||
const row = filesTableView.get(i)
|
||||
if(row.time === "") {
|
||||
filesTableView.setProperty(i, "state", "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭页面
|
||||
function closePage() {
|
||||
if(ctrlPanel.state_ !== "stop") {
|
||||
const argd = { yesText: qsTr("依然关闭") }
|
||||
const callback = (flag)=>{
|
||||
if(flag) {
|
||||
ocrStop()
|
||||
delPage()
|
||||
}
|
||||
}
|
||||
qmlapp.popup.dialog("", qsTr("任务正在进行中。\n要结束任务并关闭页面吗?"), callback, "warning", argd)
|
||||
}
|
||||
else {
|
||||
delPage()
|
||||
}
|
||||
}
|
||||
|
||||
// 预览
|
||||
function msnPreview(path) {
|
||||
const argd = configsComp.getValueDict()
|
||||
tabPage.callPy("msnPreview", path, argd)
|
||||
}
|
||||
|
||||
// ========================= 【python调用qml】 =========================
|
||||
|
||||
// 准备开始一个任务
|
||||
function onOcrReady(path) {
|
||||
// 刷新表格显示
|
||||
filesTableView.setProperty(path, "state", qsTr("处理"))
|
||||
}
|
||||
|
||||
// 获取一个OCR的返回值
|
||||
function onOcrGet(path, res) {
|
||||
const time = res.time.toFixed(2)
|
||||
let state = ""
|
||||
switch(res.code){
|
||||
case 100:
|
||||
state = "√ "+res.score.toFixed(2);break
|
||||
case 101:
|
||||
state = "√ ---- ";break
|
||||
default:
|
||||
state = "× "+res.code
|
||||
errorNum++ // 异常任务数量+1
|
||||
break
|
||||
}
|
||||
// 刷新表格显示
|
||||
filesTableView.set(path, { "time": time, "state": state })
|
||||
// 提取文字,添加到结果表格
|
||||
res.title = res.fileName
|
||||
resultsTableView.addOcrResult(res)
|
||||
ctrlPanel.msnStep(1) // 任务计数器步进
|
||||
}
|
||||
|
||||
// 任务队列完毕
|
||||
function onOcrEnd(msg, thisMsnID) {
|
||||
// msg: [Success] [Warning] [Error]
|
||||
if(msnID !== thisMsnID) { // 返回的任务ID不等于前端展示的任务ID,则不处理
|
||||
return
|
||||
}
|
||||
_ocrStop()
|
||||
// 任务成功
|
||||
if(msg.startsWith("[Success]")) {
|
||||
let errMsg = ""
|
||||
if(errorNum > 0) { // 有异常任务
|
||||
errMsg = qsTr("%1 张图片识别失败!").arg(errorNum)
|
||||
}
|
||||
const simpleType = configsComp.getValue("other.simpleNotificationType")
|
||||
qmlapp.popup.simple(qsTr("批量识别完成"), errMsg, simpleType)
|
||||
// 任务完成后续操作
|
||||
qmlapp.globalConfigs.utilsDicts.postTaskHardwareCtrl(
|
||||
configsComp.getValue("postTaskActions.system")
|
||||
)
|
||||
}
|
||||
// 任务失败
|
||||
else if(msg.startsWith("[Error]")) {
|
||||
qmlapp.popup.message(qsTr("批量识别任务异常"), msg, "error")
|
||||
}
|
||||
ctrlPanel.stopFinished()
|
||||
}
|
||||
|
||||
// 预览
|
||||
function onPreview(path, res) {
|
||||
ignoreArea.getPreview(res, path, "")
|
||||
}
|
||||
// 路径转文件名
|
||||
function path2name(path) {
|
||||
const parts = path.split("/")
|
||||
return parts[parts.length - 1]
|
||||
}
|
||||
// 文件表格中单击路径
|
||||
function onClickPath(index) {
|
||||
let info = filesTableView.get(index)
|
||||
let fileName = path2name(info.path)
|
||||
let res = resultsTableView.getResult(fileName)
|
||||
let data = undefined, text = undefined
|
||||
if(res) {
|
||||
if(res.source)
|
||||
data = JSON.parse(res.source)
|
||||
if(res.resText)
|
||||
text = res.resText
|
||||
}
|
||||
previewImage.show(info.path, data, text)
|
||||
}
|
||||
|
||||
// ========================= 【布局】 =========================
|
||||
|
||||
// 配置
|
||||
configsComp: BatchOCRConfigs {
|
||||
// 点按钮打开忽略区域
|
||||
onClickIgnoreArea: {
|
||||
if(filesTableView.rowCount > 0) {
|
||||
const path = filesTableView.get(0).path
|
||||
console.log("打开路径", path)
|
||||
ignoreArea.showPath(path)
|
||||
}
|
||||
else {
|
||||
ignoreArea.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
// 主区域:可切换双栏面板
|
||||
DoubleSwitchableLayout {
|
||||
id: doubleLayout
|
||||
saveKey: "BatchOCR_1"
|
||||
anchors.fill: parent
|
||||
|
||||
// 面板A:控制板+文件表格
|
||||
itemA: Panel {
|
||||
anchors.fill: parent
|
||||
|
||||
// 上方控制板
|
||||
MissionCtrlPanel {
|
||||
id: ctrlPanel
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: size_.spacing
|
||||
height: size_.line * 2
|
||||
|
||||
onRunClicked: tabPage.ocrStart()
|
||||
onPauseClicked: {
|
||||
tabPage.callPy("msnPause")
|
||||
pauseFinished()
|
||||
}
|
||||
onResumeClicked: {
|
||||
tabPage.callPy("msnResume")
|
||||
resumeFinished()
|
||||
}
|
||||
onStopClicked: tabPage.ocrStop()
|
||||
}
|
||||
|
||||
// 下方文件表格
|
||||
FilesTableView {
|
||||
id: filesTableView
|
||||
anchors.top: ctrlPanel.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: size_.spacing
|
||||
anchors.topMargin: size_.smallSpacing
|
||||
headers: [
|
||||
{key: "path", title: qsTr("图片"), left: true, display:path2name,
|
||||
btn: true, onClicked:onClickPath},
|
||||
{key: "time", title: qsTr("耗时"), },
|
||||
{key: "state", title: qsTr("状态"), },
|
||||
]
|
||||
openBtnText: qsTr("打开图片")
|
||||
clearBtnText: qsTr("清空")
|
||||
defaultTips: qsTr("拖入图片或文件夹")
|
||||
fileDialogTitle: qsTr("请选择图片")
|
||||
fileDialogNameFilters: [qsTr("图片")+" (*.jpg *.jpe *.jpeg *.jfif *.png *.webp *.bmp *.tif *.tiff)"]
|
||||
isLock: ctrlPanel.state_ !== "stop"
|
||||
onAddPaths: {
|
||||
tabPage.addImages(paths)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 面板B:文字输出 & 设置
|
||||
itemB: Panel {
|
||||
anchors.fill: parent
|
||||
|
||||
// 配置项控制板
|
||||
TabPanel {
|
||||
id: tabPanel
|
||||
anchors.fill: parent
|
||||
anchors.margins: size_.spacing
|
||||
isMenuTop: doubleLayout.isRow // 左右布局时,菜单在顶部;上下布局时菜单在底部
|
||||
menuHeight: size_.line * 2
|
||||
|
||||
// 结果面板
|
||||
ResultsTableView {
|
||||
id: resultsTableView
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
}
|
||||
|
||||
tabsModel: [
|
||||
{
|
||||
"key": "configs",
|
||||
"title": qsTr("设置"),
|
||||
"component": configsComp.panelComponent,
|
||||
},
|
||||
{
|
||||
"key": "ocrResult",
|
||||
"title": qsTr("记录"),
|
||||
"component": resultsTableView,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标拖入图片
|
||||
DropArea_ {
|
||||
anchors.fill: parent
|
||||
callback: tabPage.addImages
|
||||
}
|
||||
|
||||
// 预览面板
|
||||
PreviewImage {
|
||||
id: previewImage
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
// 忽略区域编辑器
|
||||
IgnoreArea {
|
||||
id: ignoreArea
|
||||
anchors.fill: parent
|
||||
pathPreview: msnPreview
|
||||
configsComp: tabPage.configsComp
|
||||
configKey: "tbpu.ignoreArea"
|
||||
}
|
||||
}
|
||||
136
UmiOCR-data/qt_res/qml/TabPages/BatchOCR/BatchOCRConfigs.qml
Normal file
136
UmiOCR-data/qt_res/qml/TabPages/BatchOCR/BatchOCRConfigs.qml
Normal file
@@ -0,0 +1,136 @@
|
||||
// ==============================================
|
||||
// =============== 批量OCR的配置项 ===============
|
||||
// ==============================================
|
||||
|
||||
import QtQuick 2.15
|
||||
import "../../Configs"
|
||||
|
||||
Configs {
|
||||
category_: "BatchOCR"
|
||||
signal clickIgnoreArea() // 打开忽略区域
|
||||
|
||||
configDict: {
|
||||
// OCR参数
|
||||
"ocr": qmlapp.globalConfigs.ocrManager.deploy(this, "ocr"),
|
||||
|
||||
// 后处理
|
||||
"tbpu": {
|
||||
"title": qsTr("OCR文本后处理"),
|
||||
"type": "group",
|
||||
|
||||
"parser": qmlapp.globalConfigs.utilsDicts.getTbpuParser(),
|
||||
"btns": {
|
||||
"title": qsTr("忽略区域"),
|
||||
"btnsList": [
|
||||
{"text":qsTr("进入设置"), "onClicked": clickIgnoreArea},
|
||||
],
|
||||
},
|
||||
"ignoreArea": {
|
||||
"type": "var",
|
||||
"save": false,
|
||||
},
|
||||
},
|
||||
|
||||
// 任务参数
|
||||
"mission": {
|
||||
"title": qsTr("批量任务"),
|
||||
"type": "group",
|
||||
|
||||
"recurrence": {
|
||||
"title": qsTr("递归读取子文件夹"),
|
||||
"toolTip": qsTr("导入文件夹时,导入子文件夹中全部图片"),
|
||||
"default": false,
|
||||
},
|
||||
"dirType": {
|
||||
"title": qsTr("保存到"),
|
||||
"optionsList": [
|
||||
["source", qsTr("图片原目录")],
|
||||
["specify", qsTr("指定目录")],
|
||||
],
|
||||
},
|
||||
"dir": {
|
||||
"title": qsTr("指定目录"),
|
||||
"toolTip": qsTr("必须先指定“保存到指定目录”才生效"),
|
||||
"type": "file",
|
||||
"selectExisting": true, // 选择现有
|
||||
"selectFolder": true, // 选择文件夹
|
||||
"dialogTitle": qsTr("OCR结果保存目录"),
|
||||
},
|
||||
"fileNameFormat": {
|
||||
"title": qsTr("文件名格式"),
|
||||
"toolTip": qsTr("无需填写拓展名。支持插入以下占位符:\n%date 日期时间\n%name 原文件夹名/文件名\n举例:[OCR]_%name_%date\n生成:[OCR]_我的图片_2023-09-01_12-13.txt\n添加占位符可以避免旧文件被新文件覆盖。"),
|
||||
"default": "[OCR]_%name_%date",
|
||||
"advanced": true, // 高级选项
|
||||
},
|
||||
"datetimeFormat": {
|
||||
"title": qsTr("日期时间格式"),
|
||||
"toolTip": qsTr("文件名中 %date 的日期格式。支持插入以下占位符:\n%Y 年、 %m 月、 %d 日、 %H 小时、 \n%M 分钟、 %S 秒 、 %unix 时间戳 \n举例:%Y年%m月%d日_%H-%M\n生成:2023年09月01日_12-13.txt"),
|
||||
"default": "%Y%m%d_%H%M",
|
||||
"advanced": true, // 高级选项
|
||||
},
|
||||
|
||||
"filesType": {
|
||||
"title": qsTr("保存文件类型"),
|
||||
"type": "group",
|
||||
"enabledFold": true,
|
||||
"fold": false,
|
||||
|
||||
"txt": {
|
||||
"title": qsTr("txt 标准格式"),
|
||||
"toolTip": qsTr("含原图片文件名和识别文字"),
|
||||
"default": true,
|
||||
},
|
||||
"txtPlain": {
|
||||
"title": qsTr("p.txt 纯文字格式"),
|
||||
"toolTip": qsTr("仅输出识别文字,不含图片标题"),
|
||||
"default": false,
|
||||
},
|
||||
"txtIndividual": {
|
||||
"title": qsTr("txt 单独文件"),
|
||||
"toolTip": qsTr("对每张图片,生成同名txt文件,仅输出识别文字"),
|
||||
"default": false,
|
||||
},
|
||||
"md": {
|
||||
"title": qsTr("md 图文混排"),
|
||||
"toolTip": qsTr("Markdown图文混排格式,可用Markdown阅读器浏览文件"),
|
||||
"default": false,
|
||||
},
|
||||
"csv": {
|
||||
"title": qsTr("csv 表格文件(Excel)"),
|
||||
"toolTip": qsTr("将图片信息和识别内容写入csv表格文件。可用Excel打开,另存为xlsx格式。"),
|
||||
"default": false,
|
||||
},
|
||||
"jsonl": {
|
||||
"title": qsTr("jsonl 原始信息"),
|
||||
"toolTip": qsTr("每行为一条json数据,便于第三方程序读取操作"),
|
||||
"default": false,
|
||||
},
|
||||
},
|
||||
|
||||
"ignoreBlank": {
|
||||
"title": qsTr("输出忽略空白图片"),
|
||||
"toolTip": qsTr("若图片没有文字或识别失败,也不会输出错误提示信息"),
|
||||
"default": true,
|
||||
},
|
||||
},
|
||||
|
||||
// 任务完成后续操作
|
||||
"postTaskActions": qmlapp.globalConfigs.utilsDicts.getPostTaskActions(),
|
||||
|
||||
"other": {
|
||||
"title": qsTr("其它"),
|
||||
"type": "group",
|
||||
|
||||
"simpleNotificationType": qmlapp.globalConfigs.utilsDicts.getSimpleNotificationType()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
输出文件类型
|
||||
.txt 标准格式
|
||||
.txt 纯文本格式
|
||||
.txt 多个独立文件
|
||||
.jsonl 原始信息
|
||||
*/
|
||||
145
UmiOCR-data/qt_res/qml/TabPages/BatchOCR/PreviewImage.qml
Normal file
145
UmiOCR-data/qt_res/qml/TabPages/BatchOCR/PreviewImage.qml
Normal file
@@ -0,0 +1,145 @@
|
||||
// ===========================================
|
||||
// =============== 图片预览面板 ===============
|
||||
// ===========================================
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import "../../Widgets"
|
||||
import "../../Widgets/ImageViewer"
|
||||
|
||||
ModalLayer {
|
||||
id: pRoot
|
||||
closeText: ""
|
||||
|
||||
// 展示图片/文本
|
||||
function show(path, data, text) {
|
||||
visible = true
|
||||
imageText.showPath(path)
|
||||
if(data) {
|
||||
console.log("展示data", data)
|
||||
imageText.showTextBoxes(data)
|
||||
}
|
||||
if(text) {
|
||||
textEdit.text = text
|
||||
}
|
||||
else {
|
||||
textEdit.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contentItem: DoubleRowLayout {
|
||||
anchors.fill: parent
|
||||
initSplitterX: 0.7
|
||||
leftItem: Panel {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
// 顶部栏
|
||||
Item {
|
||||
id: leftTop
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: size_.smallSpacing
|
||||
height: size_.line + size_.smallSpacing
|
||||
// 靠右
|
||||
Row {
|
||||
id: leftTopR
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.rightMargin: size_.spacing
|
||||
spacing: size_.smallSpacing
|
||||
|
||||
// 显示文字
|
||||
CheckButton {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
text_: qsTr("文字")
|
||||
toolTip: qsTr("在图片上叠加显示识别文字\n可在全局设置中设为默认关闭")
|
||||
checked: imageText.showOverlay
|
||||
enabledAnime: true
|
||||
onCheckedChanged: imageText.showOverlay = checked
|
||||
}
|
||||
|
||||
IconButtonBar {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
btnList: [
|
||||
{
|
||||
icon: "menu",
|
||||
onClicked: imageText.popupMenu,
|
||||
toolTip: tr("右键菜单"),
|
||||
},
|
||||
{
|
||||
icon: "save",
|
||||
onClicked: imageText.saveImage,
|
||||
toolTip: tr("保存图片"),
|
||||
},
|
||||
{
|
||||
icon: "open_image",
|
||||
onClicked: imageText.openImage,
|
||||
toolTip: tr("用默认应用打开图片"),
|
||||
},
|
||||
{
|
||||
icon: "full_screen",
|
||||
onClicked: imageText.imageFullFit,
|
||||
toolTip: tr("图片大小:适应窗口"),
|
||||
},
|
||||
{
|
||||
icon: "one_to_one",
|
||||
onClicked: imageText.imageScaleAddSub,
|
||||
toolTip: tr("图片大小:实际"),
|
||||
},
|
||||
]
|
||||
}
|
||||
// 百分比显示
|
||||
Text_ {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: (imageText.scale*100).toFixed(0) + "%"
|
||||
color: theme.subTextColor
|
||||
width: size_.line * 2.5
|
||||
}
|
||||
}
|
||||
}
|
||||
// 图片预览区域
|
||||
ImageWithText {
|
||||
id: imageText
|
||||
anchors.top: leftTop.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: size_.spacing
|
||||
anchors.topMargin: size_.smallSpacing
|
||||
}
|
||||
}
|
||||
rightItem: Panel {
|
||||
anchors.fill: parent
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: size_.spacing
|
||||
color: theme.bgColor
|
||||
border.width: 1
|
||||
border.color: theme.coverColor4
|
||||
|
||||
ScrollView {
|
||||
id: textView
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: size_.spacing
|
||||
anchors.rightMargin: size_.spacing
|
||||
contentWidth: width // 内容宽度
|
||||
clip: true // 溢出隐藏
|
||||
|
||||
TextEdit_ {
|
||||
id: textEdit
|
||||
width: textView.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user