<< to CrossControl homepage

Support & Service Center

QtQuick / QML-Programming

Creating a basic IVI Compositor with QML

This example shows how to create and use a basic custom IVI-compositor using the WaylandCompositor in QML. We will create an IVI compositor that splits the display into two IVI-surfaces. The orientation and position of the split is defined by command line arguments.

To begin, we create a new project using the CrossControl QtQuick2 Application template, selecting resolution, orientation and kit for the device we're working with. In this example, we'll be targetting av CCPilot VS.

We want to be able to control the orientation and position of the split with command line parameters, so we modify the main(...) function in main.cpp to look for them.
First we set default values in case the parameters are not provided.

// Init default values for arguments
    int orientation        = 90; // Portrait mode, USB connectors downwards
    int bottomScreenHeight = 500;
Then we add code that scans the argument vector for the ones we're interested in and replaces our default values if found.
    // Read arguments
    for (int i = 0; i < (argc - 1); i += 2) {
        QString argument(argv[i + 1]);
        if (argument == QStringLiteral("-bottomHeight")) {
            bottomScreenHeight = atoi(argv[i + 2]);
        }
        else if (argument == QStringLiteral("-orientation")) {
            // Normalize orientation to [0-360] degrees
            /** NOTE: In a real case we likely want to limit values
             * to 90*n steps here.
             * Non-orthogonal rotations are supported for
             * demonstration purposes
            */
            orientation = (atoi(argv[i + 2])) % 360;
            while (orientation < 0)
                orientation += 360;
        }
   }
We also need to switch from the default QQuickView to the more generic QQMLApplicationEngine to support the new WaylandCompositor node we'll be using as root node in our QML. We do this by replacing
    QQuickView *view = new QQuickView;
    QObject::connect(view->engine(), &QQmlEngine::quit, &app, &QGuiApplication::quit); 
 
    ...
 
    view->rootContext()->setContextProperty("targetARM", QVariant(targetARM));
    view->rootContext()->setContextProperty("displayWidth", QVariant(displayWidth));
    view->rootContext()->setContextProperty("displayHeight", QVariant(displayHeight));
 
    view->setSource(QStringLiteral("qrc:/main.qml"));
    view->showFullScreen();
with
    QQmlApplicationEngine engine;
    QObject::connect(&engine, &QQmlApplicationEngine::quit, &app, &QGuiApplication::quit);
 
    ...
 
    engine.rootContext()->setContextProperty("targetARM", QVariant(targetARM));
    engine.rootContext()->setContextProperty("displayWidth", QVariant(displayWidth));
    engine.rootContext()->setContextProperty("displayHeight", QVariant(displayHeight));
 
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
Finally, we expose our new arguments to the QML engine by adding the following next to the existing setContextProperty lines.
    // Expose arguments to QML layer
    engine.rootContext()->setContextProperty("orientation", QVariant(orientation));
    engine.rootContext()->setContextProperty("bottomScreenHeight", QVariant(bottomScreenHeight));
Now we have what we need to build our compositor in QML. The template's default main.qml will not be needed so we will completely replace it's content with our own compositor code. First, we need to import the QML modules we need.
import QtQuick 2.12
import QtWayland.Compositor 1.0
import QtQuick.Window 2.12
As root object we use a WaylandCompositor
import QtQuick 2.12
import QtWayland.Compositor 1.0
import QtQuick.Window 2.12
 
WaylandCompositor {
}
Inside it, we define the compositor's "window" structure by adding a WaylandOutput element and building a normal QML structure underneath using Rectangles to represent IVI surface areas. We start with a Window element as the structure's root, and create an Item element of same size in it's center to handle screen rotation. We also determine if we're in a portrait or landscape mode to know if we need to invert width and height. Since this example allows for non-orthogonal rotations we pick the mode that is closest.
import QtQuick 2.12
import QtWayland.Compositor 1.0
import QtQuick.Window 2.12
 
WaylandCompositor {
    WaylandOutput {
        sizeFollowsWindow: true
        window: Window {
            id: base
            width: displayWidth
            height: displayHeight
            visible: true
 
            Item {
                id: screenRoot
                anchors.centerIn: parent
                height: portrait ? parent.width : parent.height
                width: portrait ? parent.height : parent.width
                rotation: orientation
 
                // Since we support non-orthogonal orientations,
                // we make a best fit decision on portrait mode here
                property bool portrait: (orientation%360 > 45 && orientation%360 < 135) ||
                                        (orientation%360 > 225 && orientation%360 < 315)
 
            }
        }
    }
}
Under the screenRoot Item, we add our Rectangles and their positioning logic. We also add some hints about which Rectangle is used for which IVI surface ID.
import QtQuick 2.12
import QtWayland.Compositor 1.0
import QtQuick.Window 2.12
 
