简体   繁体   中英

Track text insertion in QTextBrowser

I am making an application and using QTextBrowser to show messages. It should parse ascii colors, so my class ( say MessageBoard ) is inheriting from QTextBrowser . I can replace ascii color code and set MessageBoard 's text color according to the ascii code before insertion.

But there are many ways of inserting text into the QTextBrowser , so MessageBoard should be able to detect exactly where text is inserted and what is its length .

The problem is, QTextBrowser (through QTextEdit ) provides only textChanged signal but there is no way to get where changes happened.

So is there no way to get it or I missing something?

I've solved the problem but this was the issue I was having, (See main.cpp). MessageBoard.h

#ifndef MESSAGEBOARD_H
#define MESSAGEBOARD_H

#include <QTextBrowser>

#define AC_BLACK        "\u001b[30m"
#define AC_RED          "\u001b[31m"
#define AC_GREEN        "\u001b[32m"
#define AC_YELLOW       "\u001b[33m"
#define AC_BLUE         "\u001b[34m"
#define AC_MAGENTA      "\u001b[35m"
#define AC_CYAN         "\u001b[36m"
#define AC_WHITE        "\u001b[37m"
#define AC_RESET        "\u001b[0m"

using AsciiStringPos = std::pair<int /*index*/,int /*length*/>;

class MessageBoard : public QTextBrowser
{
public:
    MessageBoard(QWidget *parent = nullptr);
    void appendText(const QByteArray &text);
    ~MessageBoard();
private:
    std::pair<AsciiStringPos,QColor> find_ascii(const QByteArray &text, int starts_from);
private:
    std::map<QByteArray, QColor> m_colors;
};
#endif // MESSAGEBOARD_H

MessageBoard.cpp

#include "MessageBoard.h"
#include <QRegularExpression>
#include <climits>

MessageBoard::MessageBoard(QWidget *parent)
    : QTextBrowser(parent),
      m_colors({
{QByteArray(AC_BLACK) ,     Qt::black},
{QByteArray(AC_RED) ,       Qt::red},
{QByteArray(AC_GREEN) ,     Qt::green},
{QByteArray(AC_YELLOW) ,    Qt::yellow},
{QByteArray(AC_BLUE) ,      Qt::blue},
{QByteArray(AC_MAGENTA) ,   Qt::magenta},
{QByteArray(AC_CYAN) ,      Qt::cyan},
{QByteArray(AC_WHITE) ,     Qt::white}
               })
{
    m_colors.insert({QByteArray(AC_RESET) , textColor()});
}

void MessageBoard::appendText(const QByteArray &text)
{
    int index = 0;
    QTextCursor text_cursor = textCursor();
    text_cursor.movePosition(QTextCursor::End);
    auto res = find_ascii(text,0);
    while(res.first.first != -1)        //color string's index
    {
        text_cursor.insertText(text.mid(index,res.first.first - index));//append text before the color
        QTextCharFormat format;
        format.setForeground(res.second);   //set color to charformat
        text_cursor.setCharFormat(format);  //set charformat
        index = res.first.first         //color string started from
                + res.first.second;     //color string length
        res = find_ascii(text,index);   //find next color
    }
    text_cursor.insertText(text.mid(index));
}

std::pair<AsciiStringPos, QColor> MessageBoard::find_ascii(const QByteArray &text, int starts_from)
{
    QByteArray first_color;
    int min_index = INT_MAX;
    for(const auto &p : m_colors)
    {
        int index = text.indexOf(p.first,starts_from);
        if(index != -1 && min_index > index)
        {
            min_index = index;
            first_color = p.first;
        }
    }
    if(first_color.isNull())
        return {{-1,0},m_colors[QByteArray(AC_RESET)]};
    else
        return {{min_index,first_color.length()},m_colors[first_color]};
}

MessageBoard::~MessageBoard()
{
}

main.cpp

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MessageBoard w;
    //appendText is manually created, so I can parse text before inserting.
    w.appendText(AC_GREEN "This is written with " AC_RED " Ascii " AC_GREEN " escaped words." AC_RESET);
    //append, can't do the same because I don't know the location where it was inserted.
    w.append(AC_MAGENTA "This won't be written in magenta.");
    w.appendText(AC_CYAN "This will be written in cyan" AC_RESET);
    w.zoomIn(5);
    w.show();
    return a.exec();
}

Output Image

I've solved it. I connected the QDocument 's contentsChange signal with MessageBoard2 s textInserted slot. Whenever any text is inserted, I copy them and remove it from the document, then I parse its ascii color codes and set the colors according to the code. And then I insert text, change text color, insert text, I do it recursively through a loop. Here are my codes

MessageBoard2.h #ifndef MESSAGEBOARD2_H #define MESSAGEBOARD2_H

#include <QTextBrowser>

