programming

Model-View-Presenter in QtQuick

One of the advantages of using QtQuick is that it's possible to fully separate the user interface from application logic. Not only there are separate classes or components, but even the programming language used in both layers is completely different. The user interface can be written (or designed) using QML, a declarative and highly expressive language created strictly for the purpose of developing modern UI. The application logic on the other hand can be written in C++, which is powerful, abstract and yet exceptionally fast. Such approach has many advantages over writing the UI directly in C++, because with all it's strengths, it's not well suited for UI development. One of these advantages is that you can have two separate teams in the project, and the UI team doesn't need to know C++, and the application logic team doesn't need to know QML.

But wait, eventually these two layers have to be integrated somehow, right? There are several possibilities of doing such integration, and at first they seem quite complex, which defeats the purpose of simplifying the development process. The Qt tutorials and documentation don't give a lot of help. They only mention a general rule of thumb that you should never directly access or manipulate QML objects from C++ code, although the API certainly makes this possible. The whole idea is that the QML code should create and use objects implemented in C++ to perform various operations. But then if a QML button directly invokes some method implemented in C++, this has nothing to do with decoupling the application logic from the UI.

The solution to this problem is well known and it can also be successfully applied to QML and C++. The idea is to introduce another layer of abstraction between the application logic (the model) and the UI (the view). There are several variants of such architecture, the most common ones are Model-View-Presenter and Model-View-Controller. The differences are quite subtle and I'm not going to dig into the details. They mostly have to do with the lifetime and control flow between these layers. Generally MVC is commonly used in web applications, and MVP is often used in desktop applications, but this doesn't always have to be true. My goal wasn't to strictly follow any particular approach, but to find one that is best suited to solve the given problem, which is what design patterns are all about.

My implementation of the Model-View-Presenter architecture in QtQuick is based on the following assumptions:

  • A view is a visual QML component which defines the look of the application and has no logic (except for basic validation). It can be anything from a single ListView to a complex form with lots of different controls. The view should have some signals that correspond to user actions (for example indicating that a button was clicked). It also may have some properties that can be used for passing data to the view (for example to change the text of some label) and from the view (for example to retrieve text entered by the user). It doesn't require or directly use any components related to application logic, so a view can be created and tested independently from the other two layers.
  • A model is a C++ class that implements some aspect of the application logic. It typically has a number of methods that perform some operations. It may also have signals, which are useful to indicate that an asynchronous operation has completed. Properties can be used to pass data to and from the model, for example parameters and results of an operation. The model doesn't know anything about any visual components of the application. It can be created independently from the other two layers, which is useful for unit testing, automation, etc.
  • A presenter is what glues the view and model together. It's a visual component written in QML, but its only visible content is the view that's embedded in it. The presenter also creates the model object. Then it connects appropriate signals and slots from the view and the model and bind their properties. These connections and bindings can include additional logic, but it's best to design the interface of the view and model in such way that they can work directly together. In that case the presenter's only job is to create and set up these two objects and the overhead of having the third layer between the UI and the application logic is very small.

The MVP architecture makes it possible for the views and models to be created and tested independently, by two different teams, as long as their interfaces (signals, slots and properties) are designed in a compatible way. Another advantage of such approach is that it makes prototyping really easy. Designing a good user interface is a difficult craft and often the best solution is to quickly build and test different variants and prototypes. But the prototype often needs to demonstrate not only the appearance, but also the behavior of the application, so it's very useful to have some mock-up of application logic. This can be done by writing mock-up models which simply pretend to perform complex operations by returning fixed or random data. Such mock-up models can even be created in QML, which makes it possible to run and test the entire prototype without writing a single line of C++ code!

I will write more about writing mock-up models and switching between mock-ups and real models in the next article. For now let's assume that a model is a simple C++ class which inherits QObject and a view is a simple QML component which inherits Item or one of its sub-classes. The key and most interesting part of this architecture is obviously the presenter, so let's take a look at an example:

Presenter {
    id: root

    signal showMainWindow()

    view: Factory.createView( "LoginView", root )
    model: Factory.createView( "LoginModel", root )

    modelToViewBindings: [ "login", "error" ]
    viewToModelBindings: [ "login", "password" ]

    Connections {
        target: root.view
        onLoginClicked: { root.view.disableView(); root.model.beginLogin(); }
    }

    Connections {
        target: root.model
        onLoginSucceeded: { root.showMainWindow(); }
        onLoginFailed: { root.view.enableView(); }
    }
}

