获取 QTextEdit 选择的边界框 [英] Getting the bounding box of QTextEdit selection

查看:117
本文介绍了获取 QTextEdit 选择的边界框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to get the bounding box of a series of text selections stored in a list. The bounding box is the smallest rectangle that can contain the whole selection. Each item in the list has a start and end point measured in characters from the beginning of the QTextEdit window and also a letter identifier. QTextEdit.cursorRect(cursor) should do this, but is producing nonsensical box dimensions:

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 正常工作或以其他方式为每个选择获得一个单独的边界框?代码如下:

The selections all start at different points so (0,0) is not correct in viewpoint coordinates. Also, some of them span several lines so the width and height should vary. The problem may be that the cursor is in a loop and I don't set it with setTextCursor until after the loop finishes. I do this because I'm also rendering the selections as highlights. How can I get cursorRect to work correctly or otherwise get a separate bounding box for each selection? Here is the code:

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:

这是节目的正文.我将对突出显示的文本使用大写:

Here is the text from the program. I'll use CAPS for the highlighted text:

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

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

Let's assume every character is 10 pixels by 10 pixels. The "IS " starts 5 characters in and extends for 3 characters (including a space at the end). So, the upper left corner of the "I" would be at x=50,y=0. The bottom right corner of the space would be at x=80,y=10. If the bounding rectangle is given in coordinates, it would be (50,0,80,10). If the bounding rectangle is given in starting coordinates and size, it would be (50,0,30,10).

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

On the second line is a highlight that continues to the third line. Its leftmost character is the "S" at the start of line 3, which is at x=0. Its rightmost character is the "O" in "ALSO" which ends at x=130. Its topmost line is the second line, which begins at y=10. Its bottom most line is the third line, which ends at y=30. So, the bounding box would be (0,10,130,30) in coordinates or (0,10,130,20) in starting point and size.

推荐答案

下面是为由数据库信息指定的所有突出显示部分查找边界框的首次尝试.为了明确每个边界框涵盖的内容,示例脚本显示了相应的橡皮筋.结果如下:

Below is a first effort at finding the bounding boxes for all the highlighted sections specified by the information from the database. To make it clear exactly what each bounding box covers, the example script displays a corresponding rubber band. Here is what the results look like:

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

Resizing the window will automatically re-calculate the boxes according to the current word-wrapping. Note that this may mean several of the boxes overlap each other if the window becomes very small.

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

I have implemented this as a separate method, because changes to the char format can potentially re-set the document layout. It is therefore more reliable to calculate the boxes in a second pass. And of course, this also allows for dynamic re-calculation whenever the window is resized.

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_())

这篇关于获取 QTextEdit 选择的边界框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