How to Append Text to Qplaintextedit Without Adding Newline, and Keep Scroll at the Bottom

How to append text to QPlainTextEdit without adding newline, and keep scroll at the bottom?

Ok, I'm not sure if my solution is actually "nice", but it seems to work for me: I just made new class QPlainTextEdit_My inherited from QPlainTextEdit, and added new methods appendPlainTextNoNL(), appendHtmlNoNL(), insertNL().

Please NOTE: read comments about params check_nl and check_br carefully, this is important! I spent several hours to figure out why is my widget so slow when I append text without new paragraphs.

/******************************************************************************************
* INCLUDED FILES
*****************************************************************************************/

#include "qplaintextedit_my.h"
#include <QScrollBar>
#include <QTextCursor>
#include <QStringList>
#include <QRegExp>

/******************************************************************************************
* CONSTRUCTOR, DESTRUCTOR
*****************************************************************************************/

QPlainTextEdit_My::QPlainTextEdit_My(QWidget *parent) :
QPlainTextEdit(parent)
{

}

QPlainTextEdit_My::QPlainTextEdit_My(const QString &text, QWidget *parent) :
QPlainTextEdit(text, parent)
{

}

/******************************************************************************************
* METHODS
*****************************************************************************************/

/* private */

/* protected */

/* public */

/**
* append html without adding new line (new paragraph)
*
* @param html html text to append
* @param check_nl if true, then text will be splitted by \n char,
* and each substring will be added as separate QTextBlock.
* NOTE: this important: if you set this to false,
* then you should append new blocks manually (say, by calling appendNL() )
* because one huge block will significantly slow down your widget.
*/
void QPlainTextEdit_My::appendPlainTextNoNL(const QString &text, bool check_nl)
{
QScrollBar *p_scroll_bar = this->verticalScrollBar();
bool bool_at_bottom = (p_scroll_bar->value() == p_scroll_bar->maximum());

if (!check_nl){
QTextCursor text_cursor = QTextCursor(this->document());
text_cursor.movePosition(QTextCursor::End);
text_cursor.insertText(text);
} else {
QTextCursor text_cursor = QTextCursor(this->document());
text_cursor.beginEditBlock();

text_cursor.movePosition(QTextCursor::End);

QStringList string_list = text.split('\n');

for (int i = 0; i < string_list.size(); i++){
text_cursor.insertText(string_list.at(i));
if ((i + 1) < string_list.size()){
text_cursor.insertBlock();
}
}

text_cursor.endEditBlock();
}

if (bool_at_bottom){
p_scroll_bar->setValue(p_scroll_bar->maximum());
}
}

/**
* append html without adding new line (new paragraph)
*
* @param html html text to append
* @param check_br if true, then text will be splitted by "<br>" tag,
* and each substring will be added as separate QTextBlock.
* NOTE: this important: if you set this to false,
* then you should append new blocks manually (say, by calling appendNL() )
* because one huge block will significantly slow down your widget.
*/
void QPlainTextEdit_My::appendHtmlNoNL(const QString &html, bool check_br)
{
QScrollBar *p_scroll_bar = this->verticalScrollBar();
bool bool_at_bottom = (p_scroll_bar->value() == p_scroll_bar->maximum());

if (!check_br){
QTextCursor text_cursor = QTextCursor(this->document());
text_cursor.movePosition(QTextCursor::End);
text_cursor.insertHtml(html);
} else {

QTextCursor text_cursor = QTextCursor(this->document());
text_cursor.beginEditBlock();

text_cursor.movePosition(QTextCursor::End);

QStringList string_list = html.split(QRegExp("\\<br\\s*\\/?\\>", Qt::CaseInsensitive));

for (int i = 0; i < string_list.size(); i++){
text_cursor.insertHtml( string_list.at(i) );
if ((i + 1) < string_list.size()){
text_cursor.insertBlock();
}
}

text_cursor.endEditBlock();
}

if (bool_at_bottom){
p_scroll_bar->setValue(p_scroll_bar->maximum());
}
}

/**
* Just insert new QTextBlock to the text.
* (in fact, adds new paragraph)
*/
void QPlainTextEdit_My::insertNL()
{
QScrollBar *p_scroll_bar = this->verticalScrollBar();
bool bool_at_bottom = (p_scroll_bar->value() == p_scroll_bar->maximum());

QTextCursor text_cursor = QTextCursor(this->document());
text_cursor.movePosition(QTextCursor::End);
text_cursor.insertBlock();

if (bool_at_bottom){
p_scroll_bar->setValue(p_scroll_bar->maximum());
}
}

