Issue with extending QML by C++ classes in a custom namespace
-
Hi everyone,
I'm trying to extend QML following the tutorial here: http://doc.qt.nokia.com/4.7-snapshot/qml-extending.html
Everything has been going ok, but a problem appeared when I tried wrapping the objects in the tutorial in their own namespace. Simply instead of having class Person, I have class XX::Person (and XX::BirthdayParty, XX::Boy and XX::Girl). I modified the source from this step: http://doc.qt.nokia.com/4.7-snapshot/qml-extending.html#object-and-list-property-types , but the sample stopped working. The component creation fails with this error:
QDeclarativeComponent: Component is not ready
(file:///Users/snowmanx/example.qml:4:11: Cannot assign object to property
host: Boy {
^)The source for QML is this (straight from the example):
@
import People 1.0BirthdayParty {
host: Boy {
name: "Bob Jones"
shoeSize: 12
}
guests: [
Boy { name: "Leo Hodges" },
Boy { name: "Jack Smith" },
Girl { name: "Anne Brown" }
]
}
@
The engine is not able to assign Boy to host. The birthdayparty.h source looks like this (note use of the namespace):@
namespace XX {
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost)
Q_PROPERTY(QDeclarativeListPropertyXX::Person guests READ guests)
public:
BirthdayParty();Person *host() const; void setHost(Person *); QDeclarativeListProperty<Person> guests(); int guestCount() const; Person *guest(int) const;
private:
Person *m_host;
QList<Person *> m_guests;
};
}
@person.h is this:
@
namespace XX {
class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
public:
Person();QString name() const; void setName(const QString &); int shoeSize() const; void setShoeSize(int);
private:
QString m_name;
int m_shoeSize;
};class Boy : public Person
{
Q_OBJECT
public:
Boy();
};class Girl : public Person
{
Q_OBJECT
public:
Girl();
};
@Again, note use of the namespace. When I remove the namespaces from the C++ classes, everything works fine (QML is loaded and the host property is assigned Boy object). When the namespaces are in effect, the loading fails. Any idea what is going on there? Is there anything else besides calling qmlRegisterType() when using namespaces?
Thanks in advance.
-
How are you calling qRegisterType?
Looking around for something with Namespaces + Qt Metatypes you'll see that there are a couple if issues.
Someone even said something like: "Welcome to Namespace/Metatype hell!" Bur apparently there are solutions, did you see "this":http://lists.qt.nokia.com/pipermail/qt-interest/2009-September/012250.html ? -
This is the code I use to register the classes for QML:
@
using namespace XX;int main(int argc, char ** argv)
{
QCoreApplication app(argc, argv);qmlRegisterType<BirthdayParty>("People", 1,0, "BirthdayParty"); qmlRegisterType<Person>(); qmlRegisterType<Boy>("People", 1,0, "Boy"); qmlRegisterType<Girl>("People", 1,0, "Girl");
...
@I briefly scanned the thread in the link you sent, but I haven't seen anything QML related. In any case registering class directly using qRegisterMetaType won't work for me, as my classed don't have copy constructors :(.
-
In the following "Jira report":https://bugreports.qt.nokia.com/browse/QTBUG-15459 it is noted that:
"If a class is declared inside a namespace, then the namespace name has to be included when referencing to the class in Q_PROPERTY declarations and in associated read and write function prototypes"
There is an example included in the report that shows how to do this. Does following the advise from the report above help?
When only having a simple namespace and not referencing a namespaced class in Q_PROPERTY then it works out of the box for me as shown below:
@
#include <QtGui>
#include <QtDeclarative>namespace MyNamespace {
class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
public:
Person()
{}QString name() const
{
return m_name;
}
void setName(const QString & n)
{
m_name = n;
}int shoeSize() const
{
return m_size;
}
void setShoeSize(int s)
{
m_size = s;
}private:
QString m_name;
int m_size;
};
}#include "main.moc"
using namespace MyNamespace;
int main(int argc, char** argv)
{
QApplication app(argc, argv);
qmlRegisterType<Person>("People", 1, 0, "Person");
MyNamespace::Person myObj;
QDeclarativeView view;
view.rootContext()->setContextProperty("rootItem", (Person *)&myObj);
view.setSource(QUrl::fromLocalFile("main.qml"));
view.resize(200,100);
view.show();
return app.exec();
}
@
main.qml
@
import QtQuick 1.0
import People 1.0Text {
text: myPerson.namePerson {
id: myPerson;
name: "Sigurd"
shoeSize: 24}
}
@ -
For each class I want to expose in QML I add QML_DECLARE_TYPE after class each class declaration in .h file (after the namespace). Something like that:
@
namespace XX {
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost)
...
};
}QML_DECLARE_TYPE(XX::BirthdayParty)
@I'm doing it, since at some point I had problems with QDeclarativeListProperty - the engine faield to load the the list with an error: Cannot assign multiple values to a singular property.
Adding the namespace to the property definition and adding QML_DECLARTY_TYPE to all QML objects helped with this case. What I also had to do was instead of using qmlRegisterType<Person>(); for abstract classes, I have to use qmlRegisterUncreatableType<Person>("XX", 1, 0, "Person", "Abstract class");
When I tried to remove the QML_DECLARE_TYPE or change from qmlRegisterUncreatableType() to qmlRegisterType() or both, I always ended up with issues loading the items to the declarative list.
Unfortunately at this point I'm not able to replicate this with the sample code we've been using for this issue - the described behavior happens on my codebase which has way more classes exposed to QML (often with protected constructors for abstract members). Any idea what could be the cause of this?
-
I just tackled the same problem. I ended up having to put the following in my headers:
typedef MyNamespace::MyType MyType;
Q_DECLARE_METATYPE( MyType )
QML_DECLARE_TYPE( MyType )Kind of defeats the point of using namespaces, but you gotta do what you gotta do.
The problem here is how qmlRegisterType is coming up with the metatype name here. If qRegisterMetaType<T>( ) hasn't been called prior to qmlRegisterType the pointer metatype and list metatypes won't be properly defined here due to the code below that sets name using T::staticMetaObject.className():
@template<typename T, int metaObjectRevision>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
{
QByteArray name(T::staticMetaObject.className());QByteArray pointerName(name + '*'); QByteArray listName("QDeclarativeListProperty<" + name + ">"); QDeclarativePrivate::RegisterType type = { 1, qRegisterMetaType<T *>(pointerName.constData()), qRegisterMetaType<QDeclarativeListProperty<T> >(listName.constData()), sizeof(T), QDeclarativePrivate::createInto<T>, QString(), uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, QDeclarativePrivate::attachedPropertiesFunc<T>(), QDeclarativePrivate::attachedPropertiesMetaObject<T>(), QDeclarativePrivate::StaticCastSelector<T,QDeclarativeParserStatus>::cast(), QDeclarativePrivate::StaticCastSelector<T,QDeclarativePropertyValueSource>::cast(), QDeclarativePrivate::StaticCastSelector<T,QDeclarativePropertyValueInterceptor>::cast(), 0, 0, 0, metaObjectRevision }; return QDeclarativePrivate::qmlregister(QDeclarativePrivate::TypeRegistration, &type);
}@
I'm a little curious however, since the following line prints out my classname with the namespace...
@ qRegisterMetaType<MyType>( "MyType" );
qDebug() << "registered class: " << MyType::staticMetaObject.className();@However, I'm able to get past this specific error, so I'm not worrying too much...
-
In response to my previous post, the problem is in the Q_DECLARE_METATYPE macro, which uses #TYPE without allowing users to specify the Qt type name. It would be handy to have the following macro:
@
#define Q_DECLARE_METATYPE2(TYPE, NAME )
QT_BEGIN_NAMESPACE
template <>
struct QMetaTypeId< TYPE >
{
enum { Defined = 1 };
static int qt_metatype_id()
{
static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
if (!metatype_id)
metatype_id = qRegisterMetaType< TYPE >(#NAME,
reinterpret_cast< TYPE *>(quintptr(-1)));
return metatype_id;
}
};
QT_END_NAMESPACE
@