WaylandCompositor {
    WaylandOutput {
        sizeFollowsWindow: true
        window: Window {
            id: base
            width: displayWidth
            height: displayHeight
            visible: true
 
            Item {
                id: screenRoot
                anchors.centerIn: parent
                height: portrait ? parent.width : parent.height
                width: portrait ? parent.height : parent.width
                rotation: orientation
 
                // Since we support non-orthogonal orientations,
                // we make a best fit decision on portrait mode here
                property bool portrait: (orientation%360 > 45 && orientation%360 < 135) ||
                                        (orientation%360 > 225 && orientation%360 < 315)
                Rectangle {
                    id: topArea
                    anchors {
                        left: parent.left
                        right: parent.right
                        top: parent.top
                        bottom: bottomArea.top
                    }
                    color: "cornflowerblue"
                    Text {
                        anchors.centerIn: parent
                        text: "Ivi surface with id 1"
                    }
                }
                Rectangle {
                    id: bottomArea
                    anchors {
                        left: parent.left
                        right: parent.right
                        bottom: parent.bottom
                    }
                    height: bottomScreenHeight
                    color: "burlywood"
                    Text {
                        anchors.centerIn: parent
                        text: "Default ivi surface"
                    }
                }
            }
        }
    }
}
We need to define what types of wayland surfaces our compositor support. This is done by instantiating a node for each supported surface type directly under the Wayland Compositor element. In this example we only support IVI-surfaces. We also need to provide a component that should be instanced when a wayland application connects.
WaylandCompositor {
    WaylandOutput {
        sizeFollowsWindow: true
        window: Window {
            id: base
            width: displayWidth
            height: displayHeight
            visible: true
 
            Item {
                id: screenRoot
                anchors.centerIn: parent
                height: portrait ? parent.width : parent.height
                width: portrait ? parent.height : parent.width
                rotation: orientation
 
                // Since we support non-orthogonal orientations,
                // we make a best fit decision on portrait mode here
                property bool portrait: (orientation%360 > 45 && orientation%360 < 135) ||
                                        (orientation%360 > 225 && orientation%360 < 315)
                Rectangle {
                    id: topArea
                    anchors {
                        left: parent.left
                        right: parent.right
                        top: parent.top
                        bottom: bottomArea.top
                    }
                    color: "cornflowerblue"
                    Text {
                        anchors.centerIn: parent
                        text: "Ivi surface with id 1"
                    }
                }
                Rectangle {
                    id: bottomArea
                    anchors {
                        left: parent.left
                        right: parent.right
                        bottom: parent.bottom
                    }
                    height: bottomScreenHeight
                    color: "burlywood"
                    Text {
                        anchors.centerIn: parent
                        text: "Default ivi surface"
                    }
                }
            }
        }
    }
 
    Component {
        id: chromeComponent
        ShellSurfaceItem {
            anchors.fill: parent
            onSurfaceDestroyed: destroy()
            onWidthChanged: handleResized()
            onHeightChanged: handleResized()
            function handleResized() {
                shellSurface.sendConfigure(Qt.size(width, height));
            }
        }
    }
 
    IviApplication {
        onIviSurfaceCreated: {
            var surfaceArea = iviSurface.iviId === 1 ? topArea : bottomArea;
            var item = chromeComponent.createObject(surfaceArea, { "shellSurface": iviSurface } );
            item.handleResized();
        }
    }
}

Our compositor is now done. To testrun our compositor directly from QtCreator we need to define a few run settings first since we're not going to be targetting the default Weston compositor. First we tell the program to run directly on EGLFS by adding the command line argument -platform eglfs in the Command line arguments: field in Project -> -> Run settings
We can also take this opportunity to test our own custom arguments so we add those too. -platform eglfs -orientation 270 -bottomHeight 300

