// ========================================================== // =============== 可显示OCR文本的增强Image组件 =============== // ========================================================== import QtQuick 2.15 import QtQuick.Controls 2.15 import ".." ImageScale { id: iRoot property bool showOverlay: true // 显示叠加层 Component.onCompleted: { // 默认显示/关闭叠加层 showOverlay = qmlapp.globalConfigs.getValue("ui.imgShowOverlay") } beforeShow: () => { mouseArea.initIndex() // 清空选字参数 textBoxes = [] // 清空旧文本块 } // 展示文本块 function showTextBoxes(res) { beforeShow() // 提取文本框 if(res.code == 100 && res.data.length > 0) { let tbs = [] for(let i in res.data) { const d = res.data[i] const info = { x: d.box[0][0], y: d.box[0][1], width: d.box[2][0] - d.box[0][0], height: d.box[2][1] - d.box[0][1], text: d.text, end: d.end || "", // 行尾间隔符 } tbs.push(info) } textBoxes = tbs } } // 弹出菜单 function popupMenu() { selectMenu.popup() } // 显示/隐藏叠加层 function switchOverlay() { showOverlay = !showOverlay if(!showOverlay) mouseArea.cursorShape = Qt.OpenHandCursor } property var textBoxes: [] // 文本块列表 // 文本块叠加层 overlayLayer: Item { id: oRoot anchors.fill: parent visible: showOverlay Repeater { id: textBoxRepeater model: textBoxes TextBox { text: modelData.text x: modelData.x y: modelData.y end: modelData.end // 结尾间隔符 Component.onCompleted: { width = modelData.width height = modelData.height resetSize() // 自适应字体和组件大小 textBoxes[index].width = width // 记录修改后的组件大小 textBoxes[index].height = height textBoxes[index].obj = this } } } } MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true property int startIndex: -1 // 拖拽开始时,文本框序号 property int startTextIndex: -1 // 拖拽开始时,字符序号 property int endIndex: -1 // 拖拽结束时,文本框序号 property int endTextIndex: -1 // 拖拽结束时,字符序号 property int selectUpdate: 0 acceptedButtons: Qt.LeftButton | Qt.RightButton // 清除index function initIndex() { startIndex = startTextIndex = endIndex = endTextIndex = -1 } // 检测当前鼠标点,位于哪一个tb内 function mouseInTextBox() { const localPoint = oRoot.mapFromItem(mouseArea, mouseX, mouseY) const x = localPoint.x, y = localPoint.y for (let i=0,l=textBoxes.length; i= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height) { return i } } return -1 } // 检测当前鼠标点,在index tb内的哪一个字符处 function mouseInTextIndex(index) { return textBoxes[index].obj.where(mouseArea, mouseX, mouseY) } // 获取Index正确顺序。返回: [li 起始块, lt 起始块选区左侧, ri 结束块, rt 结束块选区右侧] function getIndexes() { let li, lt, ri, rt if(startIndex < endIndex) { li=startIndex; lt=startTextIndex; ri=endIndex; rt=endTextIndex; } else if(startIndex > endIndex) { li=endIndex; lt=endTextIndex; ri=startIndex; rt=startTextIndex; } else { li = ri = startIndex if(startTextIndex < endTextIndex) { lt=startTextIndex; rt=endTextIndex; } else if(startTextIndex > endTextIndex) { lt=endTextIndex; rt=startTextIndex; } else { // 单击,未选中 lt = rt = -1 } } return [li, lt, ri, rt] } // 根据 Index 的参数,选择对应文本。 function selectIndex() { const [li, lt, ri, rt] = getIndexes() // 遍历每个文本框数据 for (let i = 0, l=textBoxes.length; i < l; i++) { const tEdit = textBoxes[i].obj.textEdit if( li<0 || ri<0 || i
  • ri ) { // 未被选中 tEdit.deselect() } else if(i === li && i === ri) { // 单个块 if(lt === rt) // 无有效选中 tEdit.deselect() else tEdit.select(lt, rt) } else if(i === li) { // 多个块的起始 const len = textBoxes[i].text.length tEdit.select(lt, len) } else if(i === ri) { // 多个块的结束 tEdit.select(0, rt) } else { // 多个块的中间 tEdit.selectAll(0, rt) } } } // 全选 function selectAll() { const l = textBoxes.length if(l === 0) return startIndex = startTextIndex = 0 endIndex = l-1 endTextIndex = textBoxes[endIndex].text.length selectIndex() } // 复制已选中的内容 function selectCopy() { let [li, lt, ri, rt] = getIndexes() // 没有有效选中,则复制全部 if(li<0 || ri<0 || (li===ri && lt===rt)) { selectAllCopy() return } let copyText = "" // 选中单个文本块 if(li === ri) { copyText = textBoxes[li].text.substring(lt, rt) } // 选中多个块,则遍历多个块,提取各自的文本 else { for(let i = li; i <= ri; i++) { const text = textBoxes[i].text const end = textBoxes[i].end if(i === li) // 多个块的起始 copyText = text.substring(lt) + end else if(i === ri) // 多个块的结束 copyText += text.substring(0, rt) else // 多个块的中间 copyText += text + end } } if(copyText && copyText.length > 0) { qmlapp.utilsConnector.copyText(copyText) qmlapp.popup.simple(qsTr("图片:复制%1字").arg(copyText.length), "") } else { qmlapp.popup.simple(qsTr("图片:无选中文字"), "") } } // 复制所有 function selectAllCopy() { let copyText = "" for (let i = 0, l=textBoxes.length; i < l; i++) { copyText += textBoxes[i].text if(i < l-1) copyText += textBoxes[i].end } qmlapp.utilsConnector.copyText(copyText) qmlapp.popup.simple(qsTr("图片:复制全部%1字").arg(copyText.length), "") selectAll() } // 按下 onPressed: { mouseArea.forceActiveFocus() if (mouse.button === Qt.RightButton) { selectMenu.popup() return } if(!showOverlay) { mouse.accepted = false return } initIndex() const tbi = mouseInTextBox() cursorShape = tbi < 0 ? Qt.ClosedHandCursor : Qt.IBeamCursor if(tbi >= 0) { // 选择文本 startIndex = tbi startTextIndex = mouseInTextIndex(tbi) } else { mouse.accepted = false } } // 移动 onPositionChanged: { if(!showOverlay) return const tbi = mouseInTextBox() if(pressed) { // 拖拽中 if(tbi >= 0) { // 选择文本 endIndex = tbi endTextIndex = mouseInTextIndex(tbi) selectIndex() } } else { // 悬停中 cursorShape = tbi < 0 ? Qt.OpenHandCursor : Qt.IBeamCursor } } // 抬起 onReleased: { if(!showOverlay) return if (mouse.button === Qt.RightButton) return const tbi = mouseInTextBox() cursorShape = tbi < 0 ? Qt.OpenHandCursor : Qt.IBeamCursor if(startIndex === -1) return if(tbi >= 0) { endIndex = tbi endTextIndex = mouseInTextIndex(tbi) } selectIndex() } // 菜单 Menu_ { id: selectMenu menuList: [ [mouseArea.selectCopy, qsTr("复制  (Ctrl+C)")], [mouseArea.selectAll, qsTr("全选  (Ctrl+A)")], [iRoot.copyImage, qsTr("复制图片(Ctrl+X)")], [iRoot.saveImage, qsTr("保存图片(Ctrl+S)")], [iRoot.switchOverlay, qsTr("显示/隐藏文字(Tab)")], [iRoot.openImage, qsTr("用默认应用打开图片")], [iRoot.clear, qsTr("删除图片(Ctrl+D)"), "noColor"], ] } // 按键事件 Keys.onPressed: { if (event.modifiers & Qt.ControlModifier) { event.key===Qt.Key_A && selectAll() event.key===Qt.Key_C && selectCopy() event.key===Qt.Key_X && iRoot.copyImage() event.key===Qt.Key_S && iRoot.saveImage() event.key===Qt.Key_D && iRoot.clear() } if (event.key === Qt.Key_Tab) { iRoot.switchOverlay() Qt.callLater(mouseArea.forceActiveFocus) } } } }