Armand Jordaan
Armand Jordaan

Reputation: 348

How to get QTextBrowser to always insert text at the end

I am trying to make a serial terminal program by using QTextBrowser to display incoming data from a serial port. I have set a QTimer to call the paintEvent every 100ms, and show characters on the QTextBrowser widget if anything was received on the serial port.

My problem is that every time I click say in the middle of the QTextBrowser, it is as if though the cursor moves and then on all subsequent ui->tbOutput->insertPlainText(QString(buf));, only half of the QTextBrowser gets updated.

When I click on the bottom of the QTextBrowser widget, the whole QTextBrowser is updated again.

This is the code that I have, where from various other articles, I have tried to scroll to the bottom, and move the text cursor to the end, but it does not do what I want.

void MainWindow::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    static char buf[10240];

    if (terminal->serialport.bytesAvailable() > 0)
    {
        // sizeof(buf)-1 so that there is space for zero termination character
        qint64 numread = terminal->serialport.read(buf,sizeof(buf)-1); 

        if ((numread > 0) && (numread < sizeof(buf)))
        {
            buf[numread] = 0; // set zero termination
            ui->tbOutput->insertPlainText(QString(buf));
            ui->tbOutput->verticalScrollBar()->setValue(
                ui->tbOutput->verticalScrollBar()->maximum());

            ui->tbOutput->textCursor().setPosition(QTextCursor::End);
        }
    }
}

Upvotes: 1

Views: 2133

Answers (1)

cbuchart
cbuchart

Reputation: 11555

A few things:

  • QTextBrowser::textCursor returns a copy, so any modification is not applied to the document
  • QTextBrowser::setPosition moves the cursor to an absolute position, therefore you are always moving to position 11 (int value to QTextCursor::End). Use QTextBrowser::movePosition instead
  • finally, it would be better to move the cursor before adding the text, so you are sure it will be added at the end of the document.

Here the modified code:

void MainWindow::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    static char buf[10240];

    if (terminal->serialport.bytesAvailable() > 0)
    {
        // sizeof(buf)-1 so that there is space for zero termination character
        qint64 numread = terminal->serialport.read(buf,sizeof(buf)-1); 

        if ((numread > 0) && (numread < sizeof(buf)))
        {
            buf[numread] = 0; // set zero termination
            auto textCursor = ui->tbOutput->textCursor();
            textCursor.movePosition(QTextCursor::End);
            ui->tbOutput->setTextCursor(textCursor);
            ui->tbOutput->insertPlainText(QString(buf));
            ui->tbOutput->verticalScrollBar()->setValue(
                ui->tbOutput->verticalScrollBar()->maximum());
        }
    }
}

On the other hand, some additional considerations:

  • QIODevice::read(char* data, qint64 maxSize) will read at most maxSize bytes, so checking if the number of read bytes is smaller than your buffer is unnecessary.
  • Do not do it in the paintEvent, it is not the place to read data but to display it. Instead, connect the timer with a slot and read data there and re-paint your console (ui->tbOutput->update()) only if new data has arrived.

Upvotes: 4

Related Questions