We also need to set some environment variables. We do this in Run Environment under Run settings for the VS kit. [Projects -> -> Run -> Run Environment -> exand Details -> Add]
Some of these are required, some are optional

  • XDG_RUNTIME_DIR=/run/user/root # (required) Sets the compositor to run as root
  • QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=/dev/input/touchscreen0 # (required for touch) Tell eglfs to listen to touch events from the VS touchdisplay
  • FB_MULTI_BUFFER=3 # (recommended) Use tripple buffering
  • QT_QPA_EGLFS_HIDECURSOR=1 # (recommended) Hides the mouse cursor since we're working with a touch display

And finally, after ensuring that the Weston compositor is not running on our CCPilot device (if it is, stop it with /etc/init.d/westion stop from console), we can hit Run in QtCreator and we should see our compositor running, ready to receive ivi-applications!

One final note - to really verify that it works we, of course, want to run some ivi-application. Any normal wayland application can be run as an ivi-application by defining the following environment variables for them and adding the commandline argument -platform wayland.

  • XDG_RUNTIME_DIR=/run/user/root
  • QT_WAYLAND_SHELL_INTEGRATION=ivi-shell
  • QT_IVI_SURFACE_ID=NNNN (where NNNN is the target surface id the compositor should place the application on)
In our compositor we defined the top area as ID 1 and the bottom as default meaning any application with surface ID other than 1, or none, will end up in the bottom area, and those with it set to 1 in the top.

Environment and Versions: 
Qt-5.12.0 LinX Designer 4.0 iMX 6 platforms
Applies to version: 
Qt 5.12

QML Example Project - Part 3

This example is an extension to Example_Project_2, showing the use of Data Engine and Fieldbus Access. A fourth screen has been added, where two receiving signal values are being presented as well as two outgoing signal values. The outgoing values can be set using the “+” and “-“ buttons.

All classes and the signal setup are auto-generated using the Fieldbus Access plugin in Qt Creator. The only programming needed before using the signals in QML are then:

1. Create a DataEngine object

    DataEngine dataEngine;

2. Register the type

     qmlRegisterUncreatableType<DataEngine>("CrossControl", 1,0, "DataEngine","can not instantiate DataEngine in qml");

3. Pass the handle to the frontend

     view->rootContext()->setContextProperty("dataEngine", &dataEngine);

Displays:

  • DataEngine
  • FieldBus Access
AttachmentSize
Package icon QML Example 3 (Qt-5.4.0) ARM22.32 MB
Environment and Versions: 

DataEngine 3.0.1

LinX-Base 2.1.0

Qt-5.4.0

 

QML Example Project - Part 2

This example is an extension to Example_Project_1, showing how to use the hard buttons and how to navigate between different pages. In this example the number of screens has been increased to three, and they have all been incorporated in a Flickable component. This allows the user to swipe to change between screens and to use the indicator at the bottom to know which of the three screens that is active.

Screen 1 is the same screen as in Example_Project_1.

Screen 2 uses the CCAux class to demonstrate how to read the hardware buttons (available in CCPilot VC, VA etc) from QML.

Screen 3 is under construction to later contain an example of how to connect an analogue camera video feed into QML.

 

Displays:

  • Flickable component to allow for swiping to change between screens
  • CCAux library:
    • Hardware buttons (certain models) and connection to QML
  • Analogue camera (under construction)

 

AttachmentSize
Package icon QML Example 2 (Qt-5.4.0) ARM21.9 MB
Environment and Versions: 

DataEngine 3.0.1

LinX-Base 2.1.0

Qt-5.4.0

 

QML Example Project - Part 1

This example shows a basic QML application and how to interact between the QML graphics front-end with the C++ back-end, including how to use the CCaux library.

In this example we have one screen that displays a few of the custom built CrossControl QML components. Two C++ classes (CCAux and calc_example) have been created, registered and exposed to QML in main.cpp.

The example shows how to use CCAux functions to retrieve the device’s type, IP address and serial number and display those on the screen in text.

The current brightness is also configurable using the ButtonSlider component (leftmost in the screen), with the VerticalMeter (rightmost) displaying the current setting as well. The CircularMeter (centered) uses a C++ function to randomly generate its displayed values.

 

Displays:

  • QML and connection to C++
  • The use of CrossControl QML components
  • CCAux library:
    • DeviceType
    • Brightness
    • IP Address
    • Unit Serial Number
AttachmentSize
Package icon QML Example 1 (Qt-5.4.0) x86 & ARM21.86 MB
Environment and Versions: 

DataEngine 3.0.1

LinX-Base 2.1.0

Qt-5.4.0

 

Hiding the mouse pointer and window frame on x86 devices (XM / XM2)

