A friend at work ordered me to write a script for him. Such are the benefits of being my friend.
Here’s the script: http://gitorious.org/aalperts-automatons/qmlhelpers/blobs/master/gen-qml.py . Note this is just the first prototype I whipped up tonight, and it therefore used my distro Qt of 4.8 because I couldn’t wait for my Qt5 to finish building.
Before you attempt the adventure of reading my code, here’s the problem the script attempts to solve. You have a cross-platform C++ code base not using Qt. This code base does your magical data engine stuff, and you have a separate chunk of UI code for platform specific UIs. Naturally, for QML enabled platforms you want to have a QML UI but you can’t contaminate your data logic with QObjects. Which means you can’t expose your data classes directly to QML, you need a QObject wrapper around them.
The problem isn’t so much the design of the wrapper, but more that it’s a pain to write wrappers. Before Qt Creator gained the much needed feature of automatically generating missing Q_PROPERTY members, I had a script to do that in my own specialized usecase. Automating the process of adding the QML wrappers doesn’t just make life easier, it makes it actually feel like working off the one codebase instead of maintaining a QML port alongside the mainline.
This script generates the wrapper for you. Given a C++ header and some simple comments (currently like //QML int propName) it creates a QML compatible version of the classes contained within. This way you have the one main code base, the QML UI just uses QML subclasses of your code instead of the classes direct. The script also does a few other fun things, like automatically creating the main.cpp and qmlVersion.pro files – all you need is the non-Qt .h to expose and a QML file using those types (if you’re really keen, there are such files in the repo for testing). The other quick trick was to automatically convert char* properties to QStrings in that layer so that you have a useful range of types to start with.
Now the first version of the script implements a mode that’s not ideal for all circumstances. It’s best for when you only have this code base in your dreams (also known as the proposed architecture). Either that, or your code happens to do all the getter/setter stuff perfectly for automated QML hooks already. Also you need to be able to use the Qml subclasses in your code, which means either you create them all on startup and expose them then or your design can work with dynamically creating a hierarchy in QML. There are two other related approaches that should be easy to implement and be more appropriate for other circumstances:
The first is that it directly uses a variable instead of getter/setter methods. Then it creates boilerplate getters/setters in the QML class just like Qt Creator does, and if you want to know when QML changed your data you’ll need to ‘listen’ to the virtual function as well. This is more likely to mesh with existing code models, but lacks the flexibility of arbitrary accessors.
The second is that it requests an instance from the central logic and mirrors a reference to that (instead of double inheritance). The advantage here is that you use references to existing objects instead of having to create the QML subclasses, the disadvantage is that you need to find the instances. My current thought would be on component complete to ask a singleton for the object with a distinguishing property, but that’s not very generic and templatable. This approach is great for if you’re just trying to expose one singleton, or code designs where you use the types internally everywhere and so can’t use a QML subclass. But it’s terrible at using QML to the fullest by creating your instance hierarchy in it (yes, I know this use-case isn’t trying to use QML to the fullest but I can’t help myself).
In the end it’s still just one night’s hacked-up script, not the great automaton I’ll dream about if I ever get to sleep. But at least it’s a codified thought and could help people with their next cross-platform app. Make sure your next app is Qt compatible, for even I don’t know where QML will turn up next 😉 .