formatpyside6qtexteditqtextdocument

Removing/hiding the extra block/line from the beginning of a QTextEdit frame


When inserting a text frame, there is a text block above the frame that I cannot figure out how to get rid of, even when inserting the frame from position zero of the document. It looks like the text frame puts in an extra block at the beginning and end, and I'd like to ideally remove ALL extra blocks outside the frame.

from PySide6.QtGui import QTextFrameFormat
from PySide6.QtWidgets import QApplication, QTextEdit

app = QApplication()
text_edit = QTextEdit()

frame = QTextFrameFormat()
frame.setBorder(4)

cursor = text_edit.textCursor()
cursor.insertFrame(frame)

cursor.movePosition(cursor.MoveOperation.Up)
cursor.insertText("This text shouldn't be here!")

del frame, cursor

text_edit.show()
app.exec()

Solution

  • Unfortunately, it's not possible and it's also not documented.

    QTextFrame is always shown in a new line and this means that at least the first block will always exist, because the new line character is inside the first block.

    There are two possible alternatives, though.

    Use a table

    Qt text tables are actually advanced text frames, so you can use a single cell table with a similar format (QTextTableFormat inherits from QTextFrameFormat).

    Note that, by default, tables have borders around cells too. To avoid that, you can use setBorderCollapse(True), but it's only available since Qt 5.14.

    Alternatively, set a border width that is half as required, and set the cell spacing to 0. Then, in order to make the "frame" expand to the full width of the document, use setColumnWidthConstraints() with a percentage length set to 100:

    f = QTextTableFormat()
    f.setBorder(2) # note: 2, not 4
    f.setBorderStyle(QTextFrameFormat.BorderStyle_Solid)
    f.setBorderBrush(QBrush(QColor(Qt.darkGray)))
    f.setCellSpacing(0)
    f.setColumnWidthConstraints([QTextLength(QTextLength.PercentageLength, 100)])
    
    table = cursor.insertTable(1, 1, f)
    

    Hide the first block

    This is more of a hack than an actual solution, but it works: just get the first block of the document, and call setVisible(False).

    first = textEdit.document().begin()
    first.setVisible(False)
    

    Note that if you do that after the document has already been rendered, you must set the contents as "dirty" for that block, otherwise its layout will not be properly updated.

    This also means that you have to be extremely careful with this, because if you go on with editing the document programmatically, you must remember that the first block in the document is hidden, but it still exists, and if you try to add text to that block, it will obviously never appear until the block is shown again and its contents are marked dirty again.

    doc = textEdit.document()
    first = doc.begin()
    first.setVisible(False)
    doc.markContentsDirty(0, 1)