Qt how to extract custom data pointer from QVariant without knowing the internal type - qt

I have a QVariantMap with data. Inside the QVariant I can store custom class declared and registered in meta-system.
I know does exists QVariant::value<XXX>() but since i don't know a-priori what XXX is I cannot do that.
So I tried to convert to QObject* both with QVariant::value<QObject*> and qvariant_cast but it seems to hold a void pointer:
My attempt:
MyClass* obj = new MyClass();
QVariant variant = QVariant::fromValue<MyClass*>(obj); // it works;
qDebug() << variant; // Qvariant(MyClass*,)
QObject* obj2 = variant.value<QObject*>();
qDebug() << obj2; // QObject(0x0)

The OP mentions it works with:
QVariant variant(QVariant::fromValue(static_cast<void*>(obj)));
auto test = static_cast<QObject*>(variant.value<void*>());
qDebug() << obj2; // MyClass(0x8a99838
As specified in this answer

Related

Converting QMap<QString, QString> to Json string results empty

I have a function defined and used as this:
// usage:
QMap<QString, QString> map = ...;
foo(map);
// defination:
QString stringMapToJson(const QMap<QString, QString>& arg) {
QVariant v = QVariant::fromValue(arg);
JsonDocument doc = QJsonDocument::fromVariant(v);
...
}
Then I realized v is empty.
Is there a method to convert QMap<String, QString> to QMap<String, QVariant>, so above v could be valid?
Why above v is empty? I read people were saying QVariant and qMetaData, I don't understand given the following valid, why QString have a qMetaData problem:
QString s = "";
QVariant v = s;
(A Java programmer starts her pleasant C++ journey.)
Thanks.
There are 2 ways to do this. The first is to convert your map to a QMap<QString, QVariant> like you mentioned:
QByteArray stringMapToJson1(const QMap<QString, QString>& arg)
{
QVariantMap vmap;
for(auto it = arg.cbegin(); it != arg.cend(); ++it)
{
vmap.insert(it.key(), it.value());
}
const QVariant v = QVariant::fromValue(vmap);
const QJsonDocument doc = QJsonDocument::fromVariant(v);
return doc.toJson();
}
Alternatively, you can build the json object directly from the map. In this case it's the same amount of code:
QByteArray stringMapToJson2(const QMap<QString, QString>& arg)
{
QJsonObject jObj;
for(auto it = arg.cbegin(); it != arg.cend(); ++it)
{
jObj.insert(it.key(), it.value());
}
QJsonDocument doc;
doc.setObject(jObj);
return doc.toJson();
}
This seems like a stylistic choice and I am unsure which would be faster. Both produce the same output.
One thing to note: The conversion from QString to QVariant is predefined in Qt, so the first method works fine. For objects of your own classes you would have to register that type and provide a suitable conversion which can be a bit tough to get right. In the second method you could do this conversion inline in the loop.

How to convert QList<QVariant> to QList<T>

For my serialization method i need to store a QList<T> where T is my custom Type, in a QVariantList.
QList<T> l;
l.append(T());
QVariant var = QVariant::fromValue(l);
var.canConvert(QVariant::List); // returns true
//So i can easily iterate over the variant with sth like this:
QVariantList list;
QSequentialIterable it = var.value<QSequentialIterable>();
for (const QVariant &v : it)
list << v;
/* deserialization side */
var = list;
var.value<QList<T>>(); //returns an empty list which is not my serialized list;
My problem is that i cannot convert back the variant list into QList<T>
EDIT:
#define PROPERTY(type, name) \
Q_PROPERTY(type name MEMBER name) \
type name;
class Measurement
{
Q_GADGET
public:
PROPERTY(int, index)
PROPERTY(QString, name)
PROPERTY(QString, unit)
PROPERTY(double, factor)
PROPERTY(bool, isVisible)
PROPERTY(quint8, decimal)
bool operator ==(const Measurement &other)
{
return (this->index == other.index);
}
};
you can consider this class as my custom type (T). i also save the class name (here "Measurement") along with serialized data for furthur uses, because as you know we can get the registered type with QMetaType::type(char*) but with that type i can only construct a QVariant with QVariant(int typeId, const void *copy) but here i want to construct the QList<Measurement> itself.
You will need to deserialize the QVariant list one item at a time. I am also not sure that this line:
var = list;
is performing what you intended. It will take your QVariantList list and wrap it inside another QVariant called var, which is of type QVariant(QVariantList, (QVariant(MyType, ), QVariant(MyType, ))). There doesn't seem to be much benefit to doing this.
Nonetheless, the example below shows a way to recover the list from var.
#include <QCoreApplication>
#include <QVariant>
class MyType {
public:
MyType() {}
MyType(QString value) { m_value = value; }
QString m_value;
};
Q_DECLARE_METATYPE(MyType)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<MyType> l;
l.append(MyType("foo"));
l.append(MyType("bar"));
QVariant var = QVariant::fromValue(l);
var.canConvert(QVariant::List); // returns true
//So i can easily iterate over the variant with sth like this:
QVariantList list;
QSequentialIterable it = var.value<QSequentialIterable>();
for (const QVariant &v : it)
list << v;
/* deserialization side */
var = list;
QList<MyType> deserializedList;
foreach(QVariant v, var.value<QVariantList>()) {
deserializedList << v.value<MyType>();
}
return a.exec();
}

