Dave's QML Binding Loop Backtrace Printer

Binding loops suck, and they can be hard to fix. I wrote a tool that prints a backtrace of the bindings being updated when a loop occurs. See link at bottom.

About:

QML bindings are a very fast and easy way to write a declarative UI. However it's quite easy to accidentally write an infinite loop.
This can happen if we bind propertyA to affect propertyB and also bind propertyB to affect propertyA, they would constantly update each other.

Consider the following example:

1 import QtQuick 2.0
2 
3 Rectangle {
4     width: childrenRect.width
5     Text {
6        text: parent.width > 10 ? "Hello World" : "Hi"
7     }
8 }

The Rectangle width changes on startup, that changes the text's size, which in turn changes the Rectangle's width. If this was undetected the application would loop forever and eventually crash.
QML prints a warning, and ceases processing, but it's an indication that something is wrong with the logic of your code, and it needs fixing.

However, whilst the loop here is obvious to spot, it can be considerably more complicated when looping through tens of bindings over many many components.

Creating a Tool

The problem with this warning is that on its own is rather unhelpful - trying to find the loop then becomes a manual task of tracing all possible combinations through every bindings that could lead to a short circuit. GDB on its own doesn't help as the C++ backtrace tells us absolutely nothing we can use.

I've created a small script that, using gdb, unwinds the backtrace detecting where the properties changed and then showing the QML code which is responsible.

Simply download here into $PATH and run with

"binding-loop-tracker.py myAppName"

In the case of the loop above we will see output like:

=====Binding loop detected - printing backtrace =====
#0 - file:///home/david/temp/binding_loop.qml:4:12
#1 - file:///home/david/temp/binding_loop.qml:6:15
#2 - file:///home/david/temp/binding_loop.qml:4:12

Which shows which line of QML was being updated when we hit the loop.

It still requires some manual work to follow the trace through, but it's a useful aid and has already helped me in two real world cases that I couldn't resolve manually.

Building stable branches with kdesrc-build

When coming up to a release it's important to track the stable branch of a project so that we're actually running what we're going to release and can put our focus on fixing any remaining tiny bugs.

If you build all of Plasma with kdesrc-build you can easily switching to building the stable release with the following command:

kdesrc-build --Branch Plasma/5.7 kf5-workspace-modules

To change back, simply emit the --Branch parameter.

PlasmaShell Sans GL

Since Plasma 5, the main shell is powered by QtQuick, which till now brings a requirement on a working OpenGL setup. This causes problems for Plasma in situations where we can't run OpenGL; either extremely cheap hardware, xrdc or when a user upgrades and breaks their nvidia setup (a seemingly common occurence).

Qt 5.6 brings a new module which opens some interesting possibilities; the QtQuick 2D renderer, which avoids that.
This has existed for a while, but it has only recently been open sourced as GPL3.

QtQuick Internals

QtQuick is powered by a scenegraph.

Each graphical item updates a tree containing nodes containing either transformations or content. That content being either a rectangle, a picture or a custom openGL shader from the application. The important part is that it stores a tree of items in an way optimised for rendering, but also acts somewhat as an layer between any QtQuick component and the underlying OpenGL renderer.

Using the QtQuick 2D renderer

The QtQuick 2D renderer still uses the same scenegraph, so all custom QQuickItem's which use the standard QtQuick SceneGraph nodes still work, but instead of calling OpenGL functions, calls are mapped to a raster backend instead.

The Result

My personal desktop, running Plasma using the QtQuick 2D renderer. The screenshot shows some of the parts working, but also highlights some of the bugs

Performance is surprisingly fast, not faster than the OpenGL backend, but plasmashell still remains perfectly usable on my desktop.

Most of the basic scene graph nodes have a 2d renderer implementation, however any node that does custom openGL, such as QtGraphicalEffects or certain parts of Plasma, will simply fail.

What's broken

There are plenty of known limitations with using the 2D renderer, some cosmetic, some more fundametnal.
Qt provides their own list.

In terms of Plasma, the list of broken items are:

  • We have our own GL check in the shell that needs adapting
  • Icons were broken. We implemented our own shader whilst animating between states rather than uploading a new pixmap per frame. I fixed this by simply turning that off.
  • Our load monitor plasmoids are pure GL. I've made a patch that makes it not crash. Making it actually work would mean having two implementations...which isn't a route I really want to go down.
  • Widget explorer is broken, again we have our own shader for some effects
  • We are missing a lot of minor graphical effects. Fewer shadows and alike, but that's a hit I think we will just have to accept

Summary

In general it seems that with a relatively small amount of work this might be a valid option for users without working openGL.

It will always be a second class citizen, but it should be do-able to support without hindering progress for the vast majority of users.

