Eine kleine view framework with QtQuick (Overture)

Ok, this post (and the parts coming – I’ll just split this so that it won’t become a too long read) might interest a bit more wider audience than the previous ones.

So once you’ve started with QtQuick, played around with the examples, maybe done some own UI components and a few views, the thing you start wondering is “so how am I supposed to tie all this together to make it an application?” QtQuick itself doesn’t come with any kind of application framework. Just the basic building blocks you’ve seen in the examples. I (and Wolfgang here) think it’s a great thing that it doesn’t, because such a framework can go wrong in so many ways and restrict innovation. Naturally that would also be against the QtQuick philosophy of being just a framework for constructing objects easily and declaring interactions. Of course you usually still need to have one and probably(?) the boys and girls in the QtComponents project are making one. But the best part is that even if what they are proposing doesn’t suit you, with QtQuick you can just toss it all and do it yourself with relatively little effort. I’m not saying an ubiquitous application/view framework doesn’t have merits, but for standalone apps which don’t need tight integration to other being able to roll your own in a few moments is pretty damn great.

So our goal here is to make a small QtQuick application framework with the following features:

  • Animated view switching
  • Support for loading views on demand
  • Context aware default screen furniture (Title bar, context sensitive buttons, navigation bar)
  • A neat performance trick

To demonstrate the features we’ll build an app that utilizes the aforementioned concepts and sells crap to little girls. I haven’t come up with the perfect name for this, but for now we’ll call it KittyApp. You’re free to use this idea to make a million (see, I give away great startup ideas too!) with this if you want. In that case though you should probably rename it Kittr, Catsy, Kittify, Paw*d, Pussio.us, Cattlr or LitterBox.  Anyway… our totally awesome KittyApp will look like this:

Anatomy of a feline themed social media solution

This might be painstakingly obvious, but let’s go through the parts of the UI.

Green – the title bar. This contains our context sensitive element (the title). Yellow – the changing view content. Blue – a decorative frame drawn on top of the view content. Purple – the navigation bar (also context sensitive).

A word about the frame. We are drawing it here in four separate parts. If you want to implement this kind of “hole-in-the-middle” frame then please resist the temptation of just slapping a BorderImage with a completely transparent middle section on top of the view. I don’t know the guts of raster, but I’d assume it isn’t optimized for noticing that the middle section is completely transparent and it’ll just waste precious CPU time iterating through the pixels that don’t need any drawing. I’m 99,9% sure that a GPU (if you’re using OpenGL or OpenVG backends) doesn’t check the texture for full transparency. After all what kind of an idiot asks the GPU to draw a completely transparent polygon? The right way to do it (albeit slightly more laborious) is to draw the top, left, right and bottom parts of the frame separately.

Switching views

There’s two ways to handle switching views. Either you can do it with the states and transitions of QML or you can do it in a more imperative manner. We’re going for the latter. The first one is a completely viable option if your navigation is very simple, but if your app will have a complex navigation graph (possibly infinite), then I think the way I’m about to show you is better.

Our view transitions will be simple slides to left or right. If you need something else it should be fairly easy to implement. Without further ado, may I present you the ViewSwitcher:

Usage: call switchView(<the new view>, <scroll left or right?>). That’s about it. The parent element of your view should naturally be Item or derived from Item. Item, Rectangle, Image, BorderImage – anything goes. When defining your views you should keep in mind not to anchor the left or right edge of your view to anything, so just define width instead. This is because the views are animated by changing the x property.

This is how the views are instantiated in the KittyApp in our main.qml:

Every view is neatly tucked in a separate qml file. In addition to providing a good structure for your app, it will be needed when we implement on demand loading of views.

If you need your views to be notified when they’ve been activated or deactivated, implement activationComplete() and deactivationComplete() functions in your view. If you want do things before the view transition starts, you could easily improve the ViewSwitcher component by adding (for example activationStarting() and deactivationStarting()) function calls. The ‘if (foo.bar == undefined)’ clauses are there for convenience so that you don’t have to have empty functions in your view elements, in case the view doesn’t need to be notified.

Public service announcement: QML performance

This is already mentioned in the docs, but : always set elements which don’t need to be drawn fully transparent. QGraphicsView (which is the component responsible of orchestrating the drawing of our QML scene), is smart enough to skip completely transparent items. But the items are off the screen, aren’t they clipped anyway? Yes, but even the clipping takes precious CPU cycles and setting the elements transparent skips that part.

If you find that you’re not hitting the fps you want for your view transitions, you could try a snappy (like 200ms) animation that hides some elements from your view before the transition. For this you need to implement those activationStarting() and deactivationStarting() calls I mentioned earlier.

Ok, enough for now. In the next post we will add on demand loading of views which is very important for a reasonable startup time and memory consumption.

This entry was posted in Programming and tagged , , , , , , . Bookmark the permalink.

11 Responses to Eine kleine view framework with QtQuick (Overture)

  1. Ville V says:

    Interesting performance tip. I confess to having screwed up by leaving the off-screen stuff as-is. BTW, perhaps setting “visible” to false is more explicit than zero opacity (probably just a matter of taste).

    • elpuri says:

      Yep setting visible to false or opacity to 0 has the same effect from rendering perspective. In some cases opacity is handier though, because you can animate it to 0 and just leave it at that without having to set visible separately.

  2. Pingback: Tweets that mention Eine kleine view framework with QtQuick (Overture) | Blogasdf -- Topsy.com

  3. Ville V says:

    I tried your component, seems to work fine after I fixed it (well, adapted to my app).

    1) It seemed there was a brace mismatch. Or I accidentally screwed up somewhere

    2) I suggest adding “property Item root”. It sucks to have to have id “root” at your root window.

    I didn’t measure the performance, but I’ll take your word for this being faster than my previous solution (I already got good fps on n900, but N97 experience can use some tuning).

    • elpuri says:

      Thanks for the input. By no means is this supposed to be perfect or even production ready. I just wanted to illustrate the concepts in a minimal way.

      I’d assume that the benefits of setting visibility or opacity shows itself when you’ve got a lot of views (and thus lots of stuff off the screen). An app I’ve been tinkering with @work has got like 25 or so (amazing what a couple of hyperactive product managers and a UI designer can do). I bet QGraphicsScene is well tuned, but there’s only ~16ms to render a frame if you want to have 60fps and all help is needed (and most of the time you still fall short :) )

      I don’t know the structure of your UI but, you could try experimenting with the different viewport update modes of QGraphicsView to get better performance on the cra.. performance limited devices. If your UI is such that there’s a tiny spinner running in the upper left corner and something else animating in the lower right, the default update mode (BoundingRectViewportUpdate) used by QDeclarativeView causes everything in between those to be drawn too.

  4. Ville V says:

    Also, you may want to check for previousview on your onRunningChanged signal handler.

    file:///home/ville/p/qmlreddit/qml/qmlreddit/ViewSwitcher.qml:33: TypeError: Result of expression ‘previousView’ [null] is not an object.

  5. Pingback: Qt Quick Components – survival while you wait | Confusing Developers

  6. Pingback: Eine kleine view framework with QtQuick – Finale | Blogasdf

  7. Mattias says:

    The visible property is a bit of a pain if you’re working with key events instead of mouse events as in my product. At visible: false the parent Item loses focus, so you need some pretty unintuitive handling of focus if you have multiple views with FocusScope. So I go for opacity: 0 instead.

    • Juha Turunen says:

      I’ve been working only with touch interfaces, so I wasn’t even aware of this. And probably wouldn’t have even noticed because I tend to use a lot of fade out animations. Good tip nonetheless as QtQuick is heading to desktops with Qt5 and keyboard focus is much bigger issue there. Thanks for sharing!

  8. xrqctitxfsg rbubkbygyb vrfwnjgy bpqmgkbzlb

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>