As you can see, this component inherits the Presenter class. I will show you the implementation of that class in the next article, but for now let's skip the technical details and take a look at how the actual presenter is implemented, in this case a simple login window.

The showMainWindow() signal is used to communicate to some higher level object (for example the application object) that it should display the main window of the application containing an appropriate presenter. In real life applications there will often be multiple views that can be switched or even displayed side by side, and multiple models that provide functionality for these views. The application will never interact with these views and models directly but rather will create appropriate presenters to handle them.

The view and model properties contain references to the view and model objects which are owned by the presenter. These objects are created using a helper Factory class, which makes it possible to dynamically switch between mock-up models implemented in QML and real models implemented in C++, or for example between views designed for mobile and desktop version of the same application. I will write more about the factory in the next article.

The modelToViewBindings and viewToModelBindings properties make it easy to bind properties of the same name and type between the model and the view. Again, I will present the underlying implementation in the next article, but generally the idea is to create a number of Binding components dynamically using a very simple syntax. Obviously in more complex cases it's possible to set up the bindings manually. The bindings can be unidirectional or bidirectional, like in case of the "login" property. For example, the model can remember and initialize the view with the last used login, and the view can pass the new login entered by the user back to the model.

The last part of the presenter handles signals from both the view and the model. In most cases, the signals from the model are passed directly to slots in the view and vice versa, but they can also be connected to a signal in the presenter itself or can contain more complex logic. In this simple example, when the login button is clicked, the view becomes inactive (with some visual hint like a busy indicator) and the model begins processing the login request asynchronously (for example by contacting a server or database). When login completes successfully, the showMainWindow() signal is emitted to the parent of the presenter, and in case of an error the view is activated again and some error message is displayed through the "error" property. This is obviously a very simplified example, but it represents what might happen in a typical modern application.

Filed under: Blog

Render loops and timers in QtQuick

Recently I was testing one of my Qt Quick applications on a virtual machine and I noticed that the busy indicator which is displayed at startup was spinning much faster than it should. I quickly found the reason of the problem: QTBUG-42699. Luckily, there is a simple workaround. You just have to put the following lines near the top of your main() function:

#ifdef Q_OS_WIN
    qputenv( "QSG_RENDER_LOOP", "basic" );
#endif

So what the hell is a render loop? All you should ever need to know is that it's something that makes your Qt Quick application work. This article mentions two types of render loops: threaded and non-threaded, but in reality there are three types: threaded, basic and windows. I have no idea what is the difference between the basic one and the windows one (they are both non-threaded), and why the third type is called "windows". It's definitely not specific to the Windows platform, except that it's used on Windows by default. However, I also noticed that switching to the "threaded" render loop made the busy indicator… spin even faster, so I decided to dig a bit deeper into this problem.

I took the sample project attached to QTBUG-42699 and added a third button which enables or disables a BusyIndicator control placed in the main window. The results are quite strange:

  • On the physical machine (with Windows 8) with the "windows" render loop, the problem occurs when only the custom animation is enabled.
  • On the virtual machine (with Windows 7 running on VirtualBox) with the "windows" render loop, the problem occurs when the custom animation and/or the BusyIndicator is enabled.
  • On the same virtual machine with the "threaded" render loop, the custom animation works fine, but the SequentialAnimation and the BusyIndicator are broken.

In other words, only the "basic" render loop seems to work reliably, and both the "windows" and the "threaded" ones cause various problems depending both on the environment and the types of timers that are used by the application. It's definitely not a good sign that bugs like that not only slip into an official release of Qt, but also remain unresolved for months. The busy indicator is just a relatively harmless example, but just imagine what happens if all the objects in your game start moving three times too fast. Apparently there are plans to use the threaded render loop by default on Windows in Qt 5.5, and I hope that it will eventually be fixed before that version is released.

The important lesson is that if I didn't test my application in multiple environments, I wouldn't even discover this problem, because the busy indicator seems to work fine on the machine that I use for development. I realize that making such complex thing as Qt work reliably on so many different plaftorms and configurations must be very hard, but that's exactly one of the main reasons why developers use a toolkit like Qt!