What's interesting is to see how easy it is to support a different scene graph backend, as it is a clear indication of what we will encounter when it comes to Vulcan in a few years time.

Neon and Plasma Relationship

As we saw neon, a new and fresh Linux distribution was launched last week. This project is incubated by the KDE Community, sharing KDE's hosting and community. Hopefully we'll see neon flourish into an awesome distribution over time.

However, I have seen some potential confusion in an article reaching a conclusion that this might be in some way problematic for other distributions to deploy KDE software. To make sure we're all on the same page I wanted to give a clarifying statement from the Plasma mantainer.

Plasma is and remains distro-agnostic. It's in our interest to help all of our distribution channels. As long as distributions continue to keep up with the dependencies we need and work well with us, we support everyone as best as we can.

Legacy system tray icons back in Plasma 5

Didn't we drop support?

Legacy system tray icons are problematic; they don't scale, they don't fit in with the theme, they can't multiplex (be in two trays) and they're just generally very dated.

We came up with a new scheme Status Notifier Items (SNIs) back in 2009, which was also adopted by our friends at Ubuntu in Unity, which provides logical information over DBus about what to show, rather than just an arbitrary window.

Our existing xembed code needed an entire rewrite, given wayland would also break xembed it seemed a more useful investment of time to make sure most the toolkits supported the SNI specification than work on the rewrite.

In retrospect, I think we underestimated the fallout this would cause. There are propreitory apps, more random toolkits that weren't covered, and some distros didn't apply all the patches needed

There's nothing wrong with changing our minds in response to user feedback.

Restored Support

Having decided that we can't drop support just yet, it seems unlikely we can drop it in a few months when we switch to Wayland. So we need to consider how to make that work there. Embedding an X window inside a wayland window isn't going to work.

My solution was a hidden process, xembedsniproxy, renders the embedded windows offscreen then uses the existing SNI specification to inform Plasma. Plasma gets support, including multiplexing without really knowing about X.

It should be as seemless as anything else.

Current State

It's at a state where it's usable, but there are undoubtedly still some bugs. Every toolkit has their own quirks regarding system trays.

Please leave a comment if you find anything.

The code is currently in a test repository here

It is available already in yaourt for Arch users:
aur/xembed-sni-proxy-git

I hope to have it merged into Plasma for 5.5, depending on feedback.

Just another manic monday

I love project stats, and I completely obsess over bug reports.
Whilst collecting data for another post, a mystery libreoffice autocomplete popup me to plot something I hadn't thought of plotting.

What days of the week are bugs opened and closed?


Resolved can mean one of a few things; fixing the bug with a change in the code, marking it as a duplicate of another bug or in some cases closing it as not a direction we want to go with Plasma.

Observations

People report more on weekdays than weekends

The difference surprised me. I think it gives a strong indication that Plasma 5 is being used more for work than as a hobby, with people more likely to encounter an area needing to improvement during the normal office week.

Obviously we don't know for sure and we can't pull useful timestamp information without knowing timezone of the user, but indications seem to be there.

Bugs are resolved on a weeked at 50% of the rate of the weekday

This isn't too surprising given that some Plasma maintainers like myself work for Blue Systems or Red Hat and we tend to work office hours.

What's encouraging is that, whilst slower, this shows that a significant amount of work does happen outside office hours; both employees taking the extra time to care for Plasma and our community contributors stepping up to work here. There's some shift between the people doing weekday and weekend bug closing.

We get more bugs open than we close

Naturally our graph will show that we have some open bugs, but if we assume this rate continues our number of open bugs will continue to rise.

It's not as bad as this graph makes it look as there also a lot of bugs in a zombie "needs info" state, where we're waiting to hear back from a user either with more information or to confirm their issue is fixed.

We definitely need more help here, I want Plasma to be bug free.

It's all about Tuesdays

Tuesday is apparently the day for the most reports getting handled. It's also the day Plasma 5.4 gets released.

I always knew Tuesday was an underrated day of the week.

High DPI in Plasma 5.4

As retrofitting high DPI support into such a large range of both KDE and third party applications is risky to do without breakage, progress is deliberately slow and gradual in order to do this right.

The good news is I get to write lots of blog posts as we make the continual steps forwards.

Iterative Progress

Plasma 5.3 (last release)

* High DPI Scaling via a hidden config option for early testing

Plasma 5.4 (this release)

* User facing dialog with the screen management settings for scaling

Plasma 5.5+ (future releases)

* Automtatically enabled. Providing we see positive feedback in this release.

The New Setting

Plasma 5.4 brings a new setting to adjust the screen scaling to match your device.

