312 lines
10 KiB
QML
312 lines
10 KiB
QML
// ==================================================
|
||
// =============== 功能页:批量文档处理 ===============
|
||
// ==================================================
|
||
|
||
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)
|
||
}
|
||
}
|
||
} |