// ================================================== // =============== 功能页:批量文档处理 =============== // ================================================== import QtQuick 2.15 import QtQuick.Controls 2.15 import ".." import "../../Widgets" import "../../Widgets/ResultLayout" import "../../Widgets/IgnoreArea" /* 文档参数: path 路径 pages 页数显示 state 状态显示 page_count 总页数 range_start 范围开始 range_end 范围结束 is_encrypted 需要密码 is_authenticate 密码正确 password 密码 */ TabPage { id: tabPage // ========================= 【逻辑】 ========================= property string msnID: "" // 当前任务ID // 异步加载一批文档路径 function addDocs(paths) { if(paths.length <= 0) return const isRecurrence = configsComp.getValue("mission.recurrence") qmlapp.asynFilesLoader.run(paths,"doc",isRecurrence,onAddDocs) } // 完毕后,将合法表格加入表格 function onAddDocs(docs) { if(docs.length <= 0) return let encryptedCount = 0 for(let i in docs) { const info = docs[i] filesTableView.add({ // 显示:路径,状态,页范围 path: info.path, pages: `${info.page_count}`, // 如果范围为整本,只显示总页数。否则显示 起始-结束 state: info.is_encrypted ? qsTr("加密") : "" , // 数据 page_count: info.page_count, range_start: 1, range_end: info.page_count, is_encrypted: info.is_encrypted, // 有密码 is_authenticate: !info.is_encrypted, // 已解密(密码正确) password: "", }) if(info.is_encrypted) encryptedCount++ } if(encryptedCount > 0) { qmlapp.popup.simple(qsTr("%1个加密文档").arg(encryptedCount), qsTr("请点击文件名填写密码")) } } // 运行文档任务 function docStart() { const fileCount = filesTableView.rowCount if(fileCount <= 0) { ctrlPanel.stopFinished() return } // 获取信息 const docs = filesTableView.getColumnsValues([ "path","range_start", "range_end", "page_count", "is_encrypted", "is_authenticate", "password"]) // 第1次遍历:检查密码填写 for(let i = 0; i < fileCount; i++) { const d = docs[i] if(d.is_encrypted && !d.is_authenticate) { qmlapp.popup.message(qsTr("文档已加密"), qsTr("【%1】\n请点击文档名,设置密码").arg(d.path), "warning") ctrlPanel.stopFinished() return } } // 第2次遍历:刷新信息 let allPages = 0 // 页总数 for(let i = 0; i < fileCount; i++) { const d = docs[i] allPages += d.range_end - d.range_start + 1 filesTableView.setProperty(d.path, "state", qsTr("排队")) } // 若tabPanel面板的下标没有变化过,则切换到记录页 if(tabPanel.indexChangeNum < 2) tabPanel.currentIndex = 1 // 任务进度 开始计时 ctrlPanel.runFinished(allPages) // 提交任务 const argd = configsComp.getValueDict() tabPage.callPy("msnDocs", docs, argd) } // 停止文档任务 function docStop() { tabPage.callPy("msnStop") // 刷新表格,清空未执行的任务的状态 let msnLength = filesTableView.rowCount for(let i = 0; i < msnLength; i++) { const row = filesTableView.get(i) const s = row.state if(s.length > 0 && s[0] !== "√" && s[0] !== "×") { filesTableView.setProperty(i, "state", "") } } ctrlPanel.stopFinished() } // 文件表格中单击文档 function onClickDoc(index) { if(ctrlPanel.state_ !== "stop") return const info = filesTableView.get(index) if(Object.keys(info).length > 0) previewDoc.show(info) } // 关闭页面 function closePage() { if(ctrlPanel.state_ !== "stop") { const argd = { yesText: qsTr("依然关闭") } const callback = (flag)=>{ if(flag) { docStop() delPage() } } qmlapp.popup.dialog("", qsTr("任务正在进行中。\n要结束任务并关闭页面吗?"), callback, "warning", argd) } else { delPage() } } // ========================= 【python调用qml】 ========================= // 准备开始处理一个文档 function onDocStart(path) { // 刷新表格显示 const d = filesTableView.get(path) let state = `0/${d.range_end - d.range_start + 1}` filesTableView.setProperty(path, "state", state) } // 获取一个文档的一页的结果 function onDocGet(path, page, res) { // 刷新单个文档的信息 const d = filesTableView.get(path) const state = `${page - d.range_start + 1}/${d.range_end - d.range_start + 1}` filesTableView.setProperty(path, "state", state) // 提取文字,添加到结果表格 const title = path2name(path) res.title = `${title} - ${page}` resultsTableView.addOcrResult(res) ctrlPanel.msnStep(1) } // 一个文档处理完毕。 isAll==true 时所有文档处理完毕。 function onDocEnd(path, msg, isAll) { const errTitle = qsTr("文档识别异常") // 成功结束 if(msg.startsWith("[Success]")) { filesTableView.setProperty(path, "state", "√") msg = "" } // 单个文档任务失败,总体未结束 else if(!isAll) { filesTableView.setProperty(path, "state", "× "+ qsTr("失败")) qmlapp.popup.simple(errTitle, msg) } // 所有文档处理完毕 if(isAll) { const simpleType = configsComp.getValue("other.simpleNotificationType") qmlapp.popup.simple(qsTr("批量识别完成"), "", simpleType) if(msg) // 如果有异常,则弹窗 qmlapp.popup.message(errTitle, msg, "error") ctrlPanel.stopFinished() // 任务完成后续操作 qmlapp.globalConfigs.utilsDicts.postTaskHardwareCtrl( configsComp.getValue("postTaskActions.system") ) } } // 路径转文件名 function path2name(path) { const parts = path.split("/") return parts[parts.length - 1] } // ========================= 【布局】 ========================= // 配置 configsComp: BatchDOCConfigs {} // 主区域:可切换双栏面板 DoubleSwitchableLayout { id: doubleLayout saveKey: "BatchDOC_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.docStart() onPauseClicked: { tabPage.callPy("msnPause") pauseFinished() } onResumeClicked: { tabPage.callPy("msnResume") resumeFinished() } onStopClicked: tabPage.docStop() } // 下方文件表格 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:onClickDoc}, {key: "state", title: qsTr("状态"), btn: true, onClicked:onClickDoc}, {key: "pages", title: qsTr("页数"), btn: true, onClicked:onClickDoc}, ] openBtnText: qsTr("打开文档") clearBtnText: qsTr("清空") defaultTips: qsTr("拖入文档或文件夹") fileDialogTitle: qsTr("请选择文档") fileDialogNameFilters: [qsTr("文档")+" (*.pdf *.xps *.epub *.mobi *.fb2 *.cbz)"] isLock: ctrlPanel.state_ !== "stop" onAddPaths: { tabPage.addDocs(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_ { id: "addDocsDropArea" anchors.fill: parent callback: tabPage.addDocs } // 预览面板 PreviewDoc { id: previewDoc anchors.fill: parent configsComp: tabPage.configsComp updateInfo: (path, info) => { let infoA = filesTableView.get(path) Object.assign(infoA, info) filesTableView.set(path, infoA) } } }