In the new setting we've tried to simplify things to a simple slider which adjusts both font size which we have full control over, and then above a certain threshold adjusting the entire UI which we can only do at integer values (i.e doubling the size). This is to support the semi-high screens and the current generation all in the same place.

It's still far from what I'd like to achieve overall, but it does the job of making some devices usable.

What Can We Expect From This Release?

With High DPI support enabled you will see one of three things amongst your applications:

  • The app sized sensibly, looking absolutely gorgeous on your new screen.
  • The app sized sensibly, but some aspects may not necessarily make the full use of the higher resolutions available.
  • The app not scaled at all remaining really small. We should see the font size increase to fit, but icons, dialogs, checkboxes may remain annoyingly small.

We've tried to make sure the most common, important KDE applications (Dolphin, Konsole, Kate and so on) fit into that top category. An app like xfig or dia, will remain in the bottom category until Wayland.

There's also a slim chance there may also be some breakage.
If so please do file a bug report as soon as possible and tag me to the CC list.

Props especially go to Christoph Cullman and Alex Fiestas for their help on some of the applications.

Plasmoid Tutorial 3 - Blending In

Till now we have dealt with basic QtQuick Text items. Whilst they work, they will look out of place on a Plasma desktop. More importantly it's quite likely that you can get a situation where you can get black text on a black background, or another unreadable combination.

If we run our current applet as soon as we change to another theme we get a problem.

We want to have consistent visual style amongst plasmoids that follows the user's themes, along with consistent spacing throughout the shell. We want all "third party" plasmoids to follow these rules too.

We provide a set of imports that help make this happen.

The visual Plasma import come under 3 categories:

org.kde.plasma.core

This contain low level shapes; such as varying frames, SVG loading and icons, as well as a utility to get DPI independent sizes to use for spacing and other metrics. Useful for keeping things consistent.

org.kde.plasma.components

This contains the standard set of "widgets"; text boxes, spin boxes and labels and much more.

org.kde.plasma.extras

This is extra utilities that may be useful in your plasmoid.

Best Practices

  • Avoid hardcoding fonts and colours. Use the ones from the user's theme.
    If you must hardcode, make sure you never hardcode on top of a theme background or parts could end up unreadable.
  • Avoid using the Text element from the QtQuick import, instead use Label from Plasma components, it will set the right fonts and sizes automatically.
  • Do not adjust font sizes and styles. For headings use the Plasma Heading element with the appropriate level set to affect font size.
  • Set all animations to Theme.ShortAnimation or Theme.LongAnimation as appropriate.
  • Don't use QtQuick.Controls directly from inside the main plasmoid view

Continuing The RSS Viewer example

If we apply that to our current RSS viewer, our main view code should end up looking something like this:

import QtQuick 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras


Item {
    PlasmaExtras.ScrollArea {
        anchors.fill: parent
        ListView {
            id: mainList
            focus: true
            boundsBehavior: Flickable.StopAtBounds

            model: RssModel {
                id: rssModel
                source: "http://planetkde.org/rss20.xml"
            }

            delegate: PlasmaComponents.ListItem {
                PlasmaComponents.Label {
                    anchors.left: parent.left
                    anchors.right: parent.right
                    height: implicitHeight

                    elide: Text.ElideRight
                    text: model.title

                }
                MouseArea {
                    anchors.fill: parent
                    onClicked: Qt.openUrlExternally(model.link)
                }
            }
        }
    }

    PlasmaComponents.BusyIndicator {
        anchors.centerIn: parent
        //whilst the model is loading, stay visible
        //we use opacity rather than visible to force an animation
        opacity: rssModel.loading ? 1: 0

        Behavior on opacity {
            PropertyAnimation {
                //this comes from PlasmaCore
                duration: units.shortDuration
            }
        }
    }
}

I've added a busy indicator (a spinning wheel) whilst we're fetching the RSS feed, just as a way to show how we use units.ShortAnimation

Finally, we end up with something like this: which looks good on all Plasma themes.

Plasmoid Tutorial 2 - Getting Data

Almost all applets need to interact with external data sources and actions.

This could be showing the current battery state, editing a file or in the case of our example monitoring and fetching an RSS feed.

Within Plasmoids we have a few different ways of getting data. Each have some advantages and disadvantages.

I'm going to loosely touch on them all, hopefully providing links to find out more information on a topic.

In-built javascript

QML is all powered by a javascript engine and as we've seen in the web world a lot is powerful with just javascript.

We have access to a full XMLHttpRequest object, which behaves exactly the same as making AJAX calls in any other web page.


    Component.onCompleted: {
        var request = new XMLHttpRequest();
        request.onreadystatechange = function() {
            if (request.readyState == XMLHttpRequest.DONE) {
                var reply = request.responseXML.documentElement
                //do the sort of thing that web developers normally do
            }
        }
        request.open("GET", "http://example.com/something");
        request.send();
    }