I'm confused because in original code there are much more complicated calculations of atBottom:

const bool atBottom =  q->isVisible()
&& (control->blockBoundingRect(document->lastBlock()).bottom() - verticalOffset()
<= viewport->rect().bottom());

and needScroll:

if (atBottom) {
const bool needScroll = !centerOnScroll
|| control->blockBoundingRect(document->lastBlock()).bottom() - verticalOffset()
> viewport->rect().bottom();
if (needScroll)
vbar->setValue(vbar->maximum());
}

But my easy solution seems to work too.

QT C++ how to append text on last lıne and keep old lınes?

Did you check the documentation? The flag you want is QIODevice::Append.

How to append to QTextEdit without using the current paragraph style

I tried to reproduce in an MCVE what OP described:

// Qt header:
#include <QtWidgets>

// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QTextEdit qTextEdit;
qTextEdit.show();
// populate text editor
qTextEdit.append(QString::fromUtf8(
"<p>This is some text...</p>"
"<p style='color: red'>...followed by more red text...</p>"
"<p style='color: blue; font-weight: bold'>...followed by more fat blue text.</p>"));
// test QTextEdit::append() like described by OP:
qTextEdit.setTextCursor(QTextCursor(qTextEdit.document()->findBlockByNumber(1)));
qTextEdit.append("TEST. (Reproduce what OP described.)");
qTextEdit.append("<p>TEST. (A possible fix.)</p>");
// runtime loop
return app.exec();
}

Output:

snapshot of MCVE

So, a possible fix is to provide the text to append with mark-up.

If it's just raw text the simplest solution is to wrap it in "<p>" and "</p>".

Btw. if it's just raw text I would recommend some additional adjustments to make it proper HTML according to the Supported HTML Subset.
Namely, I would search and replace the usual XML meta characters like I did it e.g. in my answer to SO: qt plaintextedit change message color.

Qt 5.3 QPlainTextEdit implement scroll lock

My solution of this problem.

ui->plainTextEdit->insertPlainText("A");//this doesn't have auto scroll
if(global)//global is bool variable, if it is true, we autoscroll to the bottom
ui->plainTextEdit->verticalScrollBar()->setValue(ui->plainTextEdit->verticalScrollBar()->maximum());//we auto scroll it everytime

Or

QTextCursor cursor(ui->plainTextEdit->textCursor());
cursor.insertText("A");
if(global)
ui->plainTextEdit->verticalScrollBar()->setValue(ui->plainTextEdit->verticalScrollBar()->maximum());

Now we do next: when user hover(enter event) plainTextEdit we stop auto-scrolling, when user leave widget, we enable auto scrolling again. I did this by eventFilter, but I hope that you understanf my idea.

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if(obj==ui->plainTextEdit && (event->type()==QEvent::Enter || event->type()==QEvent::Leave))
{

if(event->type()==QEvent::Enter)//user move mouse on widget:stop auto-scrolling
global =false;
else
global =true;// leave event:enable auto-scrolling
ui->label->setText(event->type()==QEvent::Enter ? "Hovering" : "Not Hovering");//just show it to user, you can delete this line
}

return QObject::eventFilter(obj, event);
}

QTextEdit: scroll down automatically only if the scrollbar is at the bottom

I would make Scroll bar position listener, which will remember position on scrolling (and also check is it at the bottom or not).

Then, when new line is added, check is it at bottom, if is scroll down, if is somewhere else then scroll back to that position.

Check this QScrollBar, you can grab it from QTextEdit via horizontalScrollBar() and verticalScrollBar().


More concrete, I would connect slot with signal from QScrollBar - valueChanged(int value) and play with values as it is described here.

Prevent a QTextEdit widget from scrolling when there is a selection

Saving and restoring scrollbar position is quite correct and works perfectly. When document's length is increased, scrollbar's maximum value is increased. But its value is still equal to number of pixels above the viewport. So when you add contents to the document and set the same scrollbar value repeatadly, scrollbar's handle will move to the top, but the content will remain immobile.

It seems that you already know how to check if the user has selected some text. To check if the user has scrolled away from the bottom, you should simply compare vertical scrollbar's value with its maximum.



Related Topics



Leave a reply



Submit