On x86 based devices running Qt there is a border that will appear around the main application by default.  If you do not want this border to appear you can add this code to your main.cpp file:

	Application w;
	// uncomment this line to remove window frame
	w.setWindowFlags(Qt::FramelessWindowHint);

And if you don't want the mouse pointer being shown you can add this to the main.cpp file:

	QApplication a(argc, argv);
	a.setOverrideCursor( QCursor( Qt::BlankCursor ) );
 
Environment and Versions: 

x86 Devices (XM / XM2)

QML with SocketCAN

There are many ways to work with CAN messages within the LinX software suite. Most customers using Qt will likely want a full J1939 CAN stack with all of the lower level protocols the J1939 stack includes. In this case you will want to check out the FieldBus Access tool developed for the LinX software suite. In some cases though a full J1939 CAN stack is not needed and you may only want to receive a few CAN messages or custom CAN messages that don't adhere to the J1939 stack protocol. In this case you may choose to use the lower level socketCAN interface in the Linux displays to receive raw CAN messages. This demo shows how socketCAN can be used with Qt QML code.

The demo simply takes in raw CAN data from socketCAN, sends to a custom class to be decoded and once decoded is displayed on QML UI. It provides the correct classes to receive and send CAN messages with socketCAN as well as shows how a custom class can be created and interfaced to QML so you can decode the raw CAN data into useful messages and display them on your UI. The code is commented to attempt to explain what each piece is accomplishing.

To use, download the attached file and extract in the LinX Virtual Machine. Open the '.pro' file with Qt and deploy to the display. You will need the updated LinX RT for this to work (it has been tested on ARM based displays although it should work with x86 displays if you add the appropriate kit). The message that is being displayed on the QML UI by default is the 'Engine Speed' message from PGN '61444' in the standard J1939 message list.

 

 

AttachmentSize
Binary Data SocketCAN shown with QML789.88 KB

Screen cast: Introduction in the Basics of QML programming (~20 min)

This screencast provides a short introduction into QML programming with the Linx Software Suite's UX Designer. It is targeted to developers who want to start programming with QML.

In order to follow the screencast, the Linx Software Suite needs to be installed and running on the user's PC.

 

Data Engine connection with Qt5 Quick application

In this article, one approach will be presented how signals in the Data Engine can be connected to QML components. Signal values can either be presented in the GUI for a user or be updated through user input with the GUI.
Goal:

  • Set signal value from GUI (qml) and send the value to the Data Engine
  • Read value from Data Engine and view it in the GUI (qml)

Achieved by:

  • Write a C++ backend to handle communication with Data Engine
  • Class with Q Properties defines the signals 
  • QML-binds to properties in the signal class handler

The following structure will be used for both projects to achieve the previously mentioned goal:

  • Viewer

    • The Viewer is what the user will see and interact with. It is written with QML and property bindings will be used to bind certain component properties. It communicates with the backend parts Data Engine Control or Data Engine Signal.

  • Data Engine Signal

    • Defines the signals which should be used by the application. Signals are created as properties which the Viewer can bind to. The name of the signals must follow a specific pattern for the current implementation. Will be presented further on in the article.

  • Data Engine Control

    • A central piece for this project and the backend. It handles the communication with the Data Engine through the sapcore interface or by receiving data from the observer. It is also responsible to update signals defined as properties in Data Engine Signal.

  • Observer

    • Receives data from the sapcore interface, such as subscribe result or data for updated signals. Pushes the information to the Data Engine Control which then handles the received data. It’s a part of the Data Engine Control.

Viewer

The visible layer and what the user will interact with. This example includes two projects: one will act the sender and the other as the receiver. A slider component is used to change the values which should be transmitted to the receiver. In total there are five different signals used, each representing a different data type. The selected value range can be increased or decreased, selected values are for demonstration purpose.

A central part of QML is use to bind properties to each other, and this is what we will utilize to set values and read values from the Data Engine.

Sender

To send data we will use the signals which are defined in Data Engine Signal for the SignalSender project and use the event “onValueChanged” for each slider to set a value to one of the available signals.

Slider { 
   id: boolSlider value: 0 
   minimumValue: 0 
   maximumValue: 1 
   stepSize: 1 
   onValueChanged: dataEngineSignal.boolValue_OUT = value 
}

When the event “onValueChanged” is triggered, it will set a value to the property called “boolValue_OUT”, defined in our Data Engine Signal class. By defining the signal name with “_OUT” in the end, we indicate that this signal should be transmitted to the Data Engine. More information will be presented in the Data Engine Signal section.

