qtqscrollareaqtextbrowser

QTextbrowser Scroll through text whilst new text is still being added


I have a QTextBrowser widget and I'm adding text to this widget like

QTextBrowser m_outputLog;
...
void MainWindow::readStdout()
{
  if (m_running)
  {
    QByteArray data = m_runProcess->readAllStandardOutput();
    QString text = QString::fromUtf8(data);
    if (!text.isEmpty())
    {
      m_outputLog->moveCursor (QTextCursor::End);
      m_outputLog->insertPlainText (text);
      m_outputLog->moveCursor (QTextCursor::End);
    }
  }
}

the readStdout is connected to the m_outputLog through the signal / slot mechanism. This all works OK. The text is appended at the end though the disadvantage is that with each insertion there is a jump back to the end even when I did scroll up a bit. When I remove the m_outputLog->moveCursor (QTextCursor::End); statements the text is still nicely appended at the end but there is no automatic show of the text, I always have to use the mouse to scroll down.

Any suggestions?


Solution

  • Here is what I have done for my own need:

    void appendLogMessage(const QString& message)
    {
        QScrollBar vbar = verticalScrollBar();
    
        // analyze cursor and scrollbar positions
        const QTextCursor old_cursor = textCursor();
        const bool is_scrolled_down = vbar->value() == vbar->minimum();
        const int distanceFromBottom = vbar->maximum() - vbar->value();
    
        // move the cursor to the begining of the document.
        moveCursor(QTextCursor::Start);
    
        // adding new text
        textCursor().insertPlainText(message);
    
        if (old_cursor.hasSelection() || !is_scrolled_down)
        {
            // text is selected or scrollbar is not anymore at the bottom: maintain position.
            setTextCursor(old_cursor);
            vbar->setValue(vbar->maximum() - distanceFromBottom);
        }
        else
        {
            // no text is selected and the scrollbar is at the top: scroll to the top.
            moveCursor(QTextCursor::Start);
            vbar->setValue(verticalScrollBar()->minimum());
        }
    }
    

    Basically, you need to save the scrollbar position before adding the new text, and then decide to move back the scrollbar to this position depending on your own criteria (for example, I decided not to move the scrollbar only if the user has selected some text).