获取QTextEdit选择的边界框

时间:2017-10-24 13:33:13

标签: python selection pyside bounding-box qtextedit

我正在尝试将一系列文本选择的边界框存储在列表中。边界框是可以包含整个选择的最小矩形。列表中的每个项目都有一个起始点和终点,以QTextEdit窗口开头的字符和字母标识符来衡量。 QTextEdit.cursorRect(cursor)应该这样做,但是会产生无意义的方框尺寸:

id: A -- PySide.QtCore.QRect(0, 0, 1, 10)
id: B -- PySide.QtCore.QRect(0, 0, 1, 10)
id: C -- PySide.QtCore.QRect(0, 0, 1, 10)

所有选择都从不同的点开始,因此(0,0)在视点坐标中不正确。此外,它们中的一些跨越几行,因此宽度和高度应该变化。问题可能是光标处于循环中,直到循环结束后我才用setTextCursor设置它。我这样做是因为我也将选择渲染为亮点。如何让cursorRect正常工作或以其他方式获得每个选择的单独边界框?这是代码:

import sys
from PySide.QtCore import *
from PySide.QtGui import *

db = ((5,8,'A'),(20,35,'B'),(45,60,'C')) # start, end, and identifier of highlights

class TextEditor(QTextEdit):

    def __init__(self, parent=None):
        super().__init__(parent)
        text="This is example text that is several lines\nlong and also\nstrangely broken up and can be\nwrapped."
        self.setText(text)
        for n in range(0,len(db)):
            row = db[n]
            startChar = row[0]
            endChar = row[1]
            id = row[2]

            cursor = self.textCursor()
            cursor.setPosition(startChar)
            cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, endChar-startChar)

            rect = self.cursorRect(cursor)
            print("id: %s -- %s" % (id,str(rect)))

            charfmt = cursor.charFormat()
            charfmt.setBackground(QColor(Qt.yellow))
            cursor.setCharFormat(charfmt)
        cursor.clearSelection()
        self.setTextCursor(cursor)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    editor = TextEditor()
    editor.show()
    app.exec_()
    sys.exit(app.exec_())

编辑1

以下是该计划的文字。我将使用CAPS作为突出显示的文本:

This IS example text THAT IS SEVERAL lines
loNG AND ALSO
STRangely broken up and can be
wrapped.

假设每个字符都是10像素乘10像素。 “IS”在5个字符中开始并扩展为3个字符(包括末尾的空格)。因此,“I”的左上角将是x = 50,y = 0。空间的右下角将是x = 80,y = 10。如果以坐标给出边界矩形,则为(50,0,80,10)。如果以起始坐标和大小给出边界矩形,则为(50,0,30,10)。

在第二行是继续第三行的突出显示。它最左边的字符是第3行开头的“S”,即x = 0。它最右边的字符是“ALSO”中的“O”,它以x = 130结尾。它的最上面一行是第二行,从y = 10开始。它的最底线是第三行,它以y = 30结束。因此,边界框的坐标为(0,10,130,30)或起点和大小为(0,10,130,20)。

1 个答案:

答案 0 :(得分:2)

下面是第一次找到由数据库中的信息指定的所有突出显示部分的边界框。为了清楚地说明每个边界框覆盖的内容,示例脚本显示相应的橡皮筋。结果如下:

enter image description here

调整窗口大小将根据当前自动换行自动重新计算框。请注意,如果窗口变得非常小,这可能意味着几个框彼此重叠。

我已将其作为单独的方法实现,因为对char格式的更改可能会重新设置文档布局。因此,在第二遍中计算箱子更可靠。当然,这也允许在调整窗口大小时进行动态重新计算。

import sys
from PySide.QtCore import *
from PySide.QtGui import *

db = ((5,8,'A'),(20,35,'B'),(45,60,'C')) # start, end, and identifier of highlights

class TextEditor(QTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        text="This is example text that is several lines\nlong and also\nstrangely broken up and can be\nwrapped."
        self.setText(text)
        cursor = self.textCursor()
        for n in range(0,len(db)):
            row = db[n]
            startChar = row[0]
            endChar = row[1]
            id = row[2]
            cursor.setPosition(startChar)
            cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, endChar-startChar)
            charfmt = cursor.charFormat()
            charfmt.setBackground(QColor(Qt.yellow))
            cursor.setCharFormat(charfmt)
        cursor.clearSelection()
        self.setTextCursor(cursor)

    def getBoundingRect(self, start, end):
        cursor = self.textCursor()
        cursor.setPosition(end)
        last_rect = end_rect = self.cursorRect(cursor)
        cursor.setPosition(start)
        first_rect = start_rect = self.cursorRect(cursor)
        if start_rect.y() != end_rect.y():
            cursor.movePosition(QTextCursor.StartOfLine)
            first_rect = last_rect = self.cursorRect(cursor)
            while True:
                cursor.movePosition(QTextCursor.EndOfLine)
                rect = self.cursorRect(cursor)
                if rect.y() < end_rect.y() and rect.x() > last_rect.x():
                    last_rect = rect
                moved = cursor.movePosition(QTextCursor.NextCharacter)
                if not moved or rect.y() > end_rect.y():
                    break
            last_rect = last_rect.united(end_rect)
        return first_rect.united(last_rect)

class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.edit = TextEditor(self)
        layout = QVBoxLayout(self)
        layout.addWidget(self.edit)
        self.boxes = []

    def showBoxes(self):
        while self.boxes:
            self.boxes.pop().deleteLater()
        viewport = self.edit.viewport()
        for start, end, ident in db:
            rect = self.edit.getBoundingRect(start, end)
            box = QRubberBand(QRubberBand.Rectangle, viewport)
            box.setGeometry(rect)
            box.show()
            self.boxes.append(box)

    def resizeEvent(self, event):
        self.showBoxes()
        super().resizeEvent(event)

if __name__ == '__main__':

    app = QApplication(sys.argv)
    window = Window()
    window.setGeometry(800, 100, 350, 150)
    window.show()
    window.showBoxes()
    sys.exit(app.exec_())