Filed under: Blog

Fullscreen mode in QtQuick

In theory creating a fullscreen application in QtQuick is straightforward - you just have to call the showFullScreen() method of QQuickView instead of show():

QQuickView view;

view.showFullScreen();

The application will work fine until you press Alt+Tab to switch to another window or Windows+M to minimize all windows. It may turn out that when you try to switch back to your QtQuick application, the window won't appear, and the screen will seem frozen.

Apparently I wasn't the only one who observed such problem - I found a few unanswered questions in various Qt forums, so I decided to dig deeper into it. After some experimentation I observed that adding an animated item (such as a spinning BusyIndicator) solves the problem. But why does it make a difference? An animated item causes the window to be repainted every frame (typically 60 times per second). However, if your window only contains static elements, Qt will cleverly avoid repainting it until something changes. This optimization works fine in windowed mode, but it causes problem in fullscreen mode.

Perhaps there is another way to do that, but the most obvious solution that came to my mind was to force an update whenever the window is activated or deactivated:

QObject::connect( &view, &QWindow::activeChanged, &view, &QQuickWindow::update );

I tested this on both Windows XP and Windows 8 and it seems to fix the problem in all situations.

I'm not sure if this is a bug in Qt or simply a glitch caused by Windows itself; it may have something to do with the fact that OpenGL (which is used by QtQuick under the hood) works a bit differently in fullscreen mode, bypassing the windows composition mechanism and rendering directly to the screen, but maybe I'm wrong. It doesn't really matter as long as there is a workaround.

Another small thing that you must remember about when using QtQuick in fullscreen mode is that the contents of your window should resize to the actual screen resolution. You will almost certainly want to add the following line:

view.setResizeMode( QQuickView::SizeRootObjectToView );

This can be combined with the scalable UI mechanism that I described recently. Just design your UI for some predefined resolution (I use 1600x900 because it fits nicely in windowed mode when debugging) and then set the multiplier in the u.dp() method so that it scales everything up or down according to the actual screen resolution (the Screen QML Type will come in handy).

Filed under: Blog

Scalable UI in QtQuick

One of the first problems that anyone developing QtQuick applications encounters is the scalability of the user interface. On Windows, when you change the size of text in the Display settings in the Control Panel, the fonts become larger or smaller, but the window and controls don't. When you run the same application on Mac OS X, the font sizes are completely different than on Windows. The situation is even more complicated on mobile devices, which have a wide variety on both screen resolutions and screen sizes - you can find both a mobile with 5" display and 1920x1080 resolution and a tablet with 10" display and 1280x800 resolution.

Web developers, who face similar problems, solve them by using relative font sizes (such as 0.8em or 1.5em) and logical pixels which are scaled to physical device pixels (according to the <meta name="viewport"> tag). Exactly the same approach can be used in QtQuick applications, but it requires some additional work. After some research and experimentation, I created the following simple component which I called Units.qml:

import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0

QtObject {
    function dp( x ) {
        return Math.round( x * Settings.dpiScaleFactor );
    }

    function em( x ) {
        return Math.round( x * TextSingleton.font.pixelSize );
    }
}

As you can see, the Units component provides two simple functions:

  • dp() - converts logical pixels to device (physical) pixels. Generally, all absolute sizes, positions, margins and other distances should always be specified using this function.
  • em() - converts relative font size to pixels. This function should be used for specifying all font sizes, but it can also be useful for some sizes and distances (e.g. the height of a button can be specified as a multiple of the font size, provided that appropriate layouts are used).

This is an example of how this component can be used:

import QtQuick 2.2
import QtQuick.Controls 1.2

Item {
    id: root

    implicitWidth: u.dp( 640 )
    implicitHeight: u.dp( 480 )

    Label {
        id: helloLabel

        x: u.dp( 20 )
        y: u.dp( 50 )

        text: "hello"
        font.pixelSize: u.em( 1.5 )
    }

    Units {
        id: u
    }
}

