在使用QString的基于文本的Qt小部件中,格式化属性的灵活性
在一个包含如下代码的Qt应用程序中:
QLabel lab;
lab.setText(QString::fromLatin1("<qt>The default value is: <code>YES</code></qt>"));
并且希望将单词 YES 显示为红色,例如,可以把它改为:
QLabel lab;
lab.setText(QString::fromLatin1("<qt>The default value is: <code style='color:#ff0000'>YES</code></qt>"));
但是当代码中经常出现 <code> 时,这种做法就显得相当繁琐,也不太灵活。
当然也有一些可能的做法,例如把字符串部分拼接起来,或者自动替换所有出现的 <code>,但这些都不太好,尤其是在文本由第三方提供时(例如翻译的情况)。
我当然也看到过关于 setStyleSheet 的东西,以及一个 qss 文件,尽管我还没能让它工作。
如何实现呢?
解决方案
I hope @musicamante won't mind if I post his slightly modified version in C++. This code is based on @musicamante's version:
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QTextDocument>
#include <QSyntaxHighlighter>
class MyHighlighter : public QSyntaxHighlighter
{
private:
QMap<QString, QColor> codeColors = {
{"YES", QColor("green")},
{"NO", QColor("red")}
};
public:
using QSyntaxHighlighter::QSyntaxHighlighter;
protected:
void highlightBlock(const QString &text)override{
auto tc = QTextCursor(document());
auto blockStart = currentBlock().position();
auto fixed = QFontDatabase::systemFont(QFontDatabase::FixedFont).family();
// asKeyValueRange() is not available in Qt5, in this
// version you can use iteration over the list of keys
// QMap::keys(), and get the value QMap::value(key)
for(const auto&[key, color] : codeColors.asKeyValueRange()){
if(!text.contains(key))
continue;
auto sz = key.length();
auto pos = 0;
while(true){
pos = text.indexOf(key, pos);
if(pos < 0)
break;
tc.setPosition(blockStart + pos + sz);
if (tc.charFormat().fontFamily() == fixed){
auto fmt = QTextCharFormat();
fmt.setForeground(color);
fmt.setFontWeight(QFont::Bold);
setFormat(pos, sz, fmt);
}
pos += sz;
}
}
}
};
class CustomLabel : public QLabel
{
private:
MyHighlighter *highlighter{nullptr};
public:
using QLabel::QLabel;
CustomLabel(const QString &text,
bool highlighted = false,
QWidget *parent = nullptr,
Qt::WindowFlags f = Qt::WindowFlags())
: QLabel(" ", parent, f){
setTextFormat(Qt::RichText);
if(auto doc = findChild<QTextDocument*>()){
setHighlighted(highlighted);
auto fixed = QFontDatabase::systemFont(QFontDatabase::FixedFont).family();
auto qss = QString(
"code { color: blue; }"
"code#yes, yes { color: green; }"
"yes { font-family: %1; }"
"code#no, no { color: red; }"
"no { font-family: %1; }"
).arg(fixed);
doc->setDefaultStyleSheet(qss);
setText(text.isEmpty() ? " " : text);
}
}
void setHighlighted(bool st){
if(highlighter && st)
return;
delete highlighter;
highlighter = nullptr;
if(auto doc = findChild<QTextDocument*>(); st)
highlighter = new MyHighlighter(doc);
}
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto form = new QWidget;
auto layout = new QVBoxLayout;
form->setLayout(layout);
setCentralWidget(form);
layout->addWidget(new CustomLabel("Normal text"));
layout->addWidget(new CustomLabel("Some <code>code tag</code>"));
layout->addWidget(new CustomLabel("A <code id=\"yes\">code tag with \"yes\" id</code>"));
layout->addWidget(new CustomLabel("A <yes>yes tag</yes>"));
layout->addWidget(new CustomLabel("A <no>no tag</no>"));
layout->addSpacing(20);
layout->addWidget(new CustomLabel("A <code>code tag with break,<br>YES and NO</code>"));
auto btnHighlight = new QPushButton("Highlight");
btnHighlight->setCheckable(true);
layout->addWidget(btnHighlight);
connect(btnHighlight, &QPushButton::toggled, this, [this](bool checked){
const auto labels = findChildren<QLabel*>();
for(const auto &label : labels)
if(const auto &l = dynamic_cast<CustomLabel*>(label))
l->setHighlighted(checked);
});
}
MainWindow::~MainWindow() {}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。