#define AC_BLACK        "\u001b[30m"
#define AC_RED          "\u001b[31m"
#define AC_GREEN        "\u001b[32m"
#define AC_YELLOW       "\u001b[33m"
#define AC_BLUE         "\u001b[34m"
#define AC_MAGENTA      "\u001b[35m"
#define AC_CYAN         "\u001b[36m"
#define AC_WHITE        "\u001b[37m"
#define AC_RESET        "\u001b[0m"

class MessageBoard2 : public QTextBrowser
{
private:
    class SearchResults{
    private:
        struct result_t{
            std::size_t index;
            std::size_t length;
            QColor color;
        };
        std::vector<result_t> vec;
        std::size_t iterator;
    public:
        SearchResults() : iterator(0){}
        bool hasMatch() const {return !vec.empty();}
        bool hasNext() const {return iterator < vec.size();}
        const result_t &next() {return vec[iterator++];}
        friend class ::MessageBoard2;
    };
public:
    MessageBoard2(QWidget *parent = nullptr);
    ~MessageBoard2();
    SearchResults find_ascii(const QString &text, int starts_from);
private slots:
    void textInserted(int pos, int sub, int add);
    void parseAndInsert(const QString &text);
private:
    bool m_should_react;    //prevent recursive calls
    QTextDocument *m_document;
    std::map<QString, QColor> m_colors;
};

#endif // MESSAGEBOARD2_H

MessageBoard2.cpp

#include "MessageBoard2.h"
#include <QRegularExpressionMatch>
#include <QTextBlock>

MessageBoard2::MessageBoard2(QWidget *parent) :
    QTextBrowser(parent),
    m_colors({
    {QStringLiteral(AC_BLACK) ,     Qt::black},
    {QStringLiteral(AC_RED) ,       Qt::red},
    {QStringLiteral(AC_GREEN) ,     Qt::green},
    {QStringLiteral(AC_YELLOW) ,    Qt::yellow},
    {QStringLiteral(AC_BLUE) ,      Qt::blue},
    {QStringLiteral(AC_MAGENTA) ,   Qt::magenta},
    {QStringLiteral(AC_CYAN) ,      Qt::cyan},
    {QStringLiteral(AC_WHITE) ,     Qt::white}
           }),
    m_should_react(true)
{
    m_colors.insert({QStringLiteral(AC_RESET),textColor()});
    m_document = document();
    connect(m_document,&QTextDocument::contentsChange,this,&MessageBoard2::textInserted);
}

MessageBoard2::~MessageBoard2()
{

}

void MessageBoard2::textInserted(int pos, int sub, int add)
{
    if(m_should_react && add > 0)
    {
        QTextCursor text_cursor = textCursor();
        text_cursor.setPosition(pos);
        text_cursor.setPosition(pos+add,QTextCursor::KeepAnchor);
        QString text = text_cursor.selectedText();
        m_should_react = false;
        text_cursor.removeSelectedText();
        setTextCursor(text_cursor);
        parseAndInsert(text);
        m_should_react = true;
    }
}

void MessageBoard2::parseAndInsert(const QString &text)
{
    int index = 0;
    QTextCursor text_cursor = textCursor();
    text_cursor.movePosition(QTextCursor::End);
    SearchResults results = find_ascii(text,0);
    while(results.hasNext())        //color string's index
    {
        const SearchResults::result_t &result = results.next();
        text_cursor.insertText(text.mid(index,result.index - index));//append text before the color
        QTextCharFormat format;
        format.setForeground(result.color);
        text_cursor.setCharFormat(format);
        index = result.index         //color string started from
                + result.length;     //color string length
    }
    text_cursor.insertText(text.mid(index));
}

MessageBoard2::SearchResults MessageBoard2::find_ascii(const QString &text, int starts_from)
{
    QRegularExpressionMatchIterator itr = QRegularExpression("\u001b\\[\\d+m").globalMatch(text);
    SearchResults results;
    while(itr.hasNext())
    {
        QRegularExpressionMatch match = itr.next();
        auto it = m_colors.find(match.captured());
        if(it != m_colors.end())
        {
            SearchResults::result_t result;
            result.index = match.capturedStart();
            result.length = match.capturedLength();
            result.color = it->second;
            results.vec.push_back(result);
        }
        std::cout << std::endl;
    }
    return results;
}

main.cpp

#include "MessageBoard2.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MessageBoard2 w;
    //execpt setPlainText function, I can parse the strings and set color accordingly.
    w.textCursor().insertText(AC_GREEN "This is written with " AC_RED " Ascii " AC_GREEN " escaped words." AC_RESET);
    w.insertPlainText(AC_MAGENTA "This will be written in magenta.");
    w.append(AC_CYAN "This will be written in cyan" AC_RESET);
    w.zoomIn(5);
    w.show();
    return a.exec();
}

Output Image

If I understand your request well, I guess you may want to use this signal https://doc.qt.io/qt-5/qtextdocument.html#contentsChange

You will get access to QTextDocument with this https://doc.qt.io/qt-5/qtextedit.html#document-prop

我希望当我们收到信号“textChanged”时,可以使用“ toplainText() ”函数来获取文本。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM