Archive for July, 2010

Grantlee version 0.1.4 now available

July 28, 2010

The Grantlee community is pleased to announce the release of Grantlee version 0.1.4.

This is the 3rd release of Grantlee in the month of July. Lets hope the development pace continues.

Grantlee now compiles almost cleanly with QT_NO_CAST_FROM_ASCII and QT_NO_CAST_TO_ASCII. I say almost because I can’t actually build Grantlee with those flags by default without breaking binary compatibility, because of some interesting design decisions. Making it compile with those flags was a good learning experience though, and a step towards full localization support in Grantlee templates through multiple APIs.

Another significant new feature in this release is that Grantlee now automatically finds its own default plugins at runtime. This is something I didn’t initially implement because I wrongly thought it was a platform issue to find plugins, even default plugins. Most new users of Grantlee hit into problems with this though, and finally Michael Jansen contributed a patch to resolve the issue. This should lower the barrier to entry to developing with Grantlee a little bit and that’s always a good thing.

Michael is also doing some really cool type introspection stuff, so keep a look out for that. I think it could lead to performance improvements and increased flexibility in type handling.

Grantlee version 0.1.3 now available

July 19, 2010

The Grantlee community is pleased to announce the release of Grantlee version 0.1.3.

The most interesting part of this release is the support for generating ruby code in the code generator example.

The patch adding ruby support did not touch any C++ code. It did touch some ui files and Qt Resource files. It would be possible to add extra language support without even recompiling the application if not for the Qt Resource usage. Part of the intent of that example application though is to show how to use Qt Resources with Grantlee though, so that is not going to happen for that app :).

Grantlee version 0.1.2 now available

July 4, 2010

The Grantlee community is pleased to announce the release of Grantlee version 0.1.2.

This release contains a bug fix for finding templates contributed by a Google Summer of Code student, and a new feature of enumeration support.

Note that this is not a dependency bump for KJots 4.5. KJots 4.5 depends on Grantlee version 0.1.0 or later.

Grantlee gets enum support

July 4, 2010

While at Akademy I have been doing some Grantlee work on adding support for enumerations in string templates.

This was some fun stuff digging into the possibilities in QMetaEnum and using some undocumented API for supporting enums in the Qt namespace.

The resulting possibilities are already in the Grantlee API documentation.

  {% ifequal document.alignment Qt.AlignRight %} 
    Right aligned 
  {% endifequal %}

  {% ifequal myObject.property MyClass.Thing2 %} 
    Is Thing2
  {% endifequal %}

  Alignment is {{ document.alignment.key }}. // Outputs "Alignment is AlignRight", for example.

This is a feature it would be nice to see in QML. Hopefully I can convince the QML developers to add enum support.

QModelEventLogger: Log and replay QAbstractItemModel events

July 4, 2010

At Akademy today I held a talk about Unit testing of model view components. The talk was well attended and well received, and went without a technical hitch, so thanks to the organizers and chairs for that.

My slides are here for your enjoyment and amusement. The main take-aways of the talk are to inject the object store into your model implementation polymorphically to allow the injection of fake objects, and to implement proxy models generically so that they can be unit tested against fake or easily test-drivable source models. There will eventually be a video of the talk uploaded to the internet I’m sure.

During the demo section of the talk I briefly mentioned a model logging class I wrote which deserves a more detailed description than I could fit into the talk.

The problem with proxy models is, they crash. Sometimes the reason for that is an assert being hit in the class itself, or in an observing class such as another proxy or a view (or something else entirely). Sometimes such asserts are only hit on certain conditions, such as never on the developers computer, and always on the computer of an unfortunate user. A bug which can’t be reproduced can’t be easily fixed.

The solution I came up with was to create a class to listen to to all signals that the model emits and keep track of them. When the application closes or an assert gets hit, the event log is written out as a compilable file. The file can be compiled with a trivial CMakeLists file and should hit the same assert, which the developer can use to reproduce and fix the bug, and which serves as a unit test afterward. The great thing is there is no information leak. The log does not contain the names of the folders in your kmail, or the subjects of your emails. All it shows is a number tree representing the structure of the model. Here is a log created when I started kmail on my computer, and another using a different application.

The logger is very easy to use. It follows the same pattern as the ModelTest class. Simply new the Logger with the class under test as an argument.


new ModelEventLogger(sourceModel, this);

Done.

KBiAssociativeContainer. Erm, huh?

July 1, 2010

What?

A while ago I wrote the KBiHash for encapsulating the content of two symmetrically updated QHash containers. The motivation for it was to be able to implement mapToSource and mapFromSource in proxy models sanely. By maintaining a mapping from parents in the source model to indexes in the proxy model, it was fairly clear how to map from one to the other.

It turns out it’s not so simple though. Sometimes I want a QHash on one side and a QMap on the other. I didn’t want to just copy, paste and replace QHash with QMap, so I made KBiHash even more generic to allow you to specify the associative container to use for each direction of the mapping.

That way, it is possible to use the same class to implement bi-directional QMap, bi-directional QHash, and QHash->QMap relations.

Why?

It turns out that mappings in proxy models are not always simple. If the purpose of the proxy is to change the structure of the model, there needs to be some way to quickly find the mapping closest to what you’re looking for.

What is the first number above 17 in this list:

(5, 20, 34, 12, 1, 7, 6, 21, 3, 19)

How did you find it? You looked left to right keeping track of the lowest number found so far which is greater than 17. Try again:

What is the first number above 13 in the sorted list:

(1, 3, 5, 6, 7, 12, 19, 20, 21, 34)

How did you find it? You might have looked first somewhere in the middle, see if that’s above or below 13, and then known whether to keep looking left or right. When you found it, you didn’t have to keep looking through the rest of the list because you knew all the numbers to the right are larger. When computers do this kind of thing, the first one is called linear search, and the second one is called binary search. Binary search is far faster, particularly on large lists, but only works if the list is sorted.

This is a problem I hit while writing KDescendantsProxyModel. That proxy maintains a mapping of the last item in the list in the source model to the row that item appears in in the proxy model. However, the mapToSource method also has to be able to map between intermediate items too.

KDescendantProxyModel maintains mappings to flatten a tree.

For example, if trying to map the row “8” from the proxy to the source, I need to know that the closest row below it which has a mapping is row “10”.

When mapping a row to the source, the proxy requires the first mapping that appears below the target row. To find that mapping in a QHash or KBiHash is always O(N). You have to look through each mapped row to see if it is the one closest to the target. The reason for that is that items in a QHash are accessible only in an unordered way. Items in a QMap are stored in sorted order though, which means that a computer can use a binary search algorithm, upperBound to find the mapping closest to the target, but above it. The complexity of that operation in a QMap is O(log N) in the worst case and average case, which is far faster. By storing the row numbers as keys in a QMap the speed of mapping to the source model is far faster.

How?

KBiHash was already a template class, just like QHash and QMap, but the logical leap was to make the template arguments to the class be not the key_type and mapped_type of the container, but the containers themselves.


class KBiAssociativeContainer<LeftC, RightC>
{
typedef RightC::mapped_type left_type;
typedef LeftC::mapped_type right_type;

void insert(left_type t, right_type u)
{
_leftToRight.insert(t, u);
_rightToLeft.insert(u, t);
}

private:
LeftC _leftToRight;
RightC _rightToLeft
};

Because both QMap and QHash contain typedefs for their key_type and map_type, have the same API for insert and lookup, and the same nested classes for iteration, both of those classes can be used in KBiAssociativeContainer. If Qt gets another new associative container in the future, that will work too if it has the same API. Those reading closely will notice that KBiAssociativeContainer is an associative container, but it doesn’t meet the static polymorphism requirements to be used in a KBiAssociativeContainer itself. That wouldn’t provide any benefit anyway.

KHash2Map provides fast bounds checking on elements on the right.

KBiHash and KHash2Map then becomes simple subclasses of KBiAssociativeContainer, ready to use.


template<typename T, typename U>
class KBiHash
: KBiAssociativeContainer <QHash<T, U>, QHash<U, T> >
{
};

template<typename T, typename U>
class KHash2Map
: KBiAssociativeContainer <QHash<T, U>, QMap<U, T> >
{
};

Where?

The code is in kdeui KDE SVN as private API.

Exercises for the reader

Why are the typedefs in the class flipped around? Why doesn’t the class use something like


typedef LeftContainer::key_type left_type;
typedef RightContainer::key_type right_type;

KSelectionProxyModel maintains mappings of parents, and of first child items in the list. Why does KDescendantsProxyModel maintain a mapping to the last items in the source lists instead?