Files
work-secretfile-selfcheck/UmiOCR-data/qt_res/qml/Widgets/ImageViewer/ImageWithText.qml

300 lines
10 KiB
QML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ==========================================================
// =============== 可显示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<l; i++) {
const rect = textBoxes[i]
if (x >= 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<li || 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)
}
}
}
}