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

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.

Related

Check if QVariant is convertible to other QVariant type

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

std::vector is not accepted as delegate return type in C++/CLI? C2065 C2061

I have the following C++/CLI snippet:
.h
#pragma unmanaged
#include <vector>
public delegate std::vector<std::wstring> XYZ(const std::wstring& filter);
.cpp
XYZ^ xyz = gcnew XYZ(&myClass::xyzFunc); // <-error C2065 + C2061
This case I get at this line two errors:
error C2065: 'xyz' : undeclared identifier
error C2061: syntax error : identifier 'XYZ'
However, if I change the delegate return type from vector -> wstring (for example), it works!
public delegate std::wstring XYZ(const std::wstring& filter); // <-- w/o vector<> , works!
Has anyone any idea what is the problem?
Greatly appreciated!
Clearly this is a compiler defect, it should at least have emitted a diagnostic why it didn't add the delegate type to the symbol table. You could submit it to connect.microsoft.com but they are not going to fix it.
A workaround is to use a typedef to declare the return value type:
typedef std::vector<std::wstring> returntype;
delegate returntype XYZ(const std::wstring& filter);
I would urge you a bit to treat C++/CLI as an interop language, its major reason for being. This delegate is not usable by any other managed code. Do favor String and List<String^> here.

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.

Qt: Q_PROPERTY with pointer and forward declaration for QtScript access

Problem
I am making a project using Q_OBJECT and Q_PROPERTY to access some objects from scripts. I have two problems:
making classes that use forward declarations scriptable
returning a property as pointer
Explanations
1. Why forward declaration?
The class B gets the forward declaration to A, because A needs the complete B type in the header due to the templates. B needs only an incomplete type (A*) in the header, thus the forward declaration is valid.
2. Why returning a pointer?
We cannot return a copy, as we need access to the actual object in the script. We cannot return a reference, as Qt does not allow slots to return references - their type would be ignored, they would only return void*.
Code
Complete code download on pastebin or as ZIP archive or as ZIP archive of minimal example is available, for testing / playing: I needed to split up the files for the forward declaration and for MOC. I added a Makefile to test it. Make deps: g++, moc, Qt.
Important parts
class A; // forward declaration necessary, see explanation above
class B : public QObject {
Q_OBJECT
Q_PROPERTY(A a READ GetA) // <-- ERROR HERE
// ...
public slots:
A* GetA() {
return mA;
}
private:
A* mA;
// ...
}
The error line in the script:
print(bObj.GetA().GetName());
Compile error
This error disappears when I comment out the Q_PROPERTY above.
tmp/B.moc.hpp:95:51: error: invalid use of incomplete type ‘struct A’
tmp/../B.hpp:10:7: error: forward declaration of ‘struct A’
Script exception
When leaving out the Q_PROPERTY and calling the GetA() method as a slot from the script, I get the following exception:
Line 7: "TypeError: cannot call GetA(): unknown return type `A*'
(register the type with qScriptRegisterMetaType())"
When registering A* with qRegisterMetaType<A*>("A*"); this changes to:
Line 7: "TypeError: Result of expression 'bObj.GetA().GetName'
[undefined] is not a function."
That shows that GetA() does not return the A object, or somehow it returns a pointer, but the script cannot dereference it. GetA() then actually returns a QVariant(A*), can this be used somehow?
Questions:
Can I somehow make a Q_PROPERTY from an incomplete type, or how could I avoid the forward declaration?
Can I return a reference in a slot (maybe some tricks, e.g. a class that wraps the pointer and "overrides" the script operator., if something similar exists) or
Can I somehow dereference a QVariant(A*) to A in QtScript?
Your property type is A, not A*, that's why you get very reasonable error.
You should use QScriptValue. Look this code. It works Ok:
class A; // forward declaration necessary, see explanation above
class B : public QObject
{
Q_OBJECT
// Using QScriptValue, made from A instead of A to allow script work correctly with an object
Q_PROPERTY(QScriptValue a READ GetA)
public slots:
QScriptValue GetA() {
//making QScriptValue from A. Type conversion in C style only due to limitation of incomplete type
//In real app it's beter to put defenition of this slot after A's defenition
return static_cast<QScriptEngine*>(parent())->newQObject((QObject*)mA);
}
private:
A* mA;
// ...
public:
//I decided my object will be a child of scriptEngine, but you can take a pointer to it in some other way
B(QScriptEngine * parent);
};
class A: public QObject
{
Q_OBJECT
public slots:
QString GetName() const {return "a name";}
public:
A(QScriptEngine*parent):QObject(parent){}
};
B::B(QScriptEngine *parent):QObject(parent), mA(new A(parent)){}

Resources