QColor 到人类可读的字符串

QColor to human readable string

我在 QML 中有一个颜色对象,我认为它是 QColor 的一个实例,使用调试器我可以看到颜色对象包含:

    a                1
    b                0
    g                0
    hslHue           -1
    hslLightness     0
    hslSaturation    0
    hsvHue           -1
    hsvSaturation    0
    hsvValue         0
    r                0

有没有办法将其转换为人类可读的字符串,例如 Red?

不完全是我想要的,但是我向 CPP 添加了一个例程以使用 QColor 的 .name() 方法获取对象我将对象翻译回字符串,但它 returns RGB 值的十六进制表示法。

    QString myClass::colourName(QColor colr) {
        QString hex = colr.name().toLower();

        if ( hex.compare("#ff0000") == 0 ) {
            return "Red";
        } else if ( hex.compare("#00ff00") == 0 ) {
            return "Green";
        } else if ( hex.compare("#0000ff") == 0 ) {
            return "Blue";
        } 
        return hex;
    }

我的需求很简单,我只需要红色、绿色和蓝色。

颜色是一个很难的话题。 (如果您曾经将两个屏幕连接到您的计算机并尝试为它们配置相同的颜色配置文件,您就会明白我的意思。)

不过,我相信OP的用意是有道理的。如果 Qt 可以处理人类可读的颜色名称,为什么这不应该是可逆的?

起初,我看了一下手册 – QColor:

A color can be set by passing an RGB string (such as "#112233"), or an ARGB string (such as "#ff112233") or a color name (such as "blue"), to the setNamedColor() function. The color names are taken from the SVG 1.0 color names. The name() function returns the name of the color in the format "#RRGGBB".

关于 QColor::name() 的陈述听起来与 OP 所描述的完全一样。 为了完全说服自己,我做了一个 MCVE,但它没有改变任何东西。

好的。所以,这不是错误 - 这是一个功能。

如果 Qt 中缺少它,如何添加? Qt 似乎“知道”所有的名字。因此,如果无法以某种方式利用它,那将很烦人。

我点了一下文档。并发现例如在文档中。 QColor::setNamedColor a link for the table of the SVG Color Names.

所以,我考虑了一下,只是复制它。

我也找到了 SVG Colors。请注意,它被注释为

Since: Qt 5.14

并且仍然是 qt5-dev 文档的一部分。 (在撰写本文时)。

woboq.org,我偶然发现了 qtbase/src/gui/painting/qcolor.cpp 中颜色名称的负责源代码:

#ifndef QT_NO_COLORNAMES
/*
  CSS color names = SVG 1.0 color names + transparent (rgba(0,0,0,0))
*/
#ifdef rgb
#  undef rgb
#endif
#define rgb(r,g,b) (0xff000000 | (r << 16) |  (g << 8) | b)
static const struct RGBData {
    const char name[21];
    uint  value;
} rgbTbl[] = {
    { "aliceblue", rgb(240, 248, 255) },
    { "antiquewhite", rgb(250, 235, 215) },
    { "aqua", rgb( 0, 255, 255) },

    { "yellow", rgb(255, 255, 0) },
    { "yellowgreen", rgb(154, 205, 50) }
};
static const int rgbTblSize = sizeof(rgbTbl) / sizeof(RGBData);
#undef rgb

最后,我在 QColor::colorNames():

QStringList QColor::colorNames() [static]

Returns a QStringList containing the color names Qt knows about.

See also Predefined Colors.

有了所有颜色名称的列表(Qt 可以识别),很容易构建反向映射 table。

我做了一个 MCVE testQColorName.cc 来证明这一点:

#include <functional>
#include <unordered_map>

// Qt header:
#include <QtWidgets>

namespace std {

template <> struct hash<QColor> {
  size_t operator()(const QColor &color) const
  {
    return std::hash<QRgb>()(color.rgb());
  }
};

} // namespace std

typedef std::unordered_map<QColor, QString> ColorNameMap;

class ColorButton: public QPushButton {
  private:
    QColor _qColor;