QT container, with specified order and no repetitions

I need somthing similar to QSet, but I need the items to be saved on the order I inserted them
is there such thing?
I am not aware of anything like that out of the box in neither Qt nor STL. Boost has something like that I think but it is not that hard to do this yourself.
You could do a wrapper around QHash like this:
template<typename T>
class MySet : QHash<T, int>
{
public:
using QHash<T, int>::QHash;
QVector<T> values() //this 'hides' the base QHash::values() of QHash
{
QVector<T> vec(count());
for(auto it = cbegin(); it != end(); ++it)
{
vec[it.value()] = it.key();
}
return vec;
}
void insert(const T &value)
{
if(!contains(value))
{
insert(value, m_Data.count());
}
}
};
The usage is quite similar to QSet:
MySet<QString> set;
set.insert("1");
set.insert("2");
set.insert("3");
qDebug() << set.values();
And that prints the values in order. If you need more complete support like iterators also iterating in your desired order you would have to reimplement more functionality but the gist of it would be the same. After all QSet is internally QHash as well. Note that the above does not support removal without modification.
Maybe a QList or a QVector could help.
QList<QString> stringList;
//By the way, Qt provides QStringList as a typedef for QList<QString>
stringList.append("A");
stringList.append("B");
qDebug() << stringList.at(0); //A
qDebug() << stringList.at(1); //B

QGraphicsRectItem's signal itemChange not generating on drag

I have made a custom class derived from QGraphicsRectItem.
class CornerPoint : public QGraphicsRectItem
{
public:
CornerPoint(QPointF centre);
enum { Type = UserType + 1 };
int type() const{ return Type; }
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
public slots:
void updatePosition(QPointF);
};
CPP File
CornerPoint::CornerPoint(QPointF centre): QGraphicsRectItem(NULL)
{
setPos(55,22);
setRect(0,0,99,99);
}
QVariant CornerPoint::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
std::cout << "Change is" << change << std::endl;
if( QGraphicsItem::ItemPositionChange == change && scene())
{
std::cout << "New values " << value.toPointF().x() << value.toPointF().y() << std::endl;
}
return QGraphicsItem::itemChange(change, value);
}
I have made these objects selectable and moveable. But when I drag them, I dont receive the signal for position change of this rectangle. All I receive are the signals with value 4 (ItemSelectedChange) and 14 (ItemSelectedHasChanged).
This post says that I need to enable some flags. But I can't find any example of any one doing this, and using these flags is giving errors.
Crap.
I just had to enable this flag :
setFlag(GraphicsItemFlag::ItemSendsGeometryChanges);

Config File Overwritten in Qt

I am creating a config file which stores username, password and role of certain user. I am using the following code.
void MainWindow::OnAssignButtonClicked()
{
QSettings settings("/root/configFile.ini", QSettings::IniFormat);
QString userName = lineEditUsername.text();
QString password = lineEditPassword.text();
QString Role = comboBox.currentText();
QList<QString> listUsername;
QList<QString> listPassword;
QList<QString> listRole;
QVariantMap userPasswordMapping;
QVariantMap userRoleMapping;
listUsername << userName;
listPassWord << Password;
listRole << Role;
for(int i = 0; i < listUsername.size() ; i++)
{
QString user = listUsername.at(i);
QString pass = listPassword.at(i);
QString role = listRole.at(i);
userPasswordMapping[user] = pass;
userRoleMapping[user] = role;
}
// Store the mapping.
settings.setValue("Password", userPasswordMapping);
settings.setValue("Role",userRoleMapping);
}
While Reading the Values
QVariant variantPassword = settings.value("Password");
QVariant variantRole = settings.value("Password");
QVariantMap mapPassword = variantPassword.value<QVariantMap>();
QVariantMap mapRole = variantRole.value<QVariantMap>();
QMapIterator<QString, QVariant> iteratorPassword(mapPassword);
QMapIterator<QString, QVariant>iteratorRole(mapRole);
while (iteratorPassword.hasNext())
{
iteratorPassword.next();
QString user = iteratorPassword.key();
QString pass = iteratorPassword.value().toString();
iteratorRole.next();
QString role = iteratorRole.value().toString();
}
The first time the value gets written correctly. However if I run the program again the value over writes the old values. Can some one please suggest me a solution here?
Thank You
Every time you click your 'assign' button, you create new mappings, store the user, password and role in there and save that list to the config file.
What is missing is that you read the existing mappings before modifying them.
Instead of
QVariantMap userPasswordMapping;
QVariantMap userRoleMapping;
do:
QVariantMap userPasswordMapping = settings.value("Password").value<QVariantMap>();
QVariantMap userRoleMapping = settings.value("Role").value<QVariantMap>();
Additional hint about the code you posted about reading the values:
QVariant variantRole = settings.value("Password");
this should be
.value("Role");

Resources