在 QTextBrowser 中跟踪文本插入
Track text insertion in QTextBrowser
我正在制作一个应用程序并使用 QTextBrowser
来显示消息。它应该解析 ascii 颜色,所以我的 class (say MessageBoard
) 继承自 QTextBrowser
。我可以替换ascii色码,插入前根据ascii码设置MessageBoard
的文字颜色
但是有很多方法可以将文本插入 QTextBrowser
,所以 MessageBoard
应该能够准确检测到文本插入的位置以及它的长度.
问题是,QTextBrowser
(通过 QTextEdit
)仅提供 textChanged 信号,但无法获取更改发生的位置。
所以是没有办法得到它还是我遗漏了什么?
我已经解决了问题,但这是我遇到的问题(参见 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
如果我很好地理解你的要求,我想你可能想使用这个信号https://doc.qt.io/qt-5/qtextdocument.html#contentsChange
您将通过此 https://doc.qt.io/qt-5/qtextedit.html#document-prop
访问 QTextDocument
我已经解决了。我将 QDocument
的 contentsChange
信号与 MessageBoard2
的 textInserted
插槽相连。每当插入任何文本时,我都会复制它们并将其从文档中删除,然后解析其 ascii 颜色代码并根据代码设置颜色。然后我插入文本,更改文本颜色,插入文本,我通过一个循环递归地完成它。这是我的代码
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
我正在制作一个应用程序并使用 QTextBrowser
来显示消息。它应该解析 ascii 颜色,所以我的 class (say MessageBoard
) 继承自 QTextBrowser
。我可以替换ascii色码,插入前根据ascii码设置MessageBoard
的文字颜色
但是有很多方法可以将文本插入 QTextBrowser
,所以 MessageBoard
应该能够准确检测到文本插入的位置以及它的长度.
问题是,QTextBrowser
(通过 QTextEdit
)仅提供 textChanged 信号,但无法获取更改发生的位置。
所以是没有办法得到它还是我遗漏了什么?
我已经解决了问题,但这是我遇到的问题(参见 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
如果我很好地理解你的要求,我想你可能想使用这个信号https://doc.qt.io/qt-5/qtextdocument.html#contentsChange
您将通过此 https://doc.qt.io/qt-5/qtextedit.html#document-prop
访问QTextDocument
我已经解决了。我将 QDocument
的 contentsChange
信号与 MessageBoard2
的 textInserted
插槽相连。每当插入任何文本时,我都会复制它们并将其从文档中删除,然后解析其 ascii 颜色代码并根据代码设置颜色。然后我插入文本,更改文本颜色,插入文本,我通过一个循环递归地完成它。这是我的代码
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