  public:
    explicit ColorButton(
      const QString &text = QString(), const QColor &qColor = Qt::black,
      QWidget *pQParent = nullptr):
      QPushButton(text, pQParent)
    {
      setColor(qColor);
    }
    virtual ~ColorButton() = default;
    ColorButton(const ColorButton&) = delete;
    ColorButton& operator=(const ColorButton&) = delete;

    const QColor& color() const { return _qColor; }
    void setColor(const QColor &qColor)
    {
      _qColor = qColor;
      QFontMetrics qFontMetrics(font());
      const int h = qFontMetrics.height();
      QPixmap qPixmap(h, h);
      qPixmap.fill(_qColor);
      setIcon(qPixmap);
    }

    QColor chooseColor()
    {
      setColor(QColorDialog::getColor(_qColor, this, text()));
      return _qColor;
    }
};

// main application
int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // setup data
  const ColorNameMap qMapColorNames = []() {
    ColorNameMap qMapColorNames;
    const QStringList qColorNames = QColor::colorNames();
    for (const QString &qColorName : qColorNames) {
      qMapColorNames[QColor(qColorName)] = qColorName;
      qDebug() << qColorName;
    }
    return qMapColorNames;
  }();
  // setup GUI
  QWidget qWinMain;
  qWinMain.setWindowTitle(QString::fromUtf8("Test Color Name"));
  QHBoxLayout qHBox;
  QLabel qLblColor(QString::fromUtf8("Color:"));
  qHBox.addWidget(&qLblColor);
  QLineEdit qEditColor;
  qHBox.addWidget(&qEditColor, 1);
  ColorButton qBtnColor;
  qHBox.addWidget(&qBtnColor);
  qWinMain.setLayout(&qHBox);
  qWinMain.show();
  qEditColor.setText(qBtnColor.color().name());
  // install signal handlers
  QObject::connect(&qBtnColor, &QPushButton::clicked,
    [&]() {
      const QColor qColor = qBtnColor.chooseColor();
      const ColorNameMap::const_iterator iter = qMapColorNames.find(qColor);
      qEditColor.setText(iter != qMapColorNames.end() ? iter->second : qColor.name());
    });
  QObject::connect(&qEditColor, &QLineEdit::textEdited,
    [&](const QString &text) {
      const QColor qColor(text);
      qBtnColor.setColor(qColor);
    });
  // runtime loop
  return app.exec();
}

输出:

控制台输出:

Qt Version: 5.13.0
"aliceblue"
"antiquewhite"
"aqua"
"aquamarine"
"azure"
"beige"
"bisque"
"black"
"blanchedalmond"
"blue"
"blueviolet"
"brown"
"burlywood"
"cadetblue"
"chartreuse"
"chocolate"
"coral"
"cornflowerblue"
"cornsilk"
"crimson"
"cyan"
"darkblue"
"darkcyan"
"darkgoldenrod"
"darkgray"
"darkgreen"
"darkgrey"
"darkkhaki"
"darkmagenta"
"darkolivegreen"
"darkorange"
"darkorchid"
"darkred"
"darksalmon"
"darkseagreen"
"darkslateblue"
"darkslategray"
"darkslategrey"
"darkturquoise"
"darkviolet"
"deeppink"
"deepskyblue"
"dimgray"
"dimgrey"
"dodgerblue"
"firebrick"
"floralwhite"
"forestgreen"
"fuchsia"
"gainsboro"
"ghostwhite"
"gold"
"goldenrod"
"gray"
"green"
"greenyellow"
"grey"
"honeydew"
"hotpink"
"indianred"
"indigo"
"ivory"
"khaki"
"lavender"
"lavenderblush"
"lawngreen"
"lemonchiffon"
"lightblue"
"lightcoral"
"lightcyan"
"lightgoldenrodyellow"
"lightgray"
"lightgreen"
"lightgrey"
"lightpink"
"lightsalmon"
"lightseagreen"
"lightskyblue"
"lightslategray"
"lightslategrey"
"lightsteelblue"
"lightyellow"
"lime"
"limegreen"
"linen"
"magenta"
"maroon"
"mediumaquamarine"
"mediumblue"
"mediumorchid"
"mediumpurple"
"mediumseagreen"
"mediumslateblue"
"mediumspringgreen"
"mediumturquoise"
"mediumvioletred"
"midnightblue"
"mintcream"
"mistyrose"
"moccasin"
"navajowhite"
"navy"
"oldlace"
"olive"
"olivedrab"
"orange"
"orangered"
"orchid"
"palegoldenrod"
"palegreen"
"paleturquoise"
"palevioletred"
"papayawhip"
"peachpuff"
"peru"
"pink"
"plum"
"powderblue"
"purple"
"red"
"rosybrown"
"royalblue"
"saddlebrown"
"salmon"
"sandybrown"
"seagreen"
"seashell"
"sienna"
"silver"
"skyblue"
"slateblue"
"slategray"
"slategrey"
"snow"
"springgreen"
"steelblue"
"tan"
"teal"
"thistle"
"tomato"
"transparent"
"turquoise"
"violet"
"wheat"
"white"
"whitesmoke"
"yellow"
"yellowgreen"