It's simple-ish, particularly if you are coming from a web developer world.

QML Plugins from C++

Sometimes we need to make our own modules if we want to access existing libraries, or do fast processing or interact with hardware.

QML integrates natively with C++ amazingly, every QObject in C++ appears as a javascript object. We can create new instances, all properties, signals and invokable methods or slots are visible to the JS engine as properties and methods.

as an example if I have a QObject with the structure

class MyObject: public QObject
{
    Q_PROPERTY(QString someProperty READ someProperty CONSTANT);
    Q_OBJECT
public:
    QString someProperty() const;
};

qmlRegisterType("MyLibrary", 1, 0 "MyObject");

from QML I can access it as follows.

MyObject {
  id: source
}

Text {
  text: source.someProperty
}

More information on writing plugins can be seen here

As you can see C++ plugins give you maximum integration, anything is possible, including creating new graphical types, models, or enums or anything else you might need to use in QML.
Unfortunately distribution is harder as it needs parts to be compiled.

Note that Qt provide several QML modules a full list of imports can be found here.

Including an XMLListModel that converts an XML feed into a model that can be used QtQuick, which is ideal for our tutorial of making a simple RSS reader.

Dataengines

Dataengines were an abstraction layer for fetching and data and performing actions used in Plasma 4. They provide a lanaguage agnostic method of providing data to a Plasmoid.

Dataengines made sense when we needed to be agnostic between various lanaguages; now QML is here to stay for a long long time this requirement vanishes. Writing a QML Plugin has numerous advantages over dataengines as you remove away a very limiting abstraction layer. QML Plugins allow exporting new types and enums and overall requires a lot less boiler plate code than dataengines.

However, if a dataengine already exists it totally makes sense to re-use it rather than rewriting the code from scratch, they have been honed over years of fixes and many are quite good.

For our RSS case, their is an RSS dataengine backed by libsyndication which is way more powerful than everything here as it handles every type of feed.
Unfortuantely at the time of writing this is blocked on PIM releasing.

plasmaengineexplorer provides a way to browse dataengines and contents.

High DPI Progress

High DPI Revisited

A few weeks ago I talked about high DPI in KDE applications

Abridged Recap

  • By settings QT_DEVICE_PIXEL_RATIO=2 Qt will scale all painting to draw things twice the size
  • By default this will just scale all our images so it slightly defeats the point of buying a fancy new high resolution screen
  • Qt can try and be clever and draw high resolution icons and other images
  • This is liable to break stuff, so requires each and every app to opt-in

Progress

On Monday this week I was loaned a high resolution laptop, and set about trying to make sure everything works perfectly within the KDE world.
We can now set this environment variable from a configuration file, and a user interface is in review to allow the user to manually set their scaling factor.

I then set about trying to enable high resolution image support in various applications and trying to fix all that is broken.

This task is two-fold. The first is fixing any bugs that result in simply enabling the high resolution icons. Second is making sure applications that provide their own images, do so in a way that still look spot on when used on a high resolution monitor.

Progress Screenshots

Here is my screenshot just after installing Kubuntu CI on a high resolution laptop (3800x1800).

We can correct some parts by just boosting the font size, but that doesn't solve the problems of small checkboxes, buttons and other hit areas. This isn't just a superficial problem and it becomes a major usability problem especially as these screens become more widespread.

This second screenshot shows the result with the device pixel ratio set and a weeks worth of fixing in a range of apps. (click for full size)

The most obvious thing is that the sizes are bigger, but more importantly this is happening whilst all icons and scrollbars remain crystal clear at the native resolution for that screen.

A zoomed in section looks like this:

Current Status

Every Qt5 app can double up with no work at all, but to look right requires some effort.

For some applications supporting high DPI has been easy. It is a single one line in KWrite, and suddenly all icons look spot on with no regressions. For applications such as Dolphin which do a lot more graphical tasks, this has not been so trivial. There are a lot of images involved, and a lot of complicated code around caching these which conflicts with the high resolution support without some further work.

I am tracking progress on a Kanboard page. Naturally I can't do every application, but I hope that by checking a few I can make sure all of our frameworks have full support making it easy for every other developer.

We also have a problem that Qt4 applications do not support device independent pixels. There are still many applications without a frameworks release even in the upcoming 15.04 applications release. Even in the next applications release in 15.08 August we are still unlikely to see a released PIM stack.
Is it a good idea to add an option into our UIs that improves some applications at the cost of consistency? It's not an easy answer.