PDA

View Full Version : Custom type registration on QVariant & toString()



zickedi
3rd March 2012, 19:18
I have declared my custom type with Q_DECLARE_METATYPE(MyStruct) and want to call toString() on the corresponding QVariant. But it doesn't work. Is it possible?



struct MyStruct {
public:
MyStruct() {}
MyStruct(const MyStruct& struct)
: name( struct.name ) {
}
~MyStruct() {}
QString toString() const { return name; }

QString name;
};

Q_DECLARE_METATYPE(MyStruct)

int main() {
MyStruct struct;
struct.name = "Test";
QVariant v = QVariant::fromValue(struct);
bool result = v.isValid(); // = true
MyStruct struct2 = v.value<MyStruct>(); // OK
QString output = v.toString(); // fails!!!
return 0;
}

wysota
3rd March 2012, 19:19
No, it's not possible. You need to cast to your type first and then call QString on the resulting variable.

zickedi
3rd March 2012, 20:06
Mh, not good. I want it to use in a custom ListModel (e.g. QStringListModel) for a widget and want my model to be reusable.
Are there any other possiblities implementing an internal QVariant + toString()? I just need a toString-Method for displaying purposes. I thought QVariant would be right solution for this, but obviously I can just use its "container" function. The internal Qt types like QString, qint etc. are overloaded by QVariant toString() implementation.

wysota
3rd March 2012, 21:22
Could you explain with more details what do you mean about the model? Maybe it's enough to implement a custom delegate that is aware of your data structure?

d_stranz
3rd March 2012, 22:18
No, it's not possible. You need to cast to your type first and then call QString on the resulting variable.

Why can't he add a cast operator to his MyStruct class?



struct MyStruct
{
// ...
operator const QString & () const { return name; }
}


Wouldn't that end up getting invoked by QVariant:toString()? He might have to declare his variant as a "String" type, but it should then work if I understand the docs correctly.

zickedi
3rd March 2012, 23:44
operator const QString & () const { return name; }
Doesn't work either.

My intention was to build a model like QStringListModel and save information how often an item is added. This additional information should not be in the model but in the registered type (MyStruct).
Pseudocode:


class QVariantListModel : QAbstractListModel {
bool setData(...);
QVariantList lst;
};

int main() {
MyStruct struct;
struct.name = "Test";
struct.counter += 1;
QVariantListModel model = new QVariantListModel();
model->setData(QModelIndex(), QVariant::fromValue(struct)); // Add data to model.
return 0;
}


Possible internal list should look like this:
lst = { ["entry1",2], ["entry2", 5] }

wysota
4th March 2012, 00:15
Why can't he add a cast operator to his MyStruct class?
Because he wants to convert to QString from QVariant and not his own class.


Wouldn't that end up getting invoked by QVariant:toString()?
No. QVariant::toString() calls QVariant::canConvert<QString>() and that returns false for custom types.


He might have to declare his variant as a "String" type
This is not possible, each type gets its own meta type id.


My intention was to build a model like QStringListModel and save information how often an item is added. This additional information should not be in the model but in the registered type (MyStruct).
Pseudocode:


class QVariantListModel : QAbstractListModel {
bool setData(...);
QVariantList lst;
};

int main() {
MyStruct struct;
struct.name = "Test";
struct.counter += 1;
QVariantListModel model = new QVariantListModel();
model->setData(QModelIndex(), QVariant::fromValue(struct)); // Add data to model.
return 0;
}


Possible internal list should look like this:
lst = { ["entry1",2], ["entry2", 5] }

I think you are approaching the problem from the wrong side. I think you want something like this (not tested):


class MyModel : public QAbstractTableModel {
public:
MyModel(QObject *parent = 0) : QAbstractListModel(parent) {}
int rowCount(const QModelIndex &parent = QModelIndex()) const { return parent.isValid() ? 0 : m_data.count(); }
int columnCount(const QModelIndex &parent = QModelIndex()) const { return parent.isValid() ? 0 : 2; }
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const {
if(index.row() < 0 || index.row() >= m_data.count()) return QVariant();
const MyStruct &item = m_data.at(index.row());
const int c = index.column();
switch(role) {
case Qt::DisplayRole:
case Qt::EditRole:
if(c==0) return item.name;
if(c==1) return item.count;

break;
}
return QVariant();
}
// follow with insertRows(), removeRows() and setData() implementations to make the model read-write
private:
QList<MyStruct> m_data;
};

d_stranz
4th March 2012, 01:20
I agree that it does seem like the OP realy should be creating amodel such as your example.

But in the general case of a custom QVariant type, from the docs:


QString QVariant::toString () const

Returns the variant as a QString if the variant has type() String, Bool, ByteArray, Char, Date, DateTime, Double, Int, LongLong, StringList, Time, UInt, or ULongLong; otherwise returns an empty string.

If QVariant can return a QString for all of these built-in and Qt types, it seem inconsistent that there isn't any way to define that conversion for a custom metatype. Is there some Qt design reason why this is not permitted?

wysota
4th March 2012, 01:30
The reason is that for types built into Qt one can provide static (as in known at the time Qt is compiled, not as in having a "static" modifier) code that performs the conversion. For custom types, the data is stored internally as a void* pointer treating the data itself as a black box. The type may not even be a class, no methods of the type are exposed to QVariant. The code created for such type has to be very generic to work for different kind of data. There is simply no sane way of providing a type-safe conversion such as this (I'm not counting providing a bunch of template overloads for different types as a sane way), at least I cannot think of any. For most types it wouldn't even make any sense to convert them to a string.

zickedi
4th March 2012, 01:32
Ok, thanks for the tips. I think I have to implement it this way.
I always thought of something like a class "QVariantListModel" and that class storing and displaying my custom type (displaying just my attribute name). Now I know why there is only a QStringListModel ;-)

wysota
4th March 2012, 01:34
No, there is QStandardItemModel if you really need it. QStringListModel stores simple strings and nothing more.