How does it work? The Settings object is an internal singleton of the QtQuick Controls module which provides various information. The dpiScaleFactor property basically uses the logicalDotsPerInchX property of the primary screen to calculate the scale factor. On Windows, this corresponds to the size of text in the Display settings in the Control Panel, and typical values are 1 (small) or 1.25 (normal). On Mac OS X, the dpiScaleFactor is always 1, however on Retina displays this actually corresponds to 2 physical pixels, because the OS performs additional scaling. On mobile devices you may need to use a different scale factor to ensure that the window fits the entire screen regardless of it's dimensions and DPI resolution.

Note that the dp() function uses Math.round() to ensure that the result is an integer value. Although QtQuick elements can be positioned at non-integer coordinates, this can result in ugly artifacts, so it's better to round everything.

The TextSingleton, as the name implies, is another internal singleton of the QtQuick Controls module which is simply an instance of Text component. It is used to access the pixel size of the default font. The result is also rounded in case it's used to position elements.

The Units component could also be made a singleton; in that case it wouldn't be necessary to put an instance of the Units object into every component that uses it. On the other hand, writing "u.dp()" is easier and more readable than "Units.dp()". Besides, using the singleton messes up the design mode in Qt Creator. Of course, when designing the UI, you still have to add the function calls manually and you cannot simply re-position items by dragging them in the design mode, but at least the Qt Creator displays everything correctly when the Units object is explicitly placed in the component.

Filed under: Blog

Dial and StyleItem

As I promised, I'm starting a new series of articles dedicated to QtQuick. When I started playing with QtQuick and the QtQuick Controls module, I noticed that it implements QtQuick equivalents of most widgets, and Dial is one of the few that are missing. Perhaps QDial is not a very frequently used widget, but in certain kinds of QtQuick applications, especially for mobile devices, a dial control can come in handy:

dial

A bit of googling quickly revealed that a Dial control exists as an experimental part of the QtQuick Controls module (you can find it here), but for whatever reason it never made it into the official package.

Here's my final version of Dial.qml after some tweaking:

import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0

Control {
    id: root

    width: 100
    height: 100

    property alias minimumValue: range.minimumValue
    property alias maximumValue: range.maximumValue
    property alias value: range.value
    property alias stepSize: range.stepSize
    property alias pressed: mouseArea.pressed

    property bool notchesVisible: false
    property real notchSize: 1
    property bool activeFocusOnPress: false

    activeFocusOnTab: true

    Accessible.role: Accessible.Dial
    Accessible.name: range.value

    Keys.onLeftPressed: { range.value -= range.stepSize; }

    Keys.onRightPressed: { range.value += range.stepSize; }

    RangeModel {
        id: range
        minimumValue: 0
        maximumValue: 1
        stepSize: 0
        value: 0
    }

    MouseArea {
        id: mouseArea
        anchors.fill: parent
        hoverEnabled: true

        onPositionChanged: {
            if ( mouseArea.pressed )
                range.value = mouseArea.valueFromPoint( mouseArea.mouseX, mouseArea.mouseY );
        }

        onPressed: {
            range.value = mouseArea.valueFromPoint( mouseArea.mouseX, mouseArea.mouseY );
            if ( root.activeFocusOnPress )
                root.focus = true;
        }

        onWheel: { range.value += wheel.angleDelta.y * range.stepSize / 120.0; }

        function valueFromPoint( x, y ) {
            var yy = root.height / 2 - y;
            var xx = x - root.width / 2;

            var angle = ( xx || yy ) ? Math.atan2( yy, xx ) : 0;

            if ( angle < -Math.PI / 2 )
                angle += 2 * Math.PI;

            var dist = 0;
            var minv = range.minimumValue;
            var maxv = range.maximumValue;

            if ( minimumValue < 0 ) {
                dist = -range.minimumValue;
                minv = 0;
                maxv = range.maximumValue + dist;
            }

            var v = ( minv + ( maxv - minv ) * ( Math.PI * 4 / 3 - angle ) / ( Math.PI * 10 / 6 ) );

            if ( dist > 0 )
                v -= dist;

            return Math.max( range.minimumValue, Math.min( range.maximumValue, v ) );
        }
    }

    style: Qt.createComponent( "DialStyle.qml" )
}

Note: for copyright purposes, the code snippets are licensed under the BSD-style open source license, just like the original code.

