1

I have a colour object in QML which I think is an instance of QColor, using the debugger I can see the colour object contains:

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

Is there a way to translate this into a human readable string, e.g Red ?

SPlatten
  • 5,334
  • 11
  • 57
  • 128
  • This highly depends on what red is for you. Is `1, 0, 0` red? Surely. Is `1, 0.2, 0.2` still red? Can `0.6, 0, 0` be considered red? You could make a lookup-table for some known colors (maybe there is some library for this?), but usually you can only print the r, g and b value or convert the color to a hex-value as in your answer. Only few colors actually have a name. – Lukas-T Mar 18 '20 at 11:49
  • @churill, in the case of this application its very simple and there are only three absolute colours to handle which I've now done. ".name()" returns a hex value formatted as #RRGGBB, where RR = Hex red component, GG = green and BB = Blue. – SPlatten Mar 18 '20 at 11:52
  • If it's only three colors you could easily convert them their respective names, if this would be more usefull. – Lukas-T Mar 18 '20 at 15:01
  • @churill, please explain. – SPlatten Mar 18 '20 at 15:39
  • 1
    If you have only three colors, you could do something like `if(r == 1 && g == 0 && b == 0) return "red" else if (r == 0 && g == 1 && b == 0)` return "green" else if ...` and so on.In case you want human readable names for thos 3 colors. – Lukas-T Mar 18 '20 at 16:06

3 Answers3

3

Colors are a difficult topic. (If you ever connected two screens to your computer and tried to configure them for identical color profiles you know what I mean.)

Nevertheless, I believe the intention of OP is reasonable. If Qt can process human-readable color names why shouldn't this reversible?

At first, I had a look into the manual – 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".

The statement about QColor::name() sounds precisely like what OP described. To convince myself completely, I made an MCVE but it didn't change anything.

OK. So, it's not a bug – it's a feature.

If it's missing in Qt how this could be added? Qt seems to “know” all the names. So, it would be annoying if this cannot be exploited somehow.

I clicked a bit through the doc. and found e.g. in the doc. of QColor::setNamedColor a link for the table of the SVG Color Names.

So, I considered for a second just to copy it.

I also found the SVG Colors. Please, note, that it's remarked with

Since: Qt 5.14

and still part of the qt5-dev doc. (at the time of this writing).

On woboq.org, I stumbled into the responsible source code for the color names in 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

And, finally, I ended up in QColor::colorNames():

QStringList QColor::colorNames() [static]

Returns a QStringList containing the color names Qt knows about.

See also Predefined Colors.

With a list of all color names (which Qt can recognize) it's easy to build a reverse mapping table.

I made an MCVE testQColorName.cc to demonstrate this:

#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();
}

Output:

Snapshot of testQColorName (after text input)

Snapshot of testQColorName (after chosing color #ff0000 with color button)

Console Output:

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"

Note:

It seems that QColor does provide neither a less (to allow usage as key in std::map) nor a specialization of std::hash (to allow usage as key in std::unordered_map). So, I had to specialize std::hash myself for QColor.

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

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
0

Not exactly what I was after, however I added a routine to the CPP to take the object an using the .name() method of QColor I translated the object back into a string, but it returns a hex notation of the RGB value.

    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;
    }

My need was simple I only needed Red, Green and Blue.

SPlatten
  • 5,334
  • 11
  • 57
  • 128
0

This is not as easy as it sounds, because humans always see colors in a context. So, if you want to get the answer to a question "how a human would name the color", it's going to be hard. The context cannot be separated from such a classification. Even something that we may call "no context", e.g. a uniformly illuminated color sample card on a deep black background in a blackened room is a context - an unlikely one, in fact, and perhaps counterintuitively such an "isolated" color presentation, while useful in giving reproducible results (to the extent humans agree on color names at all), is not going to align well with the common scenarios that color naming comes up in - it's about as "weird" a situation as you can imagine. We don't name colors in a lab setting, not normally. So I presume that perhaps that's not what you're after.

But it looks that perhaps you don't care much about that, but would rather simply have a mapping between a string and an RGB triple. You can go for a sparse mapping: use a list of color names, e.g. this one, and do the most trivial 32-bit RGBA-to-string lookup using a sparse-key-indexed container like QMap or QHash. Get the key from QColor::rgba(). This way, most colors won't have an assigned name, but the semantics of the naming you perform are very simple: either it's an exact RGB triple from a "manageable" list, or it's not.

Alternatively, you may want to choose some arbitrary metric of "this one is close enough", and assign the same name for a bunch of "nearby" colors. This is entirely arbitrary, because in practice context dictates what a human would consider "same color". But for some uses the simplicity of the mapping is more important than the mapping representing human visual perception.

Once you choose to "bunch together" "nearby" colors, you can disambiguate with some prefixing, e.g. #FF0000 can be red but #EE0000 can be displayed as almost red or closest to red (this depends on the choice of a distance metric, the length of the RGB difference vector is one viable metric). You can choose to base the classification on a more targeted property, e.g. all colors with the same QColor::hue() may be given the same base name as long as the saturation and value are above some threshold (#0100001 doesn't look much like red to anyone, even though it's "pure" red). This will depend on what you need the name for. If you want a lossless roundtrip between the "English" strings and the RGB triples, there's little you can do, other than taking a large color list (e.g. get a license for the Pantone system) and use that, while explicitly "tainting" colors that don't fit with an "almost" or "like" prefix.

Yet another problem is that RGB triples by themselves don't quite mean much. Some sort of a calibrated color space must go with that, like CIE RGB or sRGB. Unfortunately, QColor doesn't carry that information.

For some fun, you may have a look at A Parametric Model for Computational Colour Naming :)

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313