Check if QVariant is convertible to other QVariant type - qt

I receive data in JSON using a QJsonObject. I also have a QObject-based object holding properties, using Q_PROPERTY(...), for the keys in the JSON. Since Qt now has some more datatypes than JSON how can one check if they are convertible.
The datatypes used in the object typically are one of the following but not limited to
uint
double
QString
QDateTime
bool
The idea is to automatically call setProperty(...) on the QOject derived object for every key/value in the QJsonObject. Since this could fail due to malformed input in the JSON I have to check validity based on the QMetaProperty and QJsonObject/QVariantMap data.
Since this should run generic as a base class implementing manual checks for every datatype fails. I know there is QVariant::isConvertible<T>().
#include <QJsonObject>
#include <QVariant>
#include <QMetaObject>
#include <QMetaProperty>
class Test {
Q_GADGET
Q_PROPERTY(QString test)
QString m_test;
QJsonObject jo;
void call();
}
void Test::call()
{
jo.insert("test",QJsonValue(5));
// This will fail, since int is not convertible to QString implicitly
staticMetaObject->property(staticMetaObject->propertyOffset()).writeOnGadget(this,jo["test"].toVariant());
}
Since I am parsing the JSON before to check if every property would have a corresponding key in the JSON-Object I really like to catch these there already without changing my original object. Something like:
jo["test"].toVariant().canConvert<staticMetaObject->property(staticMetaObject->propertyOffset()).type()>()

Instead of using templated bool QVariant::canConvert<T>() one can use bool QVariant::canConvert(int targetTypeId).
QMetaProperty po = staticMetaObject->property(staticMetaObject->propertyOffset());
jo["test"].toVariant().canConvert(po.type());

Related

How to pass QString as shared_ptr<QString> to C++?

I have QML code which calls C++ slot which is defined like this:-
typedef std::shared_ptr<QString> QSString ;
...
//inside class declaration header
pulic slots:
//this gives error at runtime
void login(QSString host, QSString uname, QSString passwd) noexcept;
// this works
// void login(QString host, QString uname, QString passwd) noexcept;
...
I can call 2nd commented out function from qml side with QString just fine.
But I have some complex needs. I want to implement this slot in rust with cxx crate which.
On rust's side QString is opaque type and thus must be passed through C++'s smart pointer.
If I just run this code I get following error
"Could not convert argument 0 at"
"expression for onClicked#qrc:/Login.qml:112"
qrc:/Login.qml:112: TypeError: Passing incompatible arguments to C++ functions from JavaScript is not allowed.

Qt 5 Logging - defining category with QLoggingCategory

I have a problem with QLoggingCategory(const char * category).
When I use it like:
QString rt = "3";
QString sv = "P";
QLoggingCategory dailyLogger(QString(rt+sv+"Logger").toStdString().c_str());
it doesn't work (my custom message handler doesn't recognize this category).
But when I use:
QLoggingCategory dailyLogger("3PLogger");
message handler sees the category.
Here's the handler function:
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if (QString(context.category).contains("3"))
{
//some code
}
}
Why does my computed category name not work?
QLoggingCategory retains a copy of the char* pointer with which it's constructed. The pointed-to characters must remain valid and constant for the life of the QLoggingCategory instance.
In your failing example, c_str() returns a temporary which is no longer valid after the std::string goes out of scope. So the QLoggingCategory is now pointing at invalid memory (giving you Undefined Behaviour).
In your second example, you pass a string literal, which is valid for the lifetime of the program, so no lifetime issues arise.
You will need to copy the string data into a char array with a lifetime that outlives the logging category.

Remove first bytes from QByteArray