Receiver

The second project acts as the receiver of data. The QML code is very similar to the sender but utilize the bindings in a different manner and the names of the signals are also slightly modified.

Text { 
   id: boolValue 
   text: dataEngineSignal.boolValue_IN 
   font.pixelSize: 12 
}

With the sender project the a value was set to the property “boolValue_OUT”, in this case we bind the property “boolValue_IN” found in Data Engine Signal to the text property of Text in our QML file. When the property value is updated all properties bound to the signal will be updated, in this case a text field. Another difference is that the signal name how has “_IN” at the end of the, this indicates that the signal should be read and no data should be written to it from the GUI. More about this will be presented further on.

Data Engine Signal

This class is used in both projects to define all the signals that should be subscribed, either as producing signals or as consuming, The content of the files are almost identical, but there are differences.  Signals are created as properties with write, read and notify methods by using the macro Q_PROPERTY. The fastest way to define new signals is with code completion. Start type Q_PROPERTY and Qt Creator will suggest completing it; press the tab-key on the keyboard to do so. Then fill in the data type and the name of the signal. Lastly let Qt Creator create all missing methods and variables by right-clicking on property and select Refactor -> Generate Missing Q_PROPERTY Members...

Naming of signals

As mentioned earlier, signal names have been given a specific ending to define if it should be received (consumed) or written to (produce). This is required for this specific example and is used to instruct the Data Engine Control class which settings it should use when subscribing to all the signals defined in the Data Engine Signal class. The actual signal name used for subscription will be without the trailing “_IN/OUT”. Other types are also valid and here is the complete list:

  • _IN – Consumer
  • _OUT – Producer
  • _INOUT – Bidirectional
  • _MANYOUT – Multiple Producers

It is also possible to add “_P” in the end to indicate the signal is persistent, but this is not used in this example.

Receiver

Five signals are defined as properties and created with the method previously described. Since all the signals should be received to the application, they all have “_IN” added to the end of the signal name. All the signals are then available for binding. Definition and declaration is mainly kept in the .h-file but initial values for the signals are set on the constructor found in the .cpp-file. These values are read during initialization of the GUI.  When a new value is written to one of the signals, the set method will notify the GUI a new value is available and force an update. In this case, it is the Data Engine Control which writes to the signal by using the set method for the signal which needs to be updated.

Q_PROPERTY(bool boolValue_IN READ boolValue_IN WRITE setboolValue_IN NOTIFY boolValue_INChanged)

Sender

The same method is used to define the signals which should be sent to the receiver, but with “_OUT” instead of “_IN”. Besides this difference there is an important step in order to make it possible to write new signal values to the Data Engine. When the GUI sets a new value for a property, the associated WRITE method will be called for that property. For example, setboolValue_OUT:

Q_PROPERTY(bool boolValue_OUT READ boolValue_OUT WRITE setboolValue_OUT NOTIFY boolValue_OUTChanged)

In our case for this property, a method called setboolValue_OUT will be called. This method is generated using the refactor function pointed out earlier. It is slightly modified with the introduction of an additional signal emit, sendSignal (value, name), which is used to send a Qt signal to the Data Engine Control which has a slot paired with the emitted signal. http://doc.qt.io/qt-5/signalsandslots.html . Data Engine Control then takes over the request and sends the value if possible.

void setboolValue_OUT(bool arg) 
{ 
   if (m_boolValue_OUT != arg) 
   { 
      m_boolValue_OUT = arg; 
      emit boolValue_OUTChanged(arg); 
      emit sendSignal(arg, "boolValue"); 
   } 
} 

With this solution it is important that the setboolValue method is only called through the GUI. If Data Engine Control would use this method (done when a new value is received) then the incoming value would be sent back to the Data Engine (an echo). This is why “_IN” signals should only be read, and “_OUT” only be set.

Data Engine Control

This part of the backend has the task handle the connection with Data Engine and take care of signals which has been updated.

Similar to the Data Engine Signal class, Control also has a couple of properties which the GUI can access and read. Such as the status of the connection with Data Engine, name of the client, used host name and port number. There are also a few slots which the GUI could use to perform various tasks, but there are not used in this example.

When the application starts, three methods will be called from main.cpp to Data Engine Control to initialize it:

