Search code examples
qtformattingqgraphicsitemqgraphicstextitem

Getting formatting of empty lines


I am a bit confused about how the QTextBlock::iterator works:

The documentation shows clear examples of how to use it, on normal text:

QTextBlock::iterator it;
for (it = currentBlock.begin(); !(it.atEnd()); ++it) {
    QTextFragment currentFragment = it.fragment();
    if (currentFragment.isValid())
        processFragment(currentFragment);
}

I encounter problems on empty lines of text. On those lines,

it = currentBlock.begin();
if(it.atEnd())
    // returns true !

I still need to be able to read formatting (char and block)

Should I check the block at end ? Is there any other way to test blocks with nothing except the new line ?

My current solution: check the last iterator as well, separate from the "for" loop, and also test if it is the last block in the document (if I try to get the fragment of the last block in the document, the program crashes).

It seems that I am working against the documentation... How should I get the formatting of empty lines ?

Edit:

My old solution:

QTextBlock currentBlock = document()->findBlock(selStart);
QTextBlock lastBlock = document()->lastBlock();
while (currentBlock.isValid())
{
    QTextBlock::iterator it = currentBlock.begin();
    if(currentBlock != lastBlock && it.atEnd())
    {
        QTextFragment currentFragment = it.fragment();
        if (currentFragment.isValid())
        {
            QTextCharFormat f = currentFragment.charFormat();
            // do something
        }
    } 
    else
    {
        for (; !(it.atEnd()); ++it)
        {
            QTextFragment currentFragment = it.fragment();
            if (currentFragment.isValid())
            {
                // do stuff
                QTextCharFormat f = currentFragment.charFormat();
                // do stuff
            }
        }
    }
}

New solution based from answer from Tarod eliminates one test (but seems to have less consistent behavior)

QTextBlock currentBlock = document()->findBlock(selStart);
QTextBlock lastBlock = document()->lastBlock();
while (currentBlock.isValid())
{
    QTextBlock::iterator it = currentBlock.begin();
    if(currentBlock != lastBlock && it.atEnd())
    {
        QTextCharFormat f = currentBlock.charFormat();
        // do something
    } 
    else
    {
        for (; !(it.atEnd()); ++it)
        {
            QTextFragment currentFragment = it.fragment();
            if (currentFragment.isValid())
            {
                // do stuff
                QTextCharFormat f = currentFragment.charFormat();
                // do stuff
            }
        }
    }
}

I still need to check against last block and avoid using it if empty, sometimes it crashes.


Solution

  • I think the problem is you're just iterating over a QTextBlock and reading the contents of its text fragments. In this case, for an empty QTextBlock, as you proved, currentBlock.begin() == it.atEnd() because the QTextBlock has not any text fragments.

    You should iterate over all the document text blocks, get the required information and, if you need to, iterate over each one to read the sequence of text fragments.

    In the following example the block #3 it's an empty line (\n\n). You won't see the line qDebug() << "I am a QTextBlock with text!" printed although we still have information about this text block thanks to QTextBlockFormat and QTextCharFormat.

    main.cpp

    #include <QApplication>
    #include "graphicstextitem_3.h"
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        GraphicsTextItem_3 g3;
        g3.show();
    
        return a.exec();
    }
    

    graphicstextitem_3.h

    #ifndef GRAPHICSTEXTITEM_3_H
    #define GRAPHICSTEXTITEM_3_H
    
    #include <QMainWindow>
    
    class QGraphicsScene;
    class QGraphicsView;
    class QGraphicsTextItem;
    
    class GraphicsTextItem_3 : public QMainWindow
    {
        Q_OBJECT
    public:
        explicit GraphicsTextItem_3(QMainWindow *parent = 0);
    
    private:
         QGraphicsScene *scene;
         QGraphicsView *view;
         QGraphicsTextItem *item;
    
    signals:
    
    public slots:
    };
    
    #endif // GRAPHICSTEXTITEM_3_H
    

    graphicstextitem_3.cpp

    #include "graphicstextitem_3.h"
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QGraphicsTextItem>
    #include <QTextCursor>
    #include <QTextDocument>
    #include <QTextBlock>
    #include <QDebug>
    
    GraphicsTextItem_3::GraphicsTextItem_3(QMainWindow *parent) : QMainWindow(parent)
    {
        scene = new QGraphicsScene(this);
        view = new QGraphicsView(scene);
    
        item = new QGraphicsTextItem("Block 0\n Block 1\n Block 2\n\n Block 4");
        item->setTextInteractionFlags(Qt::TextEditorInteraction);
        QFont f = item->font();
        f.setPointSize(30);
        item->setFont(f);
    
        QTextDocument* doc = item->document();
    
        for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next())
        {
            QTextBlockFormat block_format = it.blockFormat();
            QTextCharFormat char_format = it.charFormat();
    
            qDebug() << "*** Block number: " << it.blockNumber()
                     << " with text: " << it.text();
    
            qDebug() << "* Block format info: "
                     << " leftMargin: " << block_format.leftMargin()
                     << " rightMargin: " << block_format.rightMargin()
                     << " topMargin: " << block_format.topMargin()
                     << " bottomMargin: " << block_format.bottomMargin()
                     << " lineHeight: " << block_format.lineHeight();
    
            qDebug() << "* Char format info: "
                     << " pointSize: " << char_format.font().pointSize()
                     << " fontFamily: " << char_format.font().family();
    
            QTextBlock::iterator tb_it = it.begin();
    
            if (tb_it.atEnd())
            {
                qDebug() << "it.begin() == tb_it.atEnd()";
                /* The application crashes if we get the fragment */
                // tb_it.fragment();
            }
    
            for (tb_it = it.begin(); !(tb_it.atEnd()); ++tb_it) {
                QTextFragment currentFragment = tb_it.fragment();
    
                if (currentFragment.isValid())
                {
                    qDebug() << "I am a QTextBlock with text!"
                             << " Out of here empty QTextBlock!"
                             << " You - shall not - pass!";
                }
            }
        }
    
        scene->addItem(item);
        view->setFixedSize(640, 480);
    
        this->setCentralWidget(view);
    }