Skip to content

QMetaObject/GObject-introspection inter-operability progress

Sunday, 15 February 2009  |  richard dale

I've been hacking on deriving QMetaObjects for GObject-Introspection data as I described in a recent blog and am making good progress. I've now built a complete heirarchy of QMetaObjects from the gobject-introspection Clutter module which I've been using for testing.

I've checked the current code into playground/bindings/smoke_gobject so other people can play with it. It turns out that QMetaObject and the GObject Introspection models are pretty compatible. They both have single inheritance, properties, signals, runtime method calling (slots in Qt or or dynamic function invocation via ffi in G-I), enums and so on. So we need to add a dynamic runtime to call G-I methods as Qt slots or forward GObject signals to Qt ones. But as far as I can see it looks perfectly doable.

In playground there is code for dumping a .gir module and for generating the QMetaObjects. There are three classes; Smoke::MetaObjectBuilder for generating the QMetaObjects from the G-I repository, Smoke::GObjectProxy for invoking methods on the GObject classes via qt_metacall(), and a Smoke::GObectNamespace class for each G-I namespace with the constructor methods as slots and all the enums and flags for a namespace.

A dump of the Clutter QObjectNamespace instance looks like this:

    Smoke::MetaObjectBuilder::createNamespace("Clutter");
    Smoke::GObjectNamespace * gobjectNamespace = Smoke::MetaObjectBuilder::findNamespace("Clutter");

    const QMetaObject* metaObject = gobjectNamespace->metaObject();
    for(int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) {
        printf("%s\n", metaObject->method(i).signature());
    }

// Gives this output:

Actor*Actor(Clutter::Vertex*,Clutter::ActorBox*)
Stage*Stage()
Stage*Stage()
Shader*Shader()
Timeline*Timeline(uint,uint)
Timeline*Timeline(uint)
Alpha*Alpha()
Alpha*Alpha(Clutter::Timeline*,Clutter::AlphaFunc*,void*,GLib::DestroyNotify*)
Group*Group()
BehaviourBspline*BehaviourBspline(Clutter::Alpha*,Clutter::Knot*,uint)
BehaviourDepth*BehaviourDepth(Clutter::Alpha*,int,int)
...

Or dumping the enums and flags for the namespace gives this output:

    for(int i = metaObject->enumeratorOffset(); i < metaObject->enumeratorCount(); ++i) {
        QMetaEnum enumerator = metaObject->enumerator(i);
    printf("%s::%s\n", enumerator.scope(), enumerator.name());
    for (int j = 0; j < enumerator.keyCount(); ++j) {
        if (enumerator.isFlag()) {
            printf("    %s = 0x%4.4x\n", enumerator.key(j), enumerator.value(j));
        } else {
            printf("    %s = %d\n", enumerator.key(j), enumerator.value(j));
        }
    }
}

// Gives this output: Clutter::Gravity None = 0 North = 1 NorthEast = 2 East = 3 SouthEast = 4 South = 5 SouthWest = 6 West = 7 NorthWest = 8 Center = 9 Clutter::RotateAxis XAxis = 0 YAxis = 1 ZAxis = 2 Clutter::RotateDirection Cw = 0 Ccw = 1 Clutter::RequestMode HeightForWidth = 0 WidthForHeight = 1 Clutter::ModifierType ShiftMask = 0x0001 LockMask = 0x0002 ControlMask = 0x0004 Mod1Mask = 0x0008 Mod2Mask = 0x0010 Mod3Mask = 0x0020 Mod4Mask = 0x0040 Mod5Mask = 0x0080 Button1Mask = 0x0100 Button2Mask = 0x0200 Button3Mask = 0x0400 Button4Mask = 0x0800 Button5Mask = 0x1000 ...

The other QMetaObjects have slots, signals and properties for each GObject class with the names converted to camel case and Qt types replacing the GTK ones. It looks pretty 'Qt-like' to me. I had originally been thinking of making the binding entirely dynamic, but I now think it would be nice to optionally generate a C++ class heirachy as that wouldn't be too hard. That's all for now - watch this space for more news..