dataEngineContext.InitConnection("localhost", 0x1235, "Receiver-GUI");
dataEngineContext.SetSignalHandle(signal);
dataEngineContext.SubscribeToSignals(); 
  • Firstly, the connection needs to have the correct settings: where the Data Engine is running, on which port and what the client should call itself. Once these values have been set, the method will attempt to connect to the Data Engine using the provided information.
  • Secondly the Data Engine Control needs a handle to the signals defined in Data Engine Signal. This is a requirement to pass incoming data to the correct property.
  • Lastly, it will try to subscribe to the signals found by the handle. What the code does is that it lists all the properties found through the handle, and for each one of them a series of steps are taken do determine the name of the signal, data type, direction… before it is finally used for a subscription.

Receiver

Signal updates are received from the Observer which is a part of the Data Engine Control. Once a new value arrives, the ID will be checked in one of the used dictionaries to find the corresponding signal name. Once found, it will use the signal handle previously set during the initialization to update the correct property in Data Engine Signal. The property is updated using the WRITE method associated to the signal, which will trigger the GUI to read the new value thanks to the notify signal.

Sender

When Data Engine Signals emits the signal to send data, the slot connected to the signal will be activated, in this case sendToServer(value,name). The method needs to get the ID associated with the name of the signal, this is done by reading the name paired with the key value ID. Once the ID is known, it is just a matter to set the value using the SAP method SetValue(id,value) available in the sapcore-library.

Observer

The observer is a part of the Data Engine Control class and will handle signal updates. When it receives a new value for one of the subscribed signals, it will notify the Data Engine Signal class by emitting a signal to one of the associated slots available in the Data Engine Control class (one for each data type). After this, it is up to the Data Engine Control and the slot method to process the received information and act accordingly. 

Flowcharts and structure

AttachmentSize
Binary Data Project files9.79 KB

Rotate a Qt5 application with QML

With the introduction of Qt5 and QML the rotation of the screen content needs to be handled with a different approach compared with Qt4. Qt5 applications use the EGLFS platform which is basically an OpenGL area which spans over the entire screen. This area will always use the dimensions of screen and be the root size for the GUI. New dimensions or orientation cannot be set to the root element. However, it is perfectly fine to alter the visible content; and that is what will be presented in this article. An example project is included which shows these steps in practice.

Below is an example which shows the general structure to achieve rotation of the visible content.

Item {
    id: base
    Rectangle {
        id: view
        anchors.centerIn: parent
        width: base.width; 
        height: base.height
        // Content area…
} 

The QML-file use "Item" as the base and then add content in a container, in this case called “view”. By doing it this way, it is possible to manipulate the visible layer's dimensions and rotation without changing the base (which is always fixed to the screen size).

With this structure is it very easy to rotate the visible content. There are many ways this could be achieved, and to conclude this article, here is an example how the rotation could be handled by a function. The function accepts an orientation index and the changes the width, height and rotation properties for the “view”.

function orientationChanged(orientation) {
    highlightindex(orientation)

    if(orientation == 1) {
        view.rotation = 0
        view.width = base.width; view.height = base.height
        console.log("orientation1")
    }
    else if(orientation == 2) {
        view.rotation = 180
        view.width = base.width; view.height = base.height
        console.log("orientation2")
    }
    else if(orientation == 3) {
        view.rotation = 270
        view.width = base.height; view.height = base.width
        console.log("orientation3")
    }
    else if(orientation == 4) {
        view.rotation = 90
        view.width = base.height; view.height = base.width
        console.log("orientation4")
    }
}

Width and height is the same as the “base” when the content is rotated 0 or 180 degrees. If the content should be rotated 90 or 270 degrees, it is important to switch width and height with each other. The base width becomes the content’s new height, and base height the new width.

Screen Orientation Tester GUI
AttachmentSize
Package icon Project files14.66 KB

Transition from Qt 4.x based projects to Qt 5

The transition from Qt 4.x to Qt 5 is not expected to be significant. However, the “modularization” of the Qt5 code base requires some amount of changes to project configuration, such as use of “headers”, and configuration of project build settings (such as changes to the *.pro files).

We refer to the documentation available on Qt Project for further details, found here.

To determine if a project is being built using the Qt4 or Qt5 toolchain, it is possible to add the following line to the *.pro-file to check which version that is currently being used:

// Add widgets if the project is built using Qt5
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

A similar way can be applied for *.h- or *.cpp-files when required:

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include <QtWidgets/QApplication>
#else
#include <QtGui/QApplication>
#endif

Pages

whatever