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);
}
}
}
A few things:
QTextBrowser::textCursor
returns a copy, so any modification is not applied to the documentQTextBrowser::setPosition
moves the cursor to an absolute position, therefore you are always moving to position 11 (int value to QTextCursor::End
). Use QTextBrowser::movePosition
insteadHere 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.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.