0

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.

Thalia
  • 13,637
  • 22
  • 96
  • 190

2 Answers2

1

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);
}
Tarod
  • 6,732
  • 5
  • 44
  • 50
  • Thank you for your example. Your approach - took me a minute to see - is similar - you extract the information from all blocks at `begin()` - which takes care of empty blocks as well as others non-empty. It removes the need to test for end separately. The one problem - I need to be able to do this on a selection not entire document. In case the block is not empty, but selection does not start at the beginning of the block, I am always reading info that is from wrong fragments that will always result in bad output. – Thalia Oct 02 '15 at 14:28
  • I may be stuck with my approach... The fact that my code works - that I can extract a valid fragment from an empty block - when `it.begin() == it.end()` - is puzzling - but I CAN extract a valid fragment from an empty block, and check its format with `charFormat()`, just like in the normal loop, not even with `blockCharFormat()`. But looking at your example, I did not realize that I can in that case extract the format from `it` itself and don't need a fragment. – Thalia Oct 02 '15 at 14:30
  • You're code is pretty good. With my code the application crashes if we try to extract a fragment when we're in a empty block and `iterator::atEnd()` is true. I've updated the code to illustrate this. If you have text in an empty block (that's [possible](http://doc.qt.io/qt-4.8/qtextblock.html#charFormat)!) , maybe `QTextFragment::isValid()` works different. – Tarod Oct 05 '15 at 07:32
1

Here is the pseudo code (which works for me). In short, when the method atEnd() returns TRUE, then you append a new line.

QTextEdit * pwTextEdit = whatever your source;
QTextDocument * poDocument = pwTextEdit->document();

QTextBlock oTextBlock = poDocument->begin();
while (oTextBlock.isValid())
{
    QTextBlock::iterator oTextBlockIterator(oTextBlock.begin());
    while (TRUE)
    {
        if (oTextBlockIterator.atEnd())
        {
            // Append your '\n' here
            break;
        }
        QTextFragment oTextFragment = oTextBlockIterator.fragment();
        QString sText = oTextFragment.text();
        // Process the text from sText
        oTextBlockIterator++;
    }
    oTextBlock = oTextBlock.next();
}
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Daniel
  • 51
  • 3