The code is very straightforward. The internal RangeModel object is used for handling minimum, maximum and step values (the Slider uses it too). The valueFromPoint() function converts the position of the mouse to the angle and then to the actual value. I renamed "tickmarksEnabled" to "notchesVisible" and added "notchSize" for compatibility with QDial. I also removed "wrapping", because the underlying StyleItem doesn't support it anyway. Finally, I added the missing mouse wheel and keyboard support.

What about drawing the control? The original Dial contains a StyleItem, another internal component of QtQuick Controls, which uses the application's default QStyle to paint items so that they look just like widgets. Now, a little digression. Qt5 has two separate UI modules - QtGui for handling windows and QtWidgets which implements the widgets. The former can be used without the latter, for example if your applications uses OpenGL or QtQuick or a custom painting engine and doesn't need widgets. By excluding widgets, you can reduce dependencies by a few megabytes.

The QtQuick Controls can work perfectly fine without QtWidgets. However, in that case they are drawn using built-in bitmap images, and they look the same on all platforms. On the other hand, when QtWidgets are enabled, the QtQuick Controls use the StyleItem components, which emulate the "native" look of widgets on the given platform. To be more specific, the style used by QtQuick Controls depends on whether you are using QGuiApplication or QApplication (the former is part of QtGui; the latter is part of QtWidgets). This information is deeply buried in the Qt documentation, so it can be quite confusing. If you dig into the Qt sources, you will find that the QtQuick.Controls.Styles module actually contains two subdirectories: "Base" with platform-independent styles and "Desktop" with ones that use StyleItem. The internal QQuickControlSettings class is used to select the right styles at run-time, and you can even use the "QT_QUICK_CONTROLS_STYLE" environment variable to use an entirely different set of styles.

Back to the main topic, embedding a StyleItem directly in the Dial is not a good idea, because it only works with the QtWidgets module and it's not possible to implement a custom or platform-independent style. That's why I changed the code to use the "style" property of the "Control" component, decoupling the style from the control, just like other QtQuick Controls do. The default implementation of the style is based on the original code, so it uses the StyleItem to do the job:

import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0

Style {
    property Component panel: StyleItem {
        id: styleItem

        property real visualPos: control.maximumValue - control.value

        property real granularity: {
            if ( ( control.maximumValue - control.minimumValue ) < 10 )
                return 100;
            else if ( ( control.maximumValue - control.minimumValue ) > 1000 )
                return 1 / Math.ceil( ( control.maximumValue - control.minimumValue ) / 1000 );
            else
                return Math.floor( 1000 / ( control.maximumValue - control.minimumValue ) );
        }

        elementType: "dial"
        activeControl: control.notchesVisible ? "ticks" : ""
        hasFocus: control.focus
        enabled: control.enabled
        sunken: control.pressed
        maximum: control.maximumValue * styleItem.granularity
        minimum: control.minimumValue * styleItem.granularity
        value: styleItem.visualPos * styleItem.granularity
        step: Math.ceil( control.notchSize * styleItem.granularity )

        Behavior on visualPos {
            enabled: !control.pressed
            NumberAnimation {
                duration: 300
                easing.type: Easing.OutSine
            }
        }
    }
}

This code simply passes information from the control to the StyleItem. The only interesting part is the "granularity" property. The original code uses a hard-coded value of 100 instead. By increasing the number of steps internally, it's possible to have a nice looking, smooth animation when the Dial rotates. However, the hard-coded value of 100 doesn't always work, because the internal implementation of QStyle only paints the notches correctly if the total number of steps doesn't exceed 1000. That's why my code uses a dynamic granularity instead which works correctly in most cases.

A very simple, platform-independent implementation of the style could use a rotating image instead:

import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0

Style {
    property Component panel: Image {
        id: image

        property real visualPos: control.value

        source: control.pressed ? "dial-on.png" : "dial.png"
        rotation: 180 * 7 / 6 + ( image.visualPos - control.minimumValue ) * ( 180 * 10 / 6 )
            / ( control.maximumValue - control.minimumValue )

        Behavior on visualPos {
            enabled: !control.pressed
            NumberAnimation {
                duration: 300
                easing.type: Easing.OutSine
            }
        }
    }
}

In order to create the notches, you can place a small Rectangle in a Repeater and use appropriate rotation.

Filed under: Blog
Syndicate content