Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Conversion from QFlags<> to QVariant could be easier



  • Consider the following code snippet from a run-of-the-mill implementation of the data() function by a class which inherits QAbstractItemModel:

    QVariant MyModel::data(const QModelIndex &index, int role) const
    {
      QVariant retval;
      // this doesn't compile ... I am told by the compiler: 
      // "error: ‘template<class T> class QFlags’ used without template parameters"
      // QFlags::Int i = 0;
      // ...but this does:
      uint i = 0;
      if (index.isValid()) {
        int row = index.row();
        int col = index.column();
        switch (role) {
        case Qt::TextAlignmentRole:
          switch(col) {
          case 0:
            // this doesn't compile: 
            //   "error: no match for ‘operator=’ 
            //   (operand types are ‘QVariant’ and ‘QFlags<Qt::AlignmentFlag>’)":
            // retval = Qt::AlignLeft | Qt::AlignVCenter;
            // but this compiles:
            i = Qt::AlignLeft | Qt::AlignVCenter;
            retval = i;
            break;
          default:
            i = Qt::AlignHCenter | Qt::AlignVCenter;
            retval = i;
            break;
          }
          break;
        case Qt::DisplayRole:
        // etc.
    

    Since this idiom is used in so many places, I wouldn't think that it would be necessary to store a QFlags<> value instantiated with any native Qt types in a temporary variable just so that I can write it to a QVariant; there should be a QFlags::operator QVariant() or something.

    Now I noticed that there is a typedef QFlags::Int which is supposed to resolve either to signed or unsigned int depending on how it is implemented. But I haven't figured out how to use it, so I take my chances with unsigned int and hope that I never have to compare it with a signed value anywhere.

    Is there a better way to do this?

    (EDIT: OK, I need to define i like this, then it will be the correct type:

    QFlags<Qt::AlignmentFlag>::Int i = 0;
    

    but I am still stuck with an intermediate variable I really shouldn't need...


  • Moderators

    What about QVariant::fromValue(Qt::AlignHCenter | Qt::AlignVCenter)?



  • @sierdzio : It compiles, but it doesn't seem to work correctly ... everything is left-aligned and vertically top-aligned when I set the return value like this. When I cast it to an integer before setting the QVariant return value, it works correctly. I really don't know what is happening; however, I suspect that the Qt::AlignmentFlag just doesn't have any metadata associated with it which might tell QVariant how to convert it (either directly or by using QVariant::fromValue()). The reverse conversion using the templated QVariant::value<>() function also fails to convert it correctly.


  • Qt Champions 2019

    If nothing helps why not just write your own toVariant(QFlags<>) - function?


  • Moderators

    Don't assume ints or uints. There's a typedef for that and conversion operators.
    It's a bit of a mouthful but the "correct" way to convert flags to and from variant is:

    Qt::Alignment original = Qt::AlignLeft | Qt::AlignVCenter;
    
    QVariant v = static_cast<Qt::Alignment::Int>(original);
    
    Qt::Alignment retrieved = static_cast<Qt::Alignment>(v.value<Qt::Alignment::Int>());
    

    so in your case it would look like this in the data() method:

    retval = static_cast<Qt::Alignment::Int>(Qt::AlignLeft | Qt::AlignVCenter);
    

    and then on the receiving end:

    Qt::Alignment a = static_cast<Qt::Alignment>(index.data(role).value<Qt::Alignment::Int>());
    


  • @chris-kawa : Thank you for the detailed explanation. It is a little less typing to write

    Qt::Alignment::Int
    

    instead of

    QFlags<Qt::AlignmentFlag>::Int
    

    Maybe I will take Christian Ehrlicher's advice and write the toVariant() function? It sounds like a good idea. I was just wondering why this isn't already built into the QVariant class?


  • Moderators

    @robert-hairgrove said in Conversion from QFlags<> to QVariant could be easier:

    I was just wondering why this isn't already built into the QVariant class?

    QFlags is a template so QVariant would have to support all possible specializations, for all possible enum types, which is unrealistic.
    It could be supported on the QFlags side with something like toVariant() and fromVariant(), but if you take a step back this doesn't scale well, because then why not toString, toDouble, toWhatever?

    The best place for this IMO would be outside both QVariant and QFlags, with a function template that would basically do the casts I mentioned. My guess is it's not in Qt because nobody felt like this was big enough of a deal and it wouldn't even be that much shorter either.

    Anyway, those are basically one-liners, so you can easily provide them yourself if you feel like it.



  • @chris-kawa Thank you.


Log in to reply