注:

似乎 QColor 既不提供 less(允许用作 std::map 中的键)也不提供 std::hash 的特化(允许用作 [=22 中的键) =]). 所以,我必须 std::hash 自己专攻 QColor

SO: How to specialize std::hash::operator() for user-defined type in unordered containers?)

这并不像听起来那么容易,因为人类总是在上下文中看到颜色。所以,如果你想得到一个问题的答案 "how a human would name the color",那将很难。上下文不能从这样的分类中分离出来。甚至我们可以称之为 "no context" 的东西,例如在变黑的房间中深黑色背景上的均匀照明的颜色样本卡是一个上下文 - 事实上,这是一个不太可能的,并且可能违反直觉的 "isolated" 颜色呈现,同时有助于提供可重现的结果(在某种程度上人类完全同意颜色名称),不会很好地与颜色命名出现的常见场景保持一致 - 这大约是您可以想象的 "weird" 情况。我们不会在实验室环境中命名颜色,通常不会。所以我想也许这不是你想要的。

但看起来您可能不太关心它,而更愿意简单地在字符串和 RGB 三元组之间建立映射。您可以进行稀疏映射:使用颜色名称列表,例如this one,并使用 QMapQHash 等稀疏键索引容器执行最简单的 32 位 RGBA 到字符串查找。从 QColor::rgba() 获取密钥。这样,大多数颜色都没有分配的名称,但您执行的命名语义非常简单:它要么是 "manageable" 列表中的精确 RGB 三元组,要么不是。

或者,您可能希望选择 "this one is close enough" 的任意度量,并为一组 "nearby" 颜色分配相同的名称。这完全是任意的,因为在实践中,上下文决定了人们会考虑什么 "same color"。但对于某些用途,映射的简单性比代表人类视觉感知的映射更重要。

选择 "bunch together" "nearby" 颜色后,您可以使用一些前缀来消除歧义,例如#FF0000可以是red但是#EE0000可以显示为almost redclosest to red(这取决于距离度量的选择,RGB差异的长度矢量是一个可行的指标)。您可以选择将分类基于更有针对性的 属性,例如只要饱和度和值高于某个阈值,所有具有相同 QColor::hue() 的颜色都可以赋予相同的基本名称(#0100001 对任何人来说看起来都不像红色,即使它是 "pure" 红色)。这将取决于您需要名称的用途。如果你想要 "English" 字符串和 RGB 三元组之间的无损往返,除了获取一个大的颜色列表(例如获得 Pantone 系统的许可证)并使用它之外,你几乎无能为力,同时明确 "tainting" 种颜色不符合 "almost" 或 "like" 前缀。

还有一个问题是 RGB 三元组本身意义不大。某种经过校准的颜色 space 必须随之而来,例如 CIE RGBsRGB。不幸的是,QColor 不包含该信息。

为了好玩,你可以看看A Parametric Model for Computational Colour Naming :)