QPlainTextEdit::appendPlainText(QString)
appends a newline to my text widget. QPlainTextEdit::insertPlainText(QString)
doesn't seem affected by setCurrentCharFormat()
.
Is there a way to append text while listening to the current QTextCharFormat
without inserting a newline
I have a "terminal" style widget which gets text from stdout from a child process and displays it in a QPlainTextEdit
.
Before I had colour content, I could simply do this:
void ProcessWidget::appendText(QString text)
{
m_textedit->appendPlainText(text);
}
Colours appear in the text using the escape character '\033'
followed by a colour. I can detect the colour and set the pallette appropriately:
void ProcessWidget::appendText(QString text)
{
Qt::GlobalColor colour = GetColour(text);
QTextCharFormat tf = m_textedit->currentCharFormat();
tf.setForeground(QBrush(colour));
m_textedit->setCurrentCharFormat(tf);
m_textedit->appendPlainText(text);
}
This works if there is only one colour per line, but if my colour changes half-way through each line, then I need to be a bit crazier:
std::map<QString, Qt::GlobalColor> m_colours;
QPlainTextEdit* m_textedit;
...
void ProcessWidget::AppendText(QString text)
{
while(true)
{
int iColour = text.indexOf('\033');
if (iColour == -1)
break;
QString pretext = text.mid(0, iColour);
if (!pretext.isEmpty())
{
m_textedit->appendPlainText(pretext);
}
text.remove(0, iColour);
for (auto pair : m_colours)
{
if ( text.startsWith(pair.first) )
{
QTextCharFormat tf = m_textedit->currentCharFormat();
tf.setForeground(QBrush(pair.second));
m_textedit->setCurrentCharFormat(tf);
text.remove(0, pair.first.size());
break;
}
}
}
if (!text.isEmpty())
{
m_textedit->appendPlainText(text);
}
}
However, because I use appendPlainText()
, each new colour found gives me a new line.
I've tried replacing appendPlainText()
with:
m_textedit->moveCursor (QTextCursor::End);
m_textedit->insertPlainText(text);
m_textedit->moveCursor (QTextCursor::End);
then adding '\n'
at the end. But in that case, I don't get any colours anymore. I've also tried appendHtml()
but that doesn't seem to make a difference.
For these cases the simplest thing is to use HTML and insert the tags: <font color = "..."> </ font>
Example:
#include <QApplication>
#include <QDateTime>
#include <QPlainTextEdit>
#include <QTimer>
#include <QVBoxLayout>
#include <QWidget>
const std::map<QString, QColor> m_colours {{"red", QColor(Qt::red)},
{"blue", QColor(Qt::blue)},
{"green", QColor(Qt::green)}
};
class ProcessWidget: public QWidget{
Q_OBJECT
public:
ProcessWidget(QWidget *parent=nullptr):
QWidget(parent),
lay{this}
{
m_textedit.setReadOnly(true);
lay.addWidget(&m_textedit);
}
public slots:
void appendText(const QString & text){
QString html{text};
int j = 0;
bool start = true;
QString textColor;
while ((j = html.indexOf(QChar('\033'), j)) != -1) {
html.remove(j, 1);
QColor color;
for(auto & pair : m_colours){
if(html.mid(j).startsWith(pair.first)){
color = pair.second;
html.remove(j, pair.first.length());
}
}
if(start){
textColor = QString("<font color=\"%1\">").arg(color.name());
start = false;
}
else
textColor = QString("</font><font color=\"%1\">").arg(color.name());
html.insert(j, textColor);
j += 1+textColor.length();
}
html.append("</font>");
m_textedit.appendHtml(html);
}
private:
QPlainTextEdit m_textedit;
QVBoxLayout lay;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ProcessWidget w;
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&w](){
QString text = QString("\033redDateTime: %1 \033blueDate:%2 \033greenTime:%3")
.arg(QDateTime::currentDateTime().toString())
.arg(QDate().currentDate().toString())
.arg(QTime::currentTime().toString());
w.appendText(text);
});
timer.start(1000);
w.show();
return a.exec();
}
#include "main.moc"