import QtQuick 1.1

For those who have been following QML , you might remember that we changed the imports to QtQuick 1.0 to allow us minor revisions of the Qt Quick module in minor revisions of Qt. One of those minor revisions is nearly done, and will soon be waiting in the 4.7 branch of Qt. There's a bunch of good stuff there and one area in particular I'd like to focus on is the improvements we've made to versioning.

Last year I mentioned that I was going to do a blog post on QML's versioning system with my word game module. As it so happens, around that time we looked at the versioning system and saw that there were some features we were missing - those have been implemented for QtQuick 1.1 and provide a much nicer story for the versioning system as a whole. Let me tell you that story.

With QML modules we aimed to have a nicer upgrade path than is currently possible with C++. We've strived to allow changes between versions without causing the applications to break, or restricting the module owners to trivial changes. The core of this is the versioned module imports, which is why you import 'QtQuick 1.1' instead of just 'QtQuick'. By picking a specific version we have a chance to give you just the version you asked for, even if the module has now advanced to a new version with new features. The primary mechanism for this is the version numbers in qmlRegisterType. When you specify the minor and major versions that a type is introduced in, the application has to import at least that version in order for the type to be added to the namespace. This means that the modules can add types without worrying about a symbol conflict.

As an example, I added a Letters type to the word game module. Let's imagine the below snippet is from a game that used version 1.0:

import QtQuick 1.0
import MyTypes 1.0
import Qt.labs.wordgame 1.0
Item{
BoardLogic{
...
}
Letters{
count: 26
...
}
}

The 'Letters' type in this case is one from the 'MyTypes' module, and might fulfill a similar role to the new type. But it has different properties, and so the error you get if you tried to run this file (with version 1.1 and without our versioning system) would be Cannot assign to non-existent property "test". If wordgame was a system module that was updated independently of the game, then the game would fail to launch until the game's own update got rushed out.

But this specific snippet doesn't import 1.1, it imports 1.0. So with our versioning system the new Letters type from wordgame does not get added, and the game runs as before. The application maintainer can easily fix this next version by changing their type name, or importing wordgame in a namespace (import Qt.labs.wordgame 1.1 as WordGame), in the same update where they actually use the functionality. And they don't have a broken application in the interim.

Note that if you try to import an older version of the library than the one you are using to develop with, some errors cannot be warned about and it may not work when deployed against that older version. But it's easy to work around this - develop with the version that you're targeting.

A more radical example would be if you completely changed the implementation (which we're doing for the next Qt.labs.particles release). By registering a different C++ class for the Type in a different version, then applications get the new type if and only if they import that version or later. They can still have the original class registered to that Type in the earlier version, and the class you get is determined by the version you import. So if the new approach doesn't work out for you as well, you haven't been screwed over by having the old version taken away from you.

But this is no longer the full story - in QtQuick 1.1 we have added versioning at the level of individual properties, signals, and slots. Versioned types is great, but it's best suited for big sweeping changes like a new element or a full rewrite (which is practically a new element). You don't want to have to make a whole new C++ class just to add a lineCount property to Text. And to get some use out of the Letters object in wordgame, it needed to integrate with BoardLogic; but that's merely one property that tweaks the existing behavior. The below snippets from boardlogic.h shows just what was needed:

//A Letters object is how you can set the individual letter frequencies and score
Q_PROPERTY(Letters* letters READ letters WRITE setLetters NOTIFY lettersChanged REVISION 1);
...
signals:
Q_REVISION(1) void lettersChanged(Letters* arg);

Note that the getters and setters weren't directly accessible from QML anyways, so don't need the revision. And don't forget to register the new revision in wordgame.cpp:

qmlRegisterType<BoardLogic,1>(uri,1,1,"BoardLogic");

Now the 'letters' property on BoardLogic only gets exposed to QML when you import version 1.1, avoiding the same sort of conflicts as was described for the type level. So it's safe and easy to update system modules with just some extra properties/signals/slots without instantly breaking all the applications that rely on them.

With this level of version labeling, we've been able to actually make QtQuick 1.1 a minor release and one that shouldn't break anyone's running code. It fills in some functionality gaps without having to create a load of new classes - just adding some more properties where needed. And adds a couple of types too, but that's another story.


Blog Topics:

Comments