I want to write a function in which QByteArray is input to the function.
I want to remove some header from receive data and store it into global QByteArray.
void abc::CopyData(const QByteArray &data)
{
switch(RequestPacketCount)
{
case REQUEST_FIRST_PACKET:
{
ByteArrayData = data;
}
break;
case REQUEST_SECOND_PACKET:
case REQUEST_THIRD_PACKET:
ByteArrayData.append(data);
}
}
I want to remove 'n' no. of byte from start of 'data' and store remaining data into 'ByteArrayData'
Thanks in advance.
What you seem to want is simply copy the original array and use remove;
ByteArrayData = data;
ByteArrayData.remove(0, n); // Removes first n bytes of ByteArrayData,
// leaving data unchanged
Since a QByteArray is implicitly shared, the construction of the copy takes constant time, and the modification (deletion) is what will make the actual copy when needed.
To append efficiently, you can just use data to get to the byte array, and append the part you want. That will prevent un-necessary temporary objects. That would look something like;
ByteArrayData.append(data.data() + n, data.size() - n);
You can use QByteArray::mid:
ByteArrayData = data.mid(n);
//...
ByteArrayData.append(data.mid(n));
Adding to Joachim's answer, in C++ it's rather unfortunate that some implementation details leak into the interface, but it's still preferred for performance reasons to let the compiler do the copying by passing the argument as value. So, paraphrasing, the best solution would be:
void abc::CopyData(QByteArray data)
{
...
data.remove(0, n);
...
ByteArrayData.append(data);
}
The only place where you definitely do not want to have such arguments passed by value is a signal declaration - signals never modify their data. It's fine for a slot to do so, though:
class MyClass : public QObject {
Q_OBJECT
...
public:
// pass by const reference in signals
Q_SIGNAL void dataSource(const QByteArray &); // The only correct signal form
// pass by const reference or value in slots, depending on use
Q_SLOT void dataSink1(const QByteArray &); // Valid
Q_SLOT void dataSink2(QByteArray); // Valid as well.
};
Connecting to either slot uses the same code whether it's Qt4 or Qt5 connection style. Thus you don't have to worry about such interface changes due to leaked implementation details breaking your code.
// Qt4 - you should elide const and reference anyway
connect(src, SIGNAL(dataSource(QByteArray)), dst, SLOT(dataSink1(QByteArray));
connect(src, SIGNAL(dataSource(QByteArray)), dst, SLOT(dataSink2(QByteArray));
// Qt5
connect(src, &MyClass::dataSource, dst, &MyClass::dataSink1);
connect(src, &MyClass::dataSource, dst, &MyClass::dataSink2);

method overloading with QString or std::string : call is ambiguous

I have a class which looks like this :
class MyClass {
public:
void drawText(const QString& rText);
void drawText(const std::string& rText);
};
I overloaded the drawText() method because I want to accept QString as well as std::string.
But when I write something like this :
MyClass foo;
foo.drawText("Hello");
The compiler is complaining that the call to drawText() is ambiguous.
I understand that from an array of char, the compiler cannot decide between a QString or a std::string, because both provide a suitable constructor.
But is there a way for me to make sure the user can use the drawText() method either by passing a QString or a std::stringor an array of char ?
To answer your question, yes: add another overload which takes const char*
The implicit conversion from const char* to QString is problematic because it assumes that the input is ASCII. I suspect the Qt folks would like to remove that constructor altogether but it would break source compatibility. If you want to disable it in your app, you can define QT_NO_CAST_FROM_ASCII.

Convert a QStandardItemModel to a QVariant

I'm trying to send a QStandardItemModel-derived object to PythonQt, but I'm a little confused on how it needs to be sent. When I was using boost::python I had several controls like boost::noncopyable to ensure I wasn't recreating this object, but sharing it with python. I also had constructs to provide a boost shared pointer to python from inside python.
class Scene : public boost::enable_shared_from_this<Scene>, public QStandardItemModel
In PythonQt, however, I'm not sure what's available. The function call takes a QVariantList for all the function parameters.
QVariant PythonQt::call(PyObject* object, const QString &callable, const QVariantList &args = QVariantList))
What I'm confused about now is how to get my object to python via a QVariant. Since its derived from QStandardItemModel, I figured it would already be register
void MyObject::someFunction(QString fileName)
{
QVariant myObjectV = qVariantFromValue(this);
// send to python
...
}
But this gives me the following error:
'qt_metatype_id' : is not a member of 'QMetaTypeId<MyObject>'
I've tried registering it after I declare my class, but this throws a different error.
class MyObject : public QStandardItemModel
{
Q_OBJECT
...
};
Q_DECLARE_METATYPE(MyObject)
QStandardItemModel::QStandardItemModel(const QStandardItemModel&) is private within this context.
I actually get the error twice--once in header where I add the Q_DECLARE_METATYPE and in another header, which has a class which always derives from QStandardItemModel but is otherwise unrelated.
Is Q_DECLARE_METATYPE even the correct way to go about converting this object to a QVariant?
BOOST_PYTHON_MODULE(scene)
{
class_("Scene");
}
Yes, by default, QVariant can take one of te following types - http://doc.qt.io/qt-4.8/qvariant.html#Type-enum - and they are not enough for your task. You should declare additional types by yourself via qmetatype system. Thus you shoud call qRegisterMetaType() function.

Resources