在使用QString的基于文本的Qt小部件中,格式化属性的灵活性

编程语言 2026-07-08

在